实现背景颜色随着ScrollView滑动改变的ActionBar

原文链接:http://cyrilmottier.com/2013/05/24/pushing-the-actionbar-to-the-next-level/

英文好直接戳原文哈。


先上效果图:



实现背景颜色随着ScrollView滑动改变的ActionBar


实现背景颜色随着ScrollView滑动改变的ActionBar


实现背景颜色随着ScrollView滑动改变的ActionBar



ActionBar开始是完全透明的,然后随着页面往下,开始逐渐变红,当图片完全不可见的时候,ActionBar变得完全不透明。


为ActionBar提供动画有这么两个好处:


1.为你的UI添加一些生气与活力:ActionBar的动画是跟用户的操作同步的,这往往会更吸引用户,因为这让用户觉得更自然,更像是在跟自己交互。在这一点上这个颜色渐变的动画完胜只预先加载一次的动画。


2.充分发挥屏幕的优势:这个效果让用户去注意内容而不是操作。


下面我们详细讲解一下如何实现这个炫酷的效果,源码放这了,就是这么任性!!!

链接: http://pan.baidu.com/s/1sjQACCd 密码: w6cm


主题样式的配置


在res/values下新建一个资源文件名为themes.xml,打开它,添加下面这些代码,不要粘贴答应我好吗:)


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


你刚才肯定发现了ActionBar跟屏幕内容有重叠对吧,android:windowActionBarOverlay这个属性就是用来帮我们实现重叠效果的。


接下来编辑styles.xml,添加下面的代码:


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

ab_background就是一张纯色背景图而已,你也可以在源码包里找到。transparent,透明咯。配置actionBar的style一定记得添加parent属性。


样式文件妥妥哒,现在我们就去配置一下AndroidManifest.xml


[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.zhuhean.actionbaronthemove" >  
  4.   
  5.     <application  
  6.         android:allowBackup="true"  
  7.         android:icon="@drawable/ic_launcher"  
  8.         android:label="@string/app_name"  
  9.         android:theme="@style/Theme.TranslucentActionBar" >  
  10.         <activity  
  11.             android:name=".MainActivity"  
  12.             android:label="@string/app_name"  
  13.             android:theme="@style/Theme.TranslucentActionBar.ActionBar.Overlay">  
  14.             <intent-filter>  
  15.                 <action android:name="android.intent.action.MAIN" />  
  16.   
  17.                 <category android:name="android.intent.category.LAUNCHER" />  
  18.             </intent-filter>  
  19.         </activity>  
  20.     </application>  
  21.   
  22. </manifest>  


要修改的其实就俩处,把application里的theme设成Theme.TranslucentActionBar,把activity的theme配置成Theme.TranslucentActionBar.ActionBar.Overlay。



定制自己的ScrollView


既然是滑动改变效果肯定要有一个可以滑动的容器咯,你第一个想到的肯定是ScrollView,但ScrollView有一个该死的缺点,你并不能为它注册监听滑动状态的监听器。怎么办,天啦撸我的生涯一片无悔,到底该怎么办。于是你哭了很久最后决定自己继承ScrollView来实现。


在你的Java文件所在的那个包,新建一个文件,起个霸气的名字,比如说,XuanKuWuBiDeScrollView.java,不过我这里的名字叫NotifyingScrollView.java辣,然后添加下面这些代码,再复制粘贴打手了啊。


[java] view plain copy
  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.widget.ScrollView;  
  4.   
  5. public class NotifyingScrollView extends ScrollView {  
  6.   
  7.     public interface OnScrollChangedListener{  
  8.         void OnScrollChanged(ScrollView who,int l,int t,int oldl,int oldt);  
  9.     }  
  10.   
  11.     private OnScrollChangedListener onScrollChangedListener;  
  12.   
  13.     public NotifyingScrollView(Context context) {  
  14.         super(context);  
  15.     }  
  16.   
  17.     public NotifyingScrollView(Context context,AttributeSet attributeSet){  
  18.         super(context,attributeSet);  
  19.     }  
  20.   
  21.     public NotifyingScrollView(Context context,AttributeSet attributeSet,int defStyle){  
  22.         super(context,attributeSet,defStyle);  
  23.     }  
  24.   
  25.     @Override  
  26.     protected void onScrollChanged(int l, int t, int oldl, int oldt) {  
  27.         super.onScrollChanged(l, t, oldl, oldt);  
  28.         if(onScrollChangedListener!=null){  
  29.             onScrollChangedListener.OnScrollChanged(this,l,t,oldl,oldt);  
  30.         }  
  31.     }  
  32.   
  33.     public void setOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener){  
  34.         this.onScrollChangedListener=onScrollChangedListener;  
  35.     }  
  36. }  


很简单对不对,定义了一个内部的接口,几个构造函数,重写了ScrollView得onScrollChanged()方法,最后添加了一个用来给ScrollView注册滑动监听器的方法setOnScrollChangedListener()。


