詳解Android中的沉浸式狀態(tài)欄效果實(shí)例
無意間了解到沉浸式狀態(tài)欄,感覺賊拉的高大上,于是就是試著去了解一下,就有了這篇文章。下面就來了解一下啥叫沉浸式狀態(tài)欄。傳統(tǒng)的手機(jī)狀態(tài)欄是呈現(xiàn)出黑色條狀的,有的和手機(jī)主界面有很明顯的區(qū)別。這一樣就在一定程度上犧牲了視覺寬度,界面面積變小。
Google從android kitkat(Android 4.4)開始,給我們開發(fā)者提供了一套能透明的系統(tǒng)ui樣式給狀態(tài)欄和導(dǎo)航欄,這樣的話就不用向以前那樣每天面對(duì)著黑乎乎的上下兩條黑欄了,還可以調(diào)成跟Activity一樣的樣式,形成一個(gè)完整的主題,和IOS7.0以上系統(tǒng)一樣了,沉浸式狀態(tài)欄和主界面顏色和諧一體,視覺效果更加炫酷。
不過雖然聽上去好像是很高大上的沉浸式效果,實(shí)際看上去貌似就是將內(nèi)容全屏化了而已嘛。其實(shí)這算是一個(gè)爭(zhēng)議點(diǎn)了。不少人糾結(jié)于沉浸式狀態(tài)欄到底是將屏幕顯示內(nèi)容擴(kuò)大還是僅僅是改變狀態(tài)欄、標(biāo)題欄的顏色。其實(shí)我更傾向于后者。在4.4之前狀態(tài)欄一直是黑色的,在4.4中帶來了 windowTranslucentStatus 這一特性,因此可以實(shí)現(xiàn)給狀態(tài)欄設(shè)置顏色,視覺上的效果,感覺容器部分和狀態(tài)欄、標(biāo)題欄融為一體,更加直接的說就是改變狀態(tài)欄、標(biāo)題欄的顏色,當(dāng)時(shí)可以根據(jù)界面顏色改變狀態(tài)欄、標(biāo)題欄的顏色實(shí)現(xiàn)跟加完整的界面顯示,這應(yīng)該是沉浸式狀態(tài)欄受追捧的原因吧。
谷歌并沒有給出沉浸式狀態(tài)欄這個(gè)概念,谷歌只說了沉浸式模式(Immersive Mode)。不過沉浸式狀態(tài)欄這個(gè)名字其實(shí)挺不錯(cuò),只能隨大眾,但是Android的環(huán)境并沒有IOS環(huán)境一樣特別統(tǒng)一,比如華為rom的跟小米rom的虛擬按鍵完全不一樣,并且安卓版本眾多涉及到版本兼容問題,所有Android開發(fā)者不容易。這點(diǎn)在沉浸式狀態(tài)欄的開發(fā)中顯得尤為重要。如果你在4.4之前的機(jī)子上顯示沉浸式狀態(tài)欄的話,經(jīng)常出現(xiàn)一些意想不到的結(jié)果。
沉浸式是APP界面圖片延伸到狀態(tài)欄, 應(yīng)用本身沉浸于狀態(tài)欄,所以如果第三方的軟件沒有為狀態(tài)欄分配圖片,那么自然就是黑色。頂端的狀態(tài)欄和下面的虛擬按鍵都隱藏,需要的時(shí)候從邊緣劃出。沉浸模式。當(dāng)啟用該模式,應(yīng)用程序的界面將占據(jù)整個(gè)屏幕,系統(tǒng)自動(dòng)將隱藏系統(tǒng)的狀態(tài)欄和導(dǎo)航欄,讓應(yīng)用程序內(nèi)容可以在最大顯示范圍呈現(xiàn),增加大屏體驗(yàn),而當(dāng)需要查看通知的時(shí)候只需要從頂部向下滑動(dòng)就能呼出通知欄。
沉浸模式實(shí)際上有兩種: 一種叫“沉浸模式”,狀態(tài)欄和虛擬按鈕會(huì)自動(dòng)隱藏、應(yīng)用自動(dòng)全屏,這種模式下,應(yīng)用占據(jù)屏幕的全部空間, 只有當(dāng)用戶從屏幕的上方邊沿處向下劃動(dòng)時(shí), 才會(huì)退出沉浸模式, 用戶觸摸屏幕其它部分時(shí), 不會(huì)退出該模式, 這種模式比較適用于閱讀器、 雜志類應(yīng)用。另外一種叫“黏性沉浸模式”,讓狀態(tài)欄和虛擬按鈕半透明,應(yīng)用使用屏幕的全部空間, 當(dāng)用戶從屏幕的上方邊沿處向下滑動(dòng)時(shí),也不會(huì)退出該模式, 但是系統(tǒng)界面 (狀態(tài)欄、 導(dǎo)航欄) 將會(huì)以半透明的效果浮現(xiàn)在應(yīng)用視圖之上 , 只有當(dāng)用戶點(diǎn)擊系統(tǒng)界面上的控件時(shí), 才會(huì)退出黏性沉浸模式。
下面來說一說具體的實(shí)現(xiàn)。一個(gè)Android應(yīng)用程序的界面上其實(shí)是有很多系統(tǒng)元素的,有狀態(tài)欄、ActionBar、導(dǎo)航欄等。而打造沉浸式模式的用戶體驗(yàn),就是要將這些系統(tǒng)元素進(jìn)行整合,當(dāng)主界面改變時(shí),狀態(tài)欄、ActionBar、導(dǎo)航欄同時(shí)也發(fā)生改變。這里先調(diào)用getWindow().getDecorView()方法獲取到了當(dāng)前界面的DecorView,然后調(diào)用它的setSystemUiVisibility()方法來設(shè)置系統(tǒng)UI元素的可見性。其中,SYSTEM_UI_FLAG_FULLSCREEN表示全屏的意思,也就是會(huì)將狀態(tài)欄隱藏。另外,根據(jù)Android的設(shè)計(jì)建議,ActionBar是不應(yīng)該獨(dú)立于狀態(tài)欄而單獨(dú)顯示的,因此狀態(tài)欄如果隱藏了,我們同時(shí)也需要調(diào)用ActionBar的hide()方法將ActionBar也進(jìn)行隱藏這種效果不叫沉浸式狀態(tài)欄,也完全沒有沉浸式狀態(tài)欄這種說法,我們估且可以把它叫做透明狀態(tài)欄效果吧。
隱藏狀態(tài)欄:
setContentView(R.layout.activity_main); //再該方法后執(zhí)行 if (Build.VERSION.SDK_INT >= 21) { View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); getWindow().setStatusBarColor(Color.TRANSPARENT); } ActionBar actionBar = getSupportActionBar(); actionBar.hide();
具體的沉浸效果該如何實(shí)現(xiàn)呢,系統(tǒng)提供實(shí)現(xiàn)沉浸式狀態(tài)欄的方法,通過WindowManager來實(shí)現(xiàn),可分為兩步:
1. 在需要實(shí)現(xiàn)沉浸式狀態(tài)欄的Activity的布局中添加以下參數(shù)
android:fitsSystemWindows="true" android:clipToPadding="true"
2. 在Activity的setContentView()方法后面調(diào)用初始化的方法即可。
private void initState() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //透明狀態(tài)欄 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //透明導(dǎo)航欄 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); } }
當(dāng)上述的實(shí)現(xiàn)效果,其實(shí)并不好, 沒有在布局中設(shè)置clipToPadding為true的時(shí)候,會(huì)對(duì)應(yīng)用的頂部Toolbar進(jìn)行拉伸,在布局中兩個(gè)參數(shù)都進(jìn)行設(shè)置后,頂部狀態(tài)欄變成了白色。這樣,我在github上找到一個(gè)很好的沉浸狀態(tài)欄效果,來看一下。
首先添加依賴,導(dǎo)入下面的包。有時(shí)候可能會(huì)出現(xiàn)版本不統(tǒng)一的問題,依次保證聯(lián)網(wǎng)的情況下點(diǎn)擊一下同步android studio會(huì)自動(dòng)下載包。
compile 'com.jaeger.statusbaruitl:library:1.2.5'
在自定義控件中實(shí)現(xiàn)的進(jìn)本邏輯,代碼較長。
package com.xiaoyuan; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.widget.ScrollView; import java.util.ArrayList; /** * @author Emil Sj�lander - sjolander.emil@gmail.com */ public class StickyScrollView extends ScrollView { /** * Tag for views that should stick and have constant drawing. e.g. * TextViews, ImageViews etc */ public static final String STICKY_TAG = "sticky"; /** * Flag for views that should stick and have non-constant drawing. e.g. * Buttons, ProgressBars etc */ public static final String FLAG_NONCONSTANT = "-nonconstant"; /** * Flag for views that have aren't fully opaque */ public static final String FLAG_HASTRANSPARANCY = "-hastransparancy"; /** * Default height of the shadow peeking out below the stuck view. */ private static final int DEFAULT_SHADOW_HEIGHT = 10; // dp; /** * XKJ add for add 50dp offset of top */ private static int MIN_STICK_TOP = 100;// px // private static final int MIN_STICK_TOP = 0; private ArrayList<View> stickyViews; private View currentlyStickingView; private float stickyViewTopOffset; private int stickyViewLeftOffset; private boolean redirectTouchesToStickyView; private boolean clippingToPadding; private boolean clipToPaddingHasBeenSet; private int mShadowHeight; private Drawable mShadowDrawable; private OnScrollChangedListener mOnScrollHandler = null; private IOnScrollToEnd mOnScrollToEnd = null; private final Runnable invalidateRunnable = new Runnable() { @Override public void run() { if (currentlyStickingView != null) { int l = getLeftForViewRelativeOnlyChild(currentlyStickingView); int t = getBottomForViewRelativeOnlyChild(currentlyStickingView); int r = getRightForViewRelativeOnlyChild(currentlyStickingView); int b = (int) (getScrollY() + (currentlyStickingView.getHeight() + stickyViewTopOffset)); invalidate(l, t, r, b); } postDelayed(this, 16); } }; public StickyScrollView(Context context) { this(context, null); } public StickyScrollView(Context context, AttributeSet attrs) { this(context, attrs, android.R.attr.scrollViewStyle); } public StickyScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setup(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StickyScrollView, defStyle, 0); final float density = context.getResources().getDisplayMetrics().density; int defaultShadowHeightInPix = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f); mShadowHeight = a.getDimensionPixelSize(R.styleable.StickyScrollView_stuckShadowHeight, defaultShadowHeightInPix); int shadowDrawableRes = a.getResourceId(R.styleable.StickyScrollView_stuckShadowDrawable, -1); if (shadowDrawableRes != -1) { mShadowDrawable = context.getResources().getDrawable(shadowDrawableRes); } a.recycle(); } /** * Sets the height of the shadow drawable in pixels. * * @param height */ public void setShadowHeight(int height) { mShadowHeight = height; } public void setup() { stickyViews = new ArrayList<View>(); } private int getLeftForViewRelativeOnlyChild(View v) { int left = v.getLeft(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); left += v.getLeft(); } return left; } private int getTopForViewRelativeOnlyChild(View v) { int top = v.getTop(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); top += v.getTop(); } return top; } private int getRightForViewRelativeOnlyChild(View v) { int right = v.getRight(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); right += v.getRight(); } return right; } private int getBottomForViewRelativeOnlyChild(View v) { int bottom = v.getBottom(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); bottom += v.getBottom(); } return bottom; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (!clipToPaddingHasBeenSet) { clippingToPadding = true; } notifyHierarchyChanged(); } @Override public void setClipToPadding(boolean clipToPadding) { super.setClipToPadding(clipToPadding); clippingToPadding = clipToPadding; clipToPaddingHasBeenSet = true; } @Override public void addView(View child) { super.addView(child); findStickyViews(child); } @Override public void addView(View child, int index) { super.addView(child, index); findStickyViews(child); } @Override public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) { super.addView(child, index, params); findStickyViews(child); } @Override public void addView(View child, int width, int height) { super.addView(child, width, height); findStickyViews(child); } @Override public void addView(View child, android.view.ViewGroup.LayoutParams params) { super.addView(child, params); findStickyViews(child); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (currentlyStickingView != null) { canvas.save(); canvas.translate(getPaddingLeft() + stickyViewLeftOffset, getScrollY() + stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0)); canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth() - stickyViewLeftOffset, currentlyStickingView.getHeight() + mShadowHeight + 1); if (mShadowDrawable != null) { int left = 0; int right = currentlyStickingView.getWidth(); int top = currentlyStickingView.getHeight(); int bottom = currentlyStickingView.getHeight() + mShadowHeight; mShadowDrawable.setBounds(left, top, right, bottom); mShadowDrawable.draw(canvas); } canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(), currentlyStickingView.getHeight()); if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)) { showView(currentlyStickingView); currentlyStickingView.draw(canvas); hideView(currentlyStickingView); } else { currentlyStickingView.draw(canvas); } canvas.restore(); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { redirectTouchesToStickyView = true; } if (redirectTouchesToStickyView) { redirectTouchesToStickyView = currentlyStickingView != null; if (redirectTouchesToStickyView) { redirectTouchesToStickyView = ev.getY() <= (currentlyStickingView.getHeight() + stickyViewTopOffset) && ev.getX() >= getLeftForViewRelativeOnlyChild(currentlyStickingView) && ev.getX() <= getRightForViewRelativeOnlyChild(currentlyStickingView); } } else if (currentlyStickingView == null) { redirectTouchesToStickyView = false; } if (redirectTouchesToStickyView) { ev.offsetLocation(0, -1 * ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView))); // XKJ add TODO: remove this currentlyStickingView.invalidate(); } return super.dispatchTouchEvent(ev); } private boolean hasNotDoneActionDown = true; @Override public boolean onTouchEvent(MotionEvent ev) { if (redirectTouchesToStickyView) { ev.offsetLocation(0, ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView))); } if (ev.getAction() == MotionEvent.ACTION_DOWN) { hasNotDoneActionDown = false; } if (hasNotDoneActionDown) { MotionEvent down = MotionEvent.obtain(ev); down.setAction(MotionEvent.ACTION_DOWN); super.onTouchEvent(down); hasNotDoneActionDown = false; } if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { hasNotDoneActionDown = true; } return super.onTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); doTheStickyThing(); if (mOnScrollHandler != null) { mOnScrollHandler.onScrollChanged(l, t, oldl, oldt); } int maxScroll = getChildAt(0).getHeight() - getHeight(); if (getChildCount() > 0 && t == maxScroll) { if (mOnScrollToEnd != null) { mOnScrollToEnd.onScrollToEnd(); } } } public void setOnScrollListener(OnScrollChangedListener handler) { mOnScrollHandler = handler; } public interface OnScrollChangedListener { public void onScrollChanged(int l, int t, int oldl, int oldt); } public interface IOnScrollToEnd { public void onScrollToEnd(); } public void setOnScrollToEndListener(IOnScrollToEnd handler) { mOnScrollToEnd = handler; } private void doTheStickyThing() { View viewThatShouldStick = null; View approachingView = null; for (View v : stickyViews) { int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()) - MIN_STICK_TOP;// add 50dp if (viewTop <= 0) { if (viewThatShouldStick == null || viewTop > (getTopForViewRelativeOnlyChild(viewThatShouldStick) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) { viewThatShouldStick = v; } } else { if (approachingView == null || viewTop < (getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) { approachingView = v; } } } if (viewThatShouldStick != null) { stickyViewTopOffset = approachingView == null ? MIN_STICK_TOP : Math.min(MIN_STICK_TOP, getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight()); if (viewThatShouldStick != currentlyStickingView) { if (currentlyStickingView != null) { stopStickingCurrentlyStickingView(); } // only compute the left offset when we start sticking. stickyViewLeftOffset = getLeftForViewRelativeOnlyChild(viewThatShouldStick); startStickingView(viewThatShouldStick); } } else if (currentlyStickingView != null) { stopStickingCurrentlyStickingView(); } } private void startStickingView(View viewThatShouldStick) { currentlyStickingView = viewThatShouldStick; if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)) { hideView(currentlyStickingView); } if (((String) currentlyStickingView.getTag()).contains(FLAG_NONCONSTANT)) { post(invalidateRunnable); } } private void stopStickingCurrentlyStickingView() { if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)) { showView(currentlyStickingView); } currentlyStickingView = null; removeCallbacks(invalidateRunnable); } /** * Notify that the sticky attribute has been added or removed from one or * more views in the View hierarchy */ public void notifyStickyAttributeChanged() { notifyHierarchyChanged(); } private void notifyHierarchyChanged() { if (currentlyStickingView != null) { stopStickingCurrentlyStickingView(); } stickyViews.clear(); findStickyViews(getChildAt(0)); doTheStickyThing(); invalidate(); } private void findStickyViews(View v) { if (v instanceof ViewGroup) { ViewGroup vg = (ViewGroup) v; for (int i = 0; i < vg.getChildCount(); i++) { String tag = getStringTagForView(vg.getChildAt(i)); if (tag != null && tag.contains(STICKY_TAG)) { stickyViews.add(vg.getChildAt(i)); } else if (vg.getChildAt(i) instanceof ViewGroup) { findStickyViews(vg.getChildAt(i)); } } } else { String tag = (String) v.getTag(); if (tag != null && tag.contains(STICKY_TAG)) { stickyViews.add(v); } } } private String getStringTagForView(View v) { Object tagObject = v.getTag(); return String.valueOf(tagObject); } private void hideView(View v) { if (Build.VERSION.SDK_INT >= 11) { v.setAlpha(0); } else { AlphaAnimation anim = new AlphaAnimation(1, 0); anim.setDuration(0); anim.setFillAfter(true); v.startAnimation(anim); } } private void showView(View v) { if (Build.VERSION.SDK_INT >= 11) { v.setAlpha(1); } else { AlphaAnimation anim = new AlphaAnimation(0, 1); anim.setDuration(0); anim.setFillAfter(true); v.startAnimation(anim); } } /** * 設(shè)置懸浮高度 * @param height */ public void setStickTop(int height) { MIN_STICK_TOP = height; } /** * 解決vviewpager在scrollview滑動(dòng)沖突的問題 */ // 滑動(dòng)距離及坐標(biāo) private float xDistance, yDistance, xLast, yLast; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); // com.ihaveu.utils.Log.i("test", "curx:"+curX+",cury:"+curY+",xlast:"+xLast+",ylast:"+yLast); // xLast = curX; // yLast = curY; if (xDistance > yDistance) { return false; } } return super.onInterceptTouchEvent(ev); } }
接下來是調(diào)用自定義控件了,用到兩個(gè)關(guān)鍵的方法。StatusBarUtil.setTranslucentForImageView(MainActivity.this, 0, title)和llTitle.setBackgroundColor(Color.argb((int) alpha, 227, 29, 26))分別設(shè)置狀態(tài)欄和標(biāo)題欄的顏色。
package com.xiaoyuan; import android.graphics.Color; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.jaeger.library.StatusBarUtil; public class MainActivity extends AppCompatActivity implements View.OnClickListener, StickyScrollView.OnScrollChangedListener { TextView oneTextView, twoTextView; private StickyScrollView stickyScrollView; private int height; private LinearLayout llContent; private RelativeLayout llTitle; private FrameLayout frameLayout; private TextView title; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initListeners(); } /** * 初始化View */ private void initView() { stickyScrollView = (StickyScrollView) findViewById(R.id.scrollView); frameLayout = (FrameLayout) findViewById(R.id.tabMainContainer); title = (TextView) findViewById(R.id.title); oneTextView = (TextView) findViewById(R.id.infoText); llContent = (LinearLayout) findViewById(R.id.ll_content); llTitle = (RelativeLayout) findViewById(R.id.ll_good_detail); oneTextView.setOnClickListener(this); twoTextView = (TextView) findViewById(R.id.secondText); twoTextView.setOnClickListener(this); stickyScrollView.setOnScrollListener(this); StatusBarUtil.setTranslucentForImageView(MainActivity.this, 0, title); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) llTitle.getLayoutParams(); params.setMargins(0, getStatusHeight(), 0, 0); llTitle.setLayoutParams(params); //默認(rèn)設(shè)置一個(gè)Frg getSupportFragmentManager().beginTransaction().replace(R.id.tabMainContainer, Fragment.newInstance()).commit(); } /** * 獲取狀態(tài)欄高度 * * @return */ private int getStatusHeight() { int resourceId = MainActivity.this.getResources().getIdentifier("status_bar_height", "dimen", "android"); return getResources().getDimensionPixelSize(resourceId); } @Override public void onClick(View v) { if (v.getId() == R.id.infoText) { getSupportFragmentManager().beginTransaction().replace(R.id.tabMainContainer, Fragment.newInstance()).commit(); } else if (v.getId() == R.id.secondText) { getSupportFragmentManager().beginTransaction().replace(R.id.tabMainContainer, Fragment1.newInstance()).commit(); } } private void initListeners() { //獲取內(nèi)容總高度 final ViewTreeObserver vto = llContent.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { height = llContent.getHeight(); //注意要移除 llContent.getViewTreeObserver() .removeGlobalOnLayoutListener(this); } }); //獲取Fragment高度 ViewTreeObserver viewTreeObserver = frameLayout.getViewTreeObserver(); viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { height = height - frameLayout.getHeight(); //注意要移除 frameLayout.getViewTreeObserver() .removeGlobalOnLayoutListener(this); } }); //獲取title高度 ViewTreeObserver viewTreeObserver1 = llTitle.getViewTreeObserver(); viewTreeObserver1.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { height = height - llTitle.getHeight() - getStatusHeight();//計(jì)算滑動(dòng)的總距離 stickyScrollView.setStickTop(llTitle.getHeight() + getStatusHeight());//設(shè)置距離多少懸浮 //注意要移除 llTitle.getViewTreeObserver() .removeGlobalOnLayoutListener(this); } }); } @Override public void onScrollChanged(int l, int t, int oldl, int oldt) { if (t <= 0) { llTitle.setBackgroundColor(Color.argb((int) 0, 255, 255, 255)); } else if (t > 0 && t <= height) { float scale = (float) t / height; int alpha = (int) (255 * scale); llTitle.setBackgroundColor(Color.argb((int) alpha, 227, 29, 26));//設(shè)置標(biāo)題欄的透明度及顏色 StatusBarUtil.setTranslucentForImageView(MainActivity.this, alpha, title);//設(shè)置狀態(tài)欄的透明度 } else { StatusBarUtil.setTranslucentForImageView(MainActivity.this, 0, title); llTitle.setBackgroundColor(Color.argb((int) 255, 227, 29, 26)); StatusBarUtil.setTranslucentForImageView(MainActivity.this, 255, title); } } }
最后demo下載:demo
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android 實(shí)現(xiàn)沉浸式狀態(tài)欄的方法
- Android沉浸式狀態(tài)欄微技巧(帶你真正理解沉浸式模式)
- Android之沉浸式狀態(tài)欄的實(shí)現(xiàn)方法、狀態(tài)欄透明
- 解決Android 沉浸式狀態(tài)欄和華為虛擬按鍵沖突問題
- Android App仿QQ制作Material Design風(fēng)格沉浸式狀態(tài)欄
- Android 高仿QQ 沉浸式狀態(tài)欄
- 另外兩種Android沉浸式狀態(tài)欄實(shí)現(xiàn)思路
- Android沉浸式狀態(tài)欄實(shí)現(xiàn)
- Android 沉浸式狀態(tài)欄及懸浮效果
- 快速解決Android7.0下沉浸式狀態(tài)欄變灰的問題
- Android沉浸式狀態(tài)欄 + actionBar漸變 + scrollView頂部伸縮效果
- Android編程中沉浸式狀態(tài)欄的三種實(shí)現(xiàn)方式詳解
相關(guān)文章
Android函數(shù)抽取殼的實(shí)現(xiàn)代碼
很早之前就想寫這類的殼,最近終于把它做出來了,取名為dpt,下面把代碼分享出來,對(duì)Android函數(shù)抽取殼的實(shí)現(xiàn)代碼感興趣的朋友一起看看吧2022-01-01實(shí)例詳解Android自定義ProgressDialog進(jìn)度條對(duì)話框的實(shí)現(xiàn)
這篇文章主要介紹了實(shí)例詳解Android自定義ProgressDialog進(jìn)度條對(duì)話框的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-01-01MPAndroidChart繪制自定義運(yùn)動(dòng)數(shù)據(jù)圖表示例詳解
這篇文章主要為大家介紹了MPAndroidChart繪制自定義運(yùn)動(dòng)數(shù)據(jù)圖表示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Kotlin中Lambda表達(dá)式與高階函數(shù)使用分析講解
lambda 本質(zhì)上是可以傳遞給函數(shù)的一小段代碼,Kotlin 與 Java 中的 Lambda 有一定的區(qū)別,除了對(duì) lambda 的全面支持外,還有內(nèi)聯(lián)函數(shù)等簡(jiǎn)潔高效的特性。下面我們來仔細(xì)看一下2022-12-12android中實(shí)現(xiàn)OkHttp下載文件并帶進(jìn)度條
本篇文章主要介紹了android中實(shí)現(xiàn)OkHttp下載文件并帶進(jìn)度條,OkHttp是比較火的網(wǎng)絡(luò)框架,它支持同步與異步請(qǐng)求,支持緩存,可以攔截,更方便下載大文件與上傳文件的操作,有興趣的可以了解一下2017-07-07Android動(dòng)畫之補(bǔ)間動(dòng)畫(Tween Animation)基礎(chǔ)學(xué)習(xí)
補(bǔ)間動(dòng)畫是指定開始和結(jié)束的圖像狀態(tài),自動(dòng)生成需要顯示的過度圖像的動(dòng)畫。補(bǔ)間動(dòng)畫又分為四種:移動(dòng),縮放,旋轉(zhuǎn),通明度等。下面就來給大家一篇關(guān)于Android中補(bǔ)間動(dòng)畫的基礎(chǔ)知識(shí),有需要的可以參考學(xué)習(xí)。2016-09-09Android調(diào)試出現(xiàn)The selected device is incompatible問題解決
這篇文章主要介紹了Android調(diào)試出現(xiàn)The selected device is incompatible問題解決的相關(guān)資料,需要的朋友可以參考下2017-01-01