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" ?> 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 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" ?> 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