ActionBar 背景漸變動畫效果

App的樣式/主題

如上圖所示,這種模式要求ActionBar覆蓋在內容之上,通過在style中設置android:windowActionBarOverlay屬性即可實現這種效果。下面的代碼顯示如何使用自定義style:values/themes.xml

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

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

1
2
3
4
5
6
7
8
9
10
<?xmlversion="1.0"encoding="utf-8"?>
<resources>
    <stylename="Widget.ActionBar"parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse">
        <itemname="android:background">@drawable/ab_background</item>
    </style>
 
    <stylename="Widget.ActionBar.Transparent">
        <itemname="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
<?xmlversion="1.0"encoding="utf-8"?>
    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>
                <actionandroid:name="android.intent.action.MAIN"/>
                <categoryandroid: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 extendsScrollView {
 
    /**
     * @author Cyril Mottier
     */
    publicinterface OnScrollChangedListener {
        voidonScrollChanged(ScrollView who, intl, int t, int oldl, int oldt);
    }
 
    privateOnScrollChangedListener mOnScrollChangedListener;
 
    publicNotifyingScrollView(Context context) {
        super(context);
    }
 
    publicNotifyingScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    publicNotifyingScrollView(Context context, AttributeSet attrs,int defStyle) {
        super(context, attrs, defStyle);
    }
 
    @Override
    protectedvoid onScrollChanged(intl, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if(mOnScrollChangedListener != null) {
            mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt);
        }
    }
 
    publicvoid 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
<?xmlversion="1.0"encoding="utf-8"?>
<com.cyrilmottier.android.translucentactionbar.NotifyingScrollView
    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 extendsActivity {
 
    privateDrawable mActionBarBackgroundDrawable;
 
    @Override
    publicvoid 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);
    }
 
    privateNotifyingScrollView.OnScrollChangedListener mOnScrollChangedListener =new NotifyingScrollView.OnScrollChangedListener() {
        publicvoid onScrollChanged(ScrollView who,int l, intt, int oldl, int oldt) {
            finalint headerHeight = findViewById(R.id.image_header).getHeight() - getActionBar().getHeight();
            finalfloat ratio = (float) Math.min(Math.max(t,0), headerHeight) / headerHeight;
            finalint 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 = newDrawable.Callback() {
    @Override
    publicvoid invalidateDrawable(Drawable who) {
        getActionBar().setBackgroundDrawable(who);
    }
 
    @Override
    publicvoid scheduleDrawable(Drawable who, Runnable what,long when) {
    }
 
    @Override
    publicvoid 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
<?xmlversion="1.0"encoding="utf-8"?>
       android:shape="rectangle">
 
    <sizeandroid: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(booleanenabled) {
    mIsOverScrollEnabled = enabled;
}
 
public boolean isOverScrollEnabled() {
    returnmIsOverScrollEnabled;
}
 
@Override
protectedboolean overScrollBy(intdeltaX, int deltaY, int scrollX,int scrollY, int scrollRangeX, int scrollRangeY,
                               intmaxOverScrollX, intmaxOverScrollY, booleanisTouchEvent) {
    returnsuper.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

發佈了56 篇原創文章 · 獲贊 16 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章