ActionBar 背景渐变动画效果

http://blog.chengyunfeng.com/?p=497


在Play Music (v5.0) 版本中的音乐专辑详情界面引入了一个新的ActionBar效果。用户进入界面的时候ActionBar的背景为透明的,当用户向下滚动内容的时候,ActionBar背景逐渐有透明变为不透明。这种效果有两种明显的好处:

  • Polish the UI:对于和当前操作同步的动画而言,用户满意度比较高,因为这种情况用户感觉该动画是对操作的反馈并且更加自然。这里ActionBar的动画是由于用户滚动内容区域而引发的,而不是界面启动时候的一次性动画。
  • 充分利用屏幕优势:在遵守系统UI设计风格的同时,这种模式可以让用户进来后先关注界面内容而不是一些ActionBar上的操作。

下面来看看如何实现Play Music 5.0中的这种ActionBar动画效果。

ActionBar 背景渐变动画效果_第1张图片

下载示例程序APK

App的样式/主题

如上图所示,这种模式要求ActionBar覆盖在内容之上,通过在style中设置android:windowActionBarOverlay属性即可实现这种效果。下面的代码显示如何使用自定义style:values/themes.xml

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
<? xml version = "1.0" encoding = "utf-8" ?>
< resources >
     < style name = "Theme.TranslucentActionBar" parent = "@android:style/Theme.Holo.Light.DarkActionBar" >
         < item name = "android:actionBarStyle" >@style/Widget.ActionBar</ item >
     </ style >
 
     < style name = "Theme.TranslucentActionBar.ActionBar" />
 
     < style name = "Theme.TranslucentActionBar.ActionBar.Overlay" >
         < item name = "android:actionBarStyle" >@style/Widget.ActionBar.Transparent</ item >
         < item name = "android:windowActionBarOverlay" >true</ item >
     </ style >
</ resources >

ActionBar的style定义到styles.xml文件中:

帮助
1
2
3
4
5
6
7
8
9
10
<? xml version = "1.0" encoding = "utf-8" ?>
< resources >
     < style name = "Widget.ActionBar" parent = "@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse" >
         < item name = "android:background" >@drawable/ab_background</ item >
     </ style >
 
     < style name = "Widget.ActionBar.Transparent" >
         < item name = "android:background" >@android:color/transparent</ item >
     </ style >
</ resources >

然后就可以在Activity中使用前面定义的主题样式了:

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<? xml version = "1.0" encoding = "utf-8" ?>
< manifest xmlns:android = "http://schemas.android.com/apk/res/android"
     package = "com.cyrilmottier.android.translucentactionbar"
     android:versionCode = "1"
     android:versionName = "1.0" >
 
     < uses-sdk
         android:minSdkVersion = "14"
         android:targetSdkVersion = "17" />
 
     < application
         android:allowBackup = "true"
         android:icon = "@drawable/ic_launcher"
         android:label = "@string/app_name"
         android:theme = "@style/Theme.TranslucentActionBar" >
 
         < activity
                 android:name = ".HomeActivity"
                 android:theme = "@style/Theme.TranslucentActionBar.ActionBar.Overlay" >
             < intent-filter >
                 < action android:name = "android.intent.action.MAIN" />
                 < category android:name = "android.intent.category.LAUNCHER" />
             </ intent-filter >
         </ activity >
 
     </ application >
 
</ manifest >

准备内容

如前面介绍的一样,ActionBar的动画和内容滚动操作同步。示例中使用了一个ScrollView控件,由于该控件默认没有提供监听滚动功能的接口,所以我们要自定义这个控件:NotifyingScrollView.java

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.cyrilmottier.android.translucentactionbar;
 
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
 
/**
  * @author Cyril Mottier
  */
public class NotifyingScrollView extends ScrollView {
 
     /**
      * @author Cyril Mottier
      */
     public interface OnScrollChangedListener {
         void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt);
     }
 
     private OnScrollChangedListener mOnScrollChangedListener;
 
     public NotifyingScrollView(Context context) {
         super (context);
     }
 
     public NotifyingScrollView(Context context, AttributeSet attrs) {
         super (context, attrs);
     }
 
     public NotifyingScrollView(Context context, AttributeSet attrs, int defStyle) {
         super (context, attrs, defStyle);
     }
 
     @Override
     protected void onScrollChanged( int l, int t, int oldl, int oldt) {
         super .onScrollChanged(l, t, oldl, oldt);
         if (mOnScrollChangedListener != null ) {
             mOnScrollChangedListener.onScrollChanged( this , l, t, oldl, oldt);
         }
     }
 
     public void setOnScrollChangedListener(OnScrollChangedListener listener) {
         mOnScrollChangedListener = listener;
     }
 
}

然后在布局XML文件中使用上面自定义的控件:

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<? xml version = "1.0" encoding = "utf-8" ?>
< com.cyrilmottier.android.translucentactionbar.NotifyingScrollView
     xmlns:android = "http://schemas.android.com/apk/res/android"
     android:id = "@+id/scroll_view"
     android:layout_width = "match_parent"
     android:layout_height = "match_parent" >
 
     < LinearLayout
         android:layout_width = "match_parent"
         android:layout_height = "wrap_content"
         android:orientation = "vertical" >
 
         < ImageView
             android:id = "@+id/image_header"
             android:layout_width = "match_parent"
             android:layout_height = "wrap_content"
             android:scaleType = "centerCrop"
             android:src = "@drawable/daft_punk" />
 
       <! -- Some long content -->
 
     </ LinearLayout >
 
</ com.cyrilmottier.android.translucentactionbar.NotifyingScrollView >

