今天來寫一個類似于qq空間的那種沉浸式效果。先來看看qq空間的這種效果
我們看到,頭部局上拉的時候有個頭布局的透明是從0變化到1,當你下拉的時候,頭部局透明度又從1變化到0了。始終效果看起來還是不錯的,當然這種效果要配合透明狀態欄才好看。而且我們可以再很多應用各種會看到這種廣告遮住頭布局的方式。比如160的軟件。
看起來效果還是挺酷炫的。現在我們就來講講他的實現方式吧。首先來看下demo實現的效果圖
居然說到了透明狀態欄,也說一下關于透明狀態欄和沉浸式狀態欄吧。我們首先說一下他的概念。
1.沉浸式全屏模式 隱藏status bar(狀態欄)使屏幕全屏,讓Activity接收所有的(整個屏幕的)觸摸事件。相當于就是隱藏狀態欄,讓手機瀏覽進入全屏模式2.透明化系統狀態欄 透明化系統狀態欄,使得布局侵入系統欄的后面,必須啟用fitsSystemWindows屬性來調整布局才不至于被系統欄覆蓋。
透明狀態欄的意思是指布局從狀態欄開始。然后狀態欄的一些東西比如電量那些基本信息會覆蓋在布局上面。但透明狀態欄是android 4.4及以上版本才有這種效果,4.4以下是不支持透明狀態欄的。我們先看下不設置透明狀態欄的效果。
布局文件是這樣的
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ? ? android:orientation="vertical" ? ? android:layout_width="match_parent" ? ? android:layout_height="match_parent" ? ? android:background="@color/title_bg_color" ? ? > ? ? <TextView ? ? ? ? android:id="@+id/ll_tab_top_search_parent" ? ? ? ? android:layout_width="match_parent" ? ? ? ? android:layout_height="50dp" ? ? ? ? android:background="@color/colorPrimary" ? ? ? ? android:gravity="center" ? ? ? ? android:orientation="horizontal" ? ? ? ? android:paddingBottom="9dp" ? ? ? ? android:paddingTop="10dp" ? ?/> </LinearLayout>
效果圖:
此時沒有設置狀態欄,當我們設置透明狀態欄的時候,會不一樣。來看下設置透明狀態欄的效果
設置透明狀態欄中的兩種方式
1.style文件中設置如下屬性
? ? <item name="android:windowTranslucentStatus">true</item>
然后在manifiest文件設置activity的theme屬性中引用它,就可以了
2.代碼中設置
? //api大于19時設置才有效果
??if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { ? ? ? ? ? ? getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE ? ? ? ? ? ? ? ? ? ? | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); ? ? ? ? ? ? getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
來解釋一下這幾種設置中的含義吧,大家參照一下下面的屬性對照。
setSystemUiVisibility這個方法參數表示的狀態比較多,具體如下: ? ? 1. View.SYSTEM_UI_FLAG_VISIBLE:顯示狀態欄,Activity不全屏顯示(恢復到有狀態的正常情況)。 ? ? 2. View.INVISIBLE:隱藏狀態欄,同時Activity會伸展全屏顯示。 ? ? 3. View.SYSTEM_UI_FLAG_FULLSCREEN:Activity全屏顯示,且狀態欄被隱藏覆蓋掉。 ? ? 4. View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:Activity全屏顯示,但狀態欄不會被隱藏覆蓋,狀態欄依然可見,Activity頂端布局部分會被狀態遮住。 ? ? 5. View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ? ? 6. View.SYSTEM_UI_LAYOUT_FLAGS:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ? ? 7. View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:隱藏虛擬按鍵(導航欄)。有些手機會用虛擬按鍵來代替物理按鍵。 ? ? 8. View.SYSTEM_UI_FLAG_LOW_PROFILE:狀態欄顯示處于低能顯示狀態(low profile模式),狀態欄上一些圖標顯示會被隱藏。 * window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); ? Flag表明這個窗口負責繪制系統狀態欄標題欄的背景。如果設置,系統bar繪制成透明背景,在這個窗口相應的地方會填充{@link Window#getStatusBarColor()}和 ? ? ? {@link Window#getNavigationBarColor()}對應的顏色。
但是大家有沒有注意到,有時候布局內容從狀態欄開始排布會很奇怪,比如內容布局的東西會被狀態欄的信息覆蓋。此時你想要的效果可能只是讓狀態欄變個顏色。這時候我們就需要這個屬性了。將這個屬性設置在根布局。
?android:fitsSystemWindows="true"
這時候狀態欄的顏色就會跟隨主布局。而且內容布局將不會從狀態欄開始排布,將會從狀態欄之下開始排布。我們看下效果。
這才是我們想要的效果。當然有的人會說我想自定義狀態顏色。當然可以。不過4.4系統沒有方法自定義狀態欄的顏色。5.1以上有方法可以設置狀態欄的顏色,等會我會說。不過有個很好的開源庫幫我們實現了。我們可以用它來自定義狀態欄的顏色。就是SystemBarTinManger。關于這個開源庫的使用方法也很簡單。只需要導包使用就行,使用它的前提要前提是先要設置透明狀態欄。有興趣的大家去網上看看使用方法就行了,很簡單的。接下來我們看下5.0之后設置狀態欄顏色的方法。
1.在Manifest文件中配置Activity的theme,設置狀態欄顏色,主題顏色等。
<style name="AppBaseTheme" parent="android:ThemeOverlay.Material.Dark.ActionBar"> ?
? ? ? ? <!-- 隱藏系統標題欄 --> ? ? ? ? ? <item name="android:windowNoTitle">true</item> ? ?? ? ? ? ? <!-- Main theme colors --> ? ? ? ? ? <!-- App主題顏色(滑動陰影等) ? your app's branding color (for the app bar)--> ? ? ? ? ? <item name="android:colorPrimary">#FF6600</item> ? ?? ? ? ? ? <!-- 狀態欄顏色 ? darker variant of colorPrimary (for status bar, contextual app bars) --> ? ? ? ? ? <item name="android:colorPrimaryDark">#e86053</item> ? ?? ? ? ? ? <!-- 控件主題樣式(例如EditText,CheckBox,RadioButton等) ?theme UI controls like checkboxes and text fields --> ? ? ? ? ? <item name="android:colorAccent">#BFDF0F</item> ? ?? ?? ? ? ? ? <!-- 系統默認控件樣式 --> ? ? ? ? ? <!-- 點擊時顏色 --> ? ? ? ? ? <!--<item name="android:colorControlHighlight">#0000FF</item>--> ? ? ? ? ? <!-- 正常狀態下顏色 --> ? ? ? ? ? <!--<item name="android:colorControlNormal">#FF0000</item>--> ? ? ? ? ? <!-- 選中時顏色 --> ? ? ? ? ? <!--<item name="android:colorControlActivated">#00FF00</item>--> ? ?? ? ? </style>?
然后在文件中設置相應顏色即可。?
2.下面是一個設置系統工具欄的比較通用的代碼 :
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { Windowwindow = getWindow(); window .clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); window .getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window .addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window .setStatusBarColor(color_status_bar); window .setNavigationBarColor(color_navigation_bar); }
其中getWindow.setStatusBarColor()要5.0以上才有效果。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?好的,狀態欄的知識就先說到這里了。我們來看看今天demo的實現吧。
1.實現原理
? ?其實實現原理還是挺簡單的。我們用FrameLayout作為根布局,然后設置透明狀態欄,設置一個頭布局。用頭布局覆蓋內容布局。接著在向下滑動或者向上滑動的時候,我們可以自行設置頭布局的透明度。可以利用scrollview的滑動監聽來設置。這樣就可以看到頭部局若隱若現的效果啦。
布局文件:
<? xml version="1.0" encoding="utf-8" ?>
封裝的幫助器類:
package com.example.administrator.mystatusbar; import android.content.Context;import android.graphics.Color;import android.graphics.drawable.Drawable;import android.os.Build;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.view.ViewTreeObserver;import java.lang.reflect.Field; public class ImmersiveHelper { private Context mContext; private int mAlpha; private View mStatusBar; private View mContentView; private ObservableScrollView mScrollView; private int mStatusHeight; private int mTotalHeight; private boolean isImmersived; private ObservableScrollView.ScrollViewListener listener; public ImmersiveHelper (Context mContext, View mStatusBar, final View mContentView, ObservableScrollView mScrollView) { this .mContext = mContext; this .mStatusBar = mStatusBar; this .mContentView = mContentView; this .mScrollView = mScrollView; } public void init (final Runnable runnable) { mStatusHeight = setStatusViewHeight(mContext,mStatusBar); mContentView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw () { if (mContentView.getMeasuredHeight() != 0 ){ mTotalHeight = mStatusHeight + mContentView.getMeasuredHeight(); runnable.run(); } mContentView.getViewTreeObserver().removeOnPreDrawListener(this ); return false ; } }); } public void onRestoreData (Bundle bundle) { mTotalHeight = bundle.getInt("totalHeight" ); } public void onSaveData (Bundle bundle) { bundle.putInt("totalHeight" ,mTotalHeight); } public boolean isImmersived () { return isImmersived; } public int getTotalHeight () { return mTotalHeight; } public void setImmversive (boolean isOpen, final Runnable runnable) { if (isOpen){ if (!isImmersived) { mContentView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw () { int immerseHeight = mContext.getResources().getDisplayMetrics().widthPixels / 2 - mContentView.getMeasuredHeight() - mStatusHeight; listener = setImmersiveEnabled(mScrollView, mStatusBar, mContentView, immerseHeight); logD("setImmersive-->true" ); isImmersived = true ; if (null != runnable){ runnable.run(); } mScrollView.scrollTo(0 ,0 ); mScrollView.invalidate(); mContentView.getViewTreeObserver().removeOnPreDrawListener(this ); return false ; } }); } }else if (isImmersived){ mScrollView.scrollTo(0 ,0 ); mScrollView.invalidate(); mScrollView.removeScrollViewListener(listener); mAlpha = 255 ; mStatusBar.getBackground().mutate().setAlpha(mAlpha); mContentView.getBackground().mutate().setAlpha(mAlpha); logD("setImmersive-->false" ); isImmersived = false ; if (null != runnable){ runnable.run(); } } } public ObservableScrollView.ScrollViewListener setImmersiveEnabled (ObservableScrollView scrollView, final View status, final View content, final int height) { mAlpha = 0 ; content.getBackground().mutate().setAlpha(mAlpha); status.getBackground().mutate().setAlpha(mAlpha); ObservableScrollView.ScrollViewListener listener = new ObservableScrollView.ScrollViewListener() { @Override public void onScrollChanged (ObservableScrollView scrollView, int x, int y, int oldx, int oldy) { int alpha = Math.max(0 ,Math.min(255 ,(int ) (255 * (scrollView.getScrollY()) * 1.0f / height))); if (mAlpha != alpha) { content.getBackground().mutate().setAlpha(alpha); status.getBackground().mutate().setAlpha(alpha); mAlpha = alpha; } } }; scrollView.addScrollViewListener(listener); return listener; } private void logD (String text) { Log.d("ImmersiveHelper" , text); } public static int setStatusViewHeight (Context mContext,View viewStatus) { int statusHeight = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT?getStatusBarHeight(mContext):0 ; ViewGroup.LayoutParams params = viewStatus.getLayoutParams(); if (statusHeight != params.height) { params.height = statusHeight; viewStatus.requestLayout(); } return statusHeight; } public static int getStatusBarHeight (Context context) { try { Class c = Class.forName("com.android.internal.R$dimen" ); Object obj = c.newInstance(); Field field = c.getField("status_bar_height" ); int x = Integer.parseInt(field.get(obj).toString()); int y = context.getResources().getDimensionPixelSize(x); return y; } catch (Exception e) { e.printStackTrace(); } return 0 ; } }
使用類:
emptypackage com.example.administrator.mystatusbar; import android.annotation.TargetApi;import android.app.Activity;import android.os.Build;import android.os.Bundle;import android.support.annotation.Nullable;import android.view.View;import android.view.Window;import android.view.WindowManager;import android.widget.FrameLayout;import android.widget.LinearLayout;import android.os.Handler; public class MyStatusBar extends Activity { ObservableScrollView scrollView; LinearLayout contentView; View stabarView; MyStatusBar mContext; @Override protected void onCreate (@Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.status_bar); mContext = this ; initView(); } private void initView () { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } scrollView = (ObservableScrollView) findViewById(R.id.scrollView); contentView = (LinearLayout) findViewById(R.id.ll_tab_top_search_parent); stabarView = findViewById(R.id.view_status); final ImmersiveHelper immersiveHelper = new ImmersiveHelper(mContext,stabarView,contentView,scrollView); immersiveHelper.init(new Runnable() { @Override public void run () { FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) scrollView.getLayoutParams(); params.topMargin = immersiveHelper.getTotalHeight(); scrollView.requestLayout(); } }); new Handler().postDelayed(new Runnable() { @Override public void run () { immersiveHelper.setImmversive(true , new Runnable() { @Override public void run () { if (immersiveHelper.isImmersived()){ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) scrollView.getLayoutParams(); params.topMargin = 0 ; scrollView.requestLayout(); }else { FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) scrollView.getLayoutParams(); params.topMargin = immersiveHelper.getTotalHeight(); scrollView.requestLayout(); } } }); } },3000 ); } }
關鍵代碼就是這個方法了
public ObservableScrollView.ScrollViewListener setImmersiveEnabled(ObservableScrollView scrollView, final View status, final View content, final int height){
? ? ? ? mAlpha = 0;
? ? ? ? content.getBackground().mutate().setAlpha(mAlpha);
? ? ? ? status.getBackground().mutate().setAlpha(mAlpha);
? ? ? ? ObservableScrollView.ScrollViewListener listener = new ObservableScrollView.ScrollViewListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
? ? ? ? ? ? ? ? int alpha = Math.max(0,Math.min(255,(int) (255 * (scrollView.getScrollY()) * 1.0f / height)));
? ? ? ? ? ? ? ? if(mAlpha != alpha) {
? ? ? ? ? ? ? ? ? ? content.getBackground().mutate().setAlpha(alpha);
? ? ? ? ? ? ? ? ? ? status.getBackground().mutate().setAlpha(alpha);
? ? ? ? ? ? ? ? ? ? mAlpha = alpha;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? scrollView.addScrollViewListener(listener);
? ? ? ? return listener;
? ? }
這里就是根據滑動監聽自由設置頭布局的透明實現的。
當然里面還有方法時自行控制是否需要這樣的效果的。如果不需要,我們可以讓內容布局從頭布局下面開始排列。里面還做了4.4以下的處理。大家可以看看里面的實現。應該可以看懂的....
今天的博客寫到這里,項目地址:下次給上..
總結
以上是生活随笔 為你收集整理的Android --- 详细介绍透明式状态栏和沉浸式状态栏 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。