詳解Android中的沉浸式狀態(tài)欄效果實例
無意間了解到沉浸式狀態(tài)欄,感覺賊拉的高大上,于是就是試著去了解一下,就有了這篇文章。下面就來了解一下啥叫沉浸式狀態(tài)欄。傳統(tǒng)的手機狀態(tài)欄是呈現(xiàn)出黑色條狀的,有的和手機主界面有很明顯的區(qū)別。這一樣就在一定程度上犧牲了視覺寬度,界面面積變小。
Google從android kitkat(Android 4.4)開始,給我們開發(fā)者提供了一套能透明的系統(tǒng)ui樣式給狀態(tài)欄和導(dǎo)航欄,這樣的話就不用向以前那樣每天面對著黑乎乎的上下兩條黑欄了,還可以調(diào)成跟Activity一樣的樣式,形成一個完整的主題,和IOS7.0以上系統(tǒng)一樣了,沉浸式狀態(tài)欄和主界面顏色和諧一體,視覺效果更加炫酷。
不過雖然聽上去好像是很高大上的沉浸式效果,實際看上去貌似就是將內(nèi)容全屏化了而已嘛。其實這算是一個爭議點了。不少人糾結(jié)于沉浸式狀態(tài)欄到底是將屏幕顯示內(nèi)容擴大還是僅僅是改變狀態(tài)欄、標(biāo)題欄的顏色。其實我更傾向于后者。在4.4之前狀態(tài)欄一直是黑色的,在4.4中帶來了 windowTranslucentStatus 這一特性,因此可以實現(xiàn)給狀態(tài)欄設(shè)置顏色,視覺上的效果,感覺容器部分和狀態(tài)欄、標(biāo)題欄融為一體,更加直接的說就是改變狀態(tài)欄、標(biāo)題欄的顏色,當(dāng)時可以根據(jù)界面顏色改變狀態(tài)欄、標(biāo)題欄的顏色實現(xiàn)跟加完整的界面顯示,這應(yīng)該是沉浸式狀態(tài)欄受追捧的原因吧。
谷歌并沒有給出沉浸式狀態(tài)欄這個概念,谷歌只說了沉浸式模式(Immersive Mode)。不過沉浸式狀態(tài)欄這個名字其實挺不錯,只能隨大眾,但是Android的環(huán)境并沒有IOS環(huán)境一樣特別統(tǒng)一,比如華為rom的跟小米rom的虛擬按鍵完全不一樣,并且安卓版本眾多涉及到版本兼容問題,所有Android開發(fā)者不容易。這點在沉浸式狀態(tài)欄的開發(fā)中顯得尤為重要。如果你在4.4之前的機子上顯示沉浸式狀態(tài)欄的話,經(jīng)常出現(xiàn)一些意想不到的結(jié)果。
沉浸式是APP界面圖片延伸到狀態(tài)欄, 應(yīng)用本身沉浸于狀態(tài)欄,所以如果第三方的軟件沒有為狀態(tài)欄分配圖片,那么自然就是黑色。頂端的狀態(tài)欄和下面的虛擬按鍵都隱藏,需要的時候從邊緣劃出。沉浸模式。當(dāng)啟用該模式,應(yīng)用程序的界面將占據(jù)整個屏幕,系統(tǒng)自動將隱藏系統(tǒng)的狀態(tài)欄和導(dǎo)航欄,讓應(yīng)用程序內(nèi)容可以在最大顯示范圍呈現(xiàn),增加大屏體驗,而當(dāng)需要查看通知的時候只需要從頂部向下滑動就能呼出通知欄。
沉浸模式實際上有兩種: 一種叫“沉浸模式”,狀態(tài)欄和虛擬按鈕會自動隱藏、應(yīng)用自動全屏,這種模式下,應(yīng)用占據(jù)屏幕的全部空間, 只有當(dāng)用戶從屏幕的上方邊沿處向下劃動時, 才會退出沉浸模式, 用戶觸摸屏幕其它部分時, 不會退出該模式, 這種模式比較適用于閱讀器、 雜志類應(yīng)用。另外一種叫“黏性沉浸模式”,讓狀態(tài)欄和虛擬按鈕半透明,應(yīng)用使用屏幕的全部空間, 當(dāng)用戶從屏幕的上方邊沿處向下滑動時,也不會退出該模式, 但是系統(tǒng)界面 (狀態(tài)欄、 導(dǎo)航欄) 將會以半透明的效果浮現(xiàn)在應(yīng)用視圖之上 , 只有當(dāng)用戶點擊系統(tǒng)界面上的控件時, 才會退出黏性沉浸模式。
下面來說一說具體的實現(xiàn)。一個Android應(yīng)用程序的界面上其實是有很多系統(tǒng)元素的,有狀態(tài)欄、ActionBar、導(dǎo)航欄等。而打造沉浸式模式的用戶體驗,就是要將這些系統(tǒng)元素進(jìn)行整合,當(dāng)主界面改變時,狀態(tài)欄、ActionBar、導(dǎo)航欄同時也發(fā)生改變。這里先調(diào)用getWindow().getDecorView()方法獲取到了當(dāng)前界面的DecorView,然后調(diào)用它的setSystemUiVisibility()方法來設(shè)置系統(tǒng)UI元素的可見性。其中,SYSTEM_UI_FLAG_FULLSCREEN表示全屏的意思,也就是會將狀態(tài)欄隱藏。另外,根據(jù)Android的設(shè)計建議,ActionBar是不應(yīng)該獨立于狀態(tài)欄而單獨顯示的,因此狀態(tài)欄如果隱藏了,我們同時也需要調(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();
具體的沉浸效果該如何實現(xiàn)呢,系統(tǒng)提供實現(xiàn)沉浸式狀態(tài)欄的方法,通過WindowManager來實現(xiàn),可分為兩步:
1. 在需要實現(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)上述的實現(xiàn)效果,其實并不好, 沒有在布局中設(shè)置clipToPadding為true的時候,會對應(yīng)用的頂部Toolbar進(jìn)行拉伸,在布局中兩個參數(shù)都進(jìn)行設(shè)置后,頂部狀態(tài)欄變成了白色。這樣,我在github上找到一個很好的沉浸狀態(tài)欄效果,來看一下。
首先添加依賴,導(dǎo)入下面的包。有時候可能會出現(xiàn)版本不統(tǒng)一的問題,依次保證聯(lián)網(wǎng)的情況下點擊一下同步android studio會自動下載包。
compile 'com.jaeger.statusbaruitl:library:1.2.5'
在自定義控件中實現(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滑動沖突的問題
*/
// 滑動距離及坐標(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)用自定義控件了,用到兩個關(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è)置一個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();//計算滑動的總距離
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
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android 實現(xiàn)沉浸式狀態(tài)欄的方法
- Android沉浸式狀態(tài)欄微技巧(帶你真正理解沉浸式模式)
- Android之沉浸式狀態(tài)欄的實現(xiàn)方法、狀態(tài)欄透明
- 解決Android 沉浸式狀態(tài)欄和華為虛擬按鍵沖突問題
- Android App仿QQ制作Material Design風(fēng)格沉浸式狀態(tài)欄
- Android 高仿QQ 沉浸式狀態(tài)欄
- 另外兩種Android沉浸式狀態(tài)欄實現(xiàn)思路
- Android沉浸式狀態(tài)欄實現(xiàn)
- Android 沉浸式狀態(tài)欄及懸浮效果
- 快速解決Android7.0下沉浸式狀態(tài)欄變灰的問題
- Android沉浸式狀態(tài)欄 + actionBar漸變 + scrollView頂部伸縮效果
- Android編程中沉浸式狀態(tài)欄的三種實現(xiàn)方式詳解
相關(guān)文章
實例詳解Android自定義ProgressDialog進(jìn)度條對話框的實現(xiàn)
這篇文章主要介紹了實例詳解Android自定義ProgressDialog進(jìn)度條對話框的實現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-01-01
MPAndroidChart繪制自定義運動數(shù)據(jù)圖表示例詳解
這篇文章主要為大家介紹了MPAndroidChart繪制自定義運動數(shù)據(jù)圖表示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Kotlin中Lambda表達(dá)式與高階函數(shù)使用分析講解
lambda 本質(zhì)上是可以傳遞給函數(shù)的一小段代碼,Kotlin 與 Java 中的 Lambda 有一定的區(qū)別,除了對 lambda 的全面支持外,還有內(nèi)聯(lián)函數(shù)等簡潔高效的特性。下面我們來仔細(xì)看一下2022-12-12
android中實現(xiàn)OkHttp下載文件并帶進(jìn)度條
本篇文章主要介紹了android中實現(xiàn)OkHttp下載文件并帶進(jìn)度條,OkHttp是比較火的網(wǎng)絡(luò)框架,它支持同步與異步請求,支持緩存,可以攔截,更方便下載大文件與上傳文件的操作,有興趣的可以了解一下2017-07-07
Android動畫之補間動畫(Tween Animation)基礎(chǔ)學(xué)習(xí)
補間動畫是指定開始和結(jié)束的圖像狀態(tài),自動生成需要顯示的過度圖像的動畫。補間動畫又分為四種:移動,縮放,旋轉(zhuǎn),通明度等。下面就來給大家一篇關(guān)于Android中補間動畫的基礎(chǔ)知識,有需要的可以參考學(xué)習(xí)。2016-09-09
Android調(diào)試出現(xiàn)The selected device is incompatible問題解決
這篇文章主要介紹了Android調(diào)試出現(xiàn)The selected device is incompatible問題解決的相關(guān)資料,需要的朋友可以參考下2017-01-01