ActionBar的渐隐/显动画

ActionBar背景的显示只需要根据内容滚动的距离来计算背景的透明度即可。注意下面代码中滚动距离的只不能小于0,否则会出现一些奇怪的效果。HomeActivity.java

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.cyrilmottier.android.translucentactionbar;
 
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Menu;
import android.widget.ScrollView;
 
public class HomeActivity extends Activity {
 
     private Drawable mActionBarBackgroundDrawable;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_home);
 
         mActionBarBackgroundDrawable = getResources().getDrawable(R.drawable.ab_background);
         mActionBarBackgroundDrawable.setAlpha( 0 );
 
         getActionBar().setBackgroundDrawable(mActionBarBackgroundDrawable);
 
         ((NotifyingScrollView) findViewById(R.id.scroll_view)).setOnScrollChangedListener(mOnScrollChangedListener);
     }
 
     private NotifyingScrollView.OnScrollChangedListener mOnScrollChangedListener = new NotifyingScrollView.OnScrollChangedListener() {
         public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
             final int headerHeight = findViewById(R.id.image_header).getHeight() - getActionBar().getHeight();
             final float ratio = ( float ) Math.min(Math.max(t, 0 ), headerHeight) / headerHeight;
             final int newAlpha = ( int ) (ratio * 255 );
             mActionBarBackgroundDrawable.setAlpha(newAlpha);
         }
     };
}

由于JELLY_BEAN_MR1之前版本的Bug,上面的代码在JELLY_BEAN_MR1之前版本中无法正常使用。由于在之前版本中,ActionBar背景的Drawable没有注册自己为Drawable的callback,所以无法重绘自己。通过添加下面的Callback可以解决该问题:HomeActivity.java

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private Drawable.Callback mDrawableCallback = new Drawable.Callback() {
     @Override
     public void invalidateDrawable(Drawable who) {
         getActionBar().setBackgroundDrawable(who);
     }
 
     @Override
     public void scheduleDrawable(Drawable who, Runnable what, long when) {
     }
 
     @Override
     public void unscheduleDrawable(Drawable who, Runnable what) {
     }
};
 
//onCreate...
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
     mActionBarBackgroundDrawable.setCallback(mDrawableCallback);
}

增加ActionBar内容的对比度

为了避免ActionBar中的内容和内容颜色一样(比如ActionBar的文字颜色为白色,而内容图片最上面也为白色,这样在ActionBar背景透明的情况下,文字就看不清楚了),可以在内容最上方覆盖一个黑暗的半透明图层。这样即使内容颜色和ActionBar内容颜色一样的时候,也能保证ActionBar的内容可见。 唯一的缺点就是ActionBar下的内容一开始看起来比较黑暗,下面是实现方式:drawable/gradient.xml

帮助
1
2
3
4
5
6
7
8
9
10
11
12
<? xml version = "1.0" encoding = "utf-8" ?>
< shape xmlns:android = "http://schemas.android.com/apk/res/android"
        android:shape = "rectangle" >
 
     < size android:height = "100dp" />
 
     < gradient
         android:angle = "270"
         android:startColor = "#8000"
         android:endColor = "#0000" />
 
</ shape >

半透明背景覆盖到图片上面(在布局文件中把ImageView用下面代码替换):

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
< FrameLayout
     android:layout_width = "match_parent"
     android:layout_height = "wrap_content" >
 
     < ImageView
         android:id = "@+id/image_header"
         android:layout_width = "match_parent"
         android:layout_height = "wrap_content"
         android:scaleType = "centerCrop"
         android:src = "@drawable/daft_punk" />
 
     < View
         android:layout_width = "match_parent"
         android:layout_height = "wrap_content"
         android:background = "@drawable/gradient" />
 
</ FrameLayout >

避免过渡滚动

在Gingerbread (API 9)中,Android引入了一种新的方式来告诉用户可滚动内容已经滚动到头。在API 14中添加了一个EdgeEffect类并且还可以过渡滚动(和IPhone的滚动效果类似,不过没这么明显)。过渡滚动在一般情况下没什么问题,但是当内容区域和背景颜色反差比较大的时候,比较烦人。
通过快速的滑动可滚动区域可以复现这个效果,如果仔细看的话可以发现在当快速滑动到头的时候,图片上面会出现一个白色的滚动过渡的背景。

可以通过View#setOverScrollMode(int)函数并设置参数为View#OVER_SCROLL_NEVER来禁用过渡滚动来解决这个问题。 但是这样同时禁用了滚动效果。 另外一种比较好的方式是修改NotifyingScrollView类,在一些情况下设置最大过渡滚动的值为0:NotifyingScrollView.java

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private boolean mIsOverScrollEnabled = true ;
 
public void setOverScrollEnabled( boolean enabled) {
     mIsOverScrollEnabled = enabled;
}
 
public boolean isOverScrollEnabled() {
     return mIsOverScrollEnabled;
}
 
@Override
protected boolean overScrollBy( int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY,
                                int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
     return super .overScrollBy(
             deltaX,
             deltaY,
             scrollX,
             scrollY,
             scrollRangeX,
             scrollRangeY,
             mIsOverScrollEnabled ? maxOverScrollX : 0 ,
             mIsOverScrollEnabled ? maxOverScrollY : 0 ,
             isTouchEvent);
}

From:http://cyrilmottier.com/2013/05/24/pushing-the-actionbar-to-the-next-level


Read more: http://blog.chengyunfeng.com/?p=497#ixzz31qTKu3sC

你可能感兴趣的:(ActionBar 背景渐变动画效果)