然后就可以愉快的在xml文件中使用自己定义的ScrollView了,打开activity_main.xml,添加下面的代码:


[html] view plain copy
  1. <com.zhuhean.actionbaronthemove.NotifyingScrollView xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:id="@+id/scroll_view">  
  5.   
  6.     <LinearLayout  
  7.         android:layout_width="match_parent"  
  8.         android:layout_height="wrap_content"  
  9.         android:orientation="vertical">  
  10.   
  11.         <ImageView  
  12.             android:layout_width="match_parent"  
  13.             android:layout_height="350dp"  
  14.             android:scaleType="centerCrop"  
  15.             android:background="@drawable/bg"  
  16.             android:id="@+id/images"/>  
  17.   
  18.         <TextView  
  19.             android:layout_width="match_parent"  
  20.             android:layout_height="wrap_content"  
  21.             android:padding="16dp"  
  22.             android:textSize="20sp"  
  23.             android:background="#fff"  
  24.             android:text="@string/text"/>  
  25.   
  26.     </LinearLayout>  
  27.   
  28. </com.zhuhean.actionbaronthemove.NotifyingScrollView>  


自定义View的用法只是前面加上你的包名后面跟上你的自定义view(在这里就是刚才定义NotifyingScrollView)名而已。
顺便提一下xmlns:android="http://schemas.android.com/apk/res/android",xmlns在XML文件中代表声明了一个命名空间,http://schemas.android.com/apk/res/android表示一个URI(Uniform Resource Identifier),就相当于java中的包名一样,用来标志文件的独一无二,还可以用来给XML解析器提供信息。



ActionBar动画的具体实现


这已经是最后一步了。实现ActionBar动画效果的算法其实很简单的,只是根据当前ScrollView的滑动状态不断地计算alpha的值,但注意有效的动画发生区间必须设置在[0,图片高度与ActionBar高度之差],避免由于我们自定义了ScrollView而产生些奇怪的值。

打开MainActivity.java,添加下列代码:



[java] view plain copy
  1. import android.app.Activity;  
  2. import android.graphics.drawable.Drawable;  
  3. import android.os.Bundle;  
  4. import android.widget.ScrollView;  
  5.   
  6.   
  7. public class MainActivity extends Activity {  
  8.     private Drawable actionBarbackgroundDrawable;  
  9.   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.         actionBarbackgroundDrawable=getResources().getDrawable(R.drawable.ab_background);  
  15.         actionBarbackgroundDrawable.setAlpha(0);  
  16.   
  17.         getActionBar().setBackgroundDrawable(actionBarbackgroundDrawable);  
  18.         ((NotifyingScrollView)findViewById(R.id.scroll_view)).setOnScrollChangedListener(onScrollChangedListener);  
  19.     }  
  20.   
  21.     private NotifyingScrollView.OnScrollChangedListener onScrollChangedListener=new NotifyingScrollView.OnScrollChangedListener(){  
  22.         @Override  
  23.         public void OnScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {  
  24.             final int headerHeight=findViewById(R.id.images).getHeight()-getActionBar().getHeight();  
  25.             final float ratio=(float) Math.min(Math.max(t, 0), headerHeight) / headerHeight;  
  26.             final int newAlpha=(int)(ratio*255);  
  27.             actionBarbackgroundDrawable.setAlpha(newAlpha);  
  28.         }  
  29.     };  
  30. }  


动画的算法在OnScrollChanged()方法中实现。找到ScrollView为它设置一个OnScrollChangedListener()即可。

现在赶快运行一下试试。

小手一抖,可激动了,什么,不可以吗!!!??(如果成功了的就赶快截图发到朋友圈,如果没有成功的壮士请留下来继续。。。)

是不是你的minSDK在Jelly Bean之前,如果是的话,请按下面这样做:


在MainActivity.java中继续添加:


[java] view plain copy
  1. private Drawable.Callback mDrawableCallback = new Drawable.Callback() {  
  2.     @Override  
  3.     public void invalidateDrawable(Drawable who) {  
  4.         getActionBar().setBackgroundDrawable(who);  
  5.     }  
  6.   
  7.     @Override  
  8.     public void scheduleDrawable(Drawable who, Runnable what, long when) {  
  9.     }  
  10.   
  11.     @Override  
  12.     public void unscheduleDrawable(Drawable who, Runnable what) {  
  13.     }  
  14. };  


onCreate()方法中:


[java] view plain copy
  1. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {  
  2.     actionBarBackgroundDrawable.setCallback(mDrawableCallback);  
  3. }  


把一个CallBack注册给actionBarBackgroundDrawable,因为在那版本之前如果不注册一个回调机制ActionBar根本不能更新自己。


好了。有没有感觉自己萌萌打!!!

下面简单分析下 alpha的值是0-1 所以上面不用乘以255  有毛病呀  

headheight  是你要的变化距离的那个值