Android中的沉浸式状态栏效果.docx
《Android中的沉浸式状态栏效果.docx》由会员分享,可在线阅读,更多相关《Android中的沉浸式状态栏效果.docx(21页珍藏版)》请在冰豆网上搜索。
![Android中的沉浸式状态栏效果.docx](https://file1.bdocx.com/fileroot1/2023-1/23/95807fea-8017-42fa-90f7-680b0bae396e/95807fea-8017-42fa-90f7-680b0bae396e1.gif)
Android中的沉浸式状态栏效果
Android中的沉浸式状态栏效果
无意间了解到沉浸式状态栏,感觉贼拉的高大上,于是就是试着去了解一下,就有了这篇文章。
下面就来了解一下啥叫沉浸式状态栏。
传统的手机状态栏是呈现出黑色条状的,有的和手机主界面有很明显的区别。
这一样就在一定程度上牺牲了视觉宽度,界面面积变小。
Google从androidkitkat(Android4.4)开始,给我们开发者提供了一套能透明的系统ui样式给状态栏和导航栏,这样的话就不用向以前那样每天面对着黑乎乎的上下两条黑栏了,还可以调成跟Activity一样的样式,形成一个完整的主题,和IOS7.0以上系统一样了,沉浸式状态栏和主界面颜色和谐一体,视觉效果更加炫酷。
不过虽然听上去好像是很高大上的沉浸式效果,实际看上去貌似就是将内容全屏化了而已嘛。
其实这算是一个争议点了。
不少人纠结于沉浸式状态栏到底是将屏幕显示内容扩大还是仅仅是改变状态栏、标题栏的颜色。
其实我更倾向于后者。
在4.4之前状态栏一直是黑色的,在4.4中带来了windowTranslucentStatus这一特性,因此可以实现给状态栏设置颜色,视觉上的效果,感觉容器部分和状态栏、标题栏融为一体,更加直接的说就是改变状态栏、标题栏的颜色,当时可以根据界面颜色改变状态栏、标题栏的颜色实现跟加完整的界面显示,这应该是沉浸式状态栏受追捧的原因吧。
谷歌并没有给出沉浸式状态栏这个概念,谷歌只说了沉浸式模式(ImmersiveMode)。
不过沉浸式状态栏这个名字其实挺不错,只能随大众,但是Android的环境并没有IOS环境一样特别统一,比如华为rom的跟小米rom的虚拟按键完全不一样,并且安卓版本众多涉及到版本兼容问题,所有Android开发者不容易。
这点在沉浸式状态栏的开发中显得尤为重要。
如果你在4.4之前的机子上显示沉浸式状态栏的话,经常出现一些意想不到的结果。
沉浸式是APP界面图片延伸到状态栏,应用本身沉浸于状态栏,所以如果第三方的软件没有为状态栏分配图片,那么自然就是黑色。
顶端的状态栏和下面的虚拟按键都隐藏,需要的时候从边缘划出。
沉浸模式。
当启用该模式,应用程序的界面将占据整个屏幕,系统自动将隐藏系统的状态栏和导航栏,让应用程序内容可以在最大显示范围呈现,增加大屏体验,而当需要查看通知的时候只需要从顶部向下滑动就能呼出通知栏。
沉浸模式实际上有两种:
一种叫“沉浸模式”,状态栏和虚拟按钮会自动隐藏、应用自动全屏,这种模式下,应用占据屏幕的全部空间,只有当用户从屏幕的上方边沿处向下划动时,才会退出沉浸模式,用户触摸屏幕其它部分时,不会退出该模式,这种模式比较适用于阅读器、杂志类应用。
另外一种叫“黏性沉浸模式”,让状态栏和虚拟按钮半透明,应用使用屏幕的全部空间,当用户从屏幕的上方边沿处向下滑动时,也不会退出该模式,但是系统界面(状态栏、导航栏)将会以半透明的效果浮现在应用视图之上,只有当用户点击系统界面上的控件时,才会退出黏性沉浸模式。
下面来说一说具体的实现。
一个Android应用程序的界面上其实是有很多系统元素的,有状态栏、ActionBar、导航栏等。
而打造沉浸式模式的用户体验,就是要将这些系统元素进行整合,当主界面改变时,状态栏、ActionBar、导航栏同时也发生改变。
这里先调用getWindow().getDecorView()方法获取到了当前界面的DecorView,然后调用它的setSystemUiVisibility()方法来设置系统UI元素的可见性。
其中,SYSTEM_UI_FLAG_FULLSCREEN表示全屏的意思,也就是会将状态栏隐藏。
另外,根据Android的设计建议,ActionBar是不应该独立于状态栏而单独显示的,因此状态栏如果隐藏了,我们同时也需要调用ActionBar的hide()方法将ActionBar也进行隐藏这种效果不叫沉浸式状态栏,也完全没有沉浸式状态栏这种说法,我们估且可以把它叫做透明状态栏效果吧。
隐藏状态栏:
复制代码
setContentView(R.layout.activity_main);//再该方法后执行
if(Build.VERSION.SDK_INT>=21){
ViewdecorView=getWindow().getDecorView();
intoption=View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ActionBaractionBar=getSupportActionBar();
actionBar.hide();
复制代码
具体的沉浸效果该如何实现呢,系统提供实现沉浸式状态栏的方法,通过WindowManager来实现,可分为两步:
1.在需要实现沉浸式状态栏的Activity的布局中添加以下参数
android:
fitsSystemWindows="true"
android:
clipToPadding="true"
2.在Activity的setContentView()方法后面调用初始化的方法即可。
复制代码
privatevoidinitState(){
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//透明导航栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
复制代码
当上述的实现效果,其实并不好,没有在布局中设置clipToPadding为true的时候,会对应用的顶部Toolbar进行拉伸,在布局中两个参数都进行设置后,顶部状态栏变成了白色。
这样,我在github上找到一个很好的沉浸状态栏效果,来看一下。
首先添加依赖,导入下面的包。
有时候可能会出现版本不统一的问题,依次保证联网的情况下点击一下同步androidstudio会自动下载包。
compile'com.jaeger.statusbaruitl:
library:
1.2.5'
在自定义控件中实现的进本逻辑,代码较长。
复制代码
packagecom.xiaoyuan;
importandroid.content.Context;
importandroid.content.res.TypedArray;
importandroid.graphics.Canvas;
importandroid.graphics.drawable.Drawable;
importandroid.os.Build;
importandroid.util.AttributeSet;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.view.animation.AlphaAnimation;
importandroid.widget.ScrollView;
importjava.util.ArrayList;
/**
*@authorEmilSj�lander-sjolander.emil@
*/
publicclassStickyScrollViewextendsScrollView{
/**
*Tagforviewsthatshouldstickandhaveconstantdrawing.e.g.
*TextViews,ImageViewsetc
*/
publicstaticfinalStringSTICKY_TAG="sticky";
/**
*Flagforviewsthatshouldstickandhavenon-constantdrawing.e.g.
*Buttons,ProgressBarsetc
*/
publicstaticfinalStringFLAG_NONCONSTANT="-nonconstant";
/**
*Flagforviewsthathavearen'tfullyopaque
*/
publicstaticfinalStringFLAG_HASTRANSPARANCY="-hastransparancy";
/**
*Defaultheightoftheshadowpeekingoutbelowthestuckview.
*/
privatestaticfinalintDEFAULT_SHADOW_HEIGHT=10;//dp;
/**
*XKJaddforadd50dpoffsetoftop
*/
privatestaticintMIN_STICK_TOP=100;//px
//privatestaticfinalintMIN_STICK_TOP=0;
privateArrayListstickyViews;
privateViewcurrentlyStickingView;
privatefloatstickyViewTopOffset;
privateintstickyViewLeftOffset;
privatebooleanredirectTouchesToStickyView;
privatebooleanclippingToPadding;
privatebooleanclipToPaddingHasBeenSet;
privateintmShadowHeight;
privateDrawablemShadowDrawable;
privateOnScrollChangedListenermOnScrollHandler=null;
privateIOnScrollToEndmOnScrollToEnd=null;
privatefinalRunnableinvalidateRunnable=newRunnable(){
@Override
publicvoidrun(){
if(currentlyStickingView!
=null){
intl=getLeftForViewRelativeOnlyChild(currentlyStickingView);
intt=getBottomForViewRelativeOnlyChild(currentlyStickingView);
intr=getRightForViewRelativeOnlyChild(currentlyStickingView);
intb=(int)(getScrollY()+(currentlyStickingView.getHeight()+stickyViewTopOffset));
invalidate(l,t,r,b);
}
postDelayed(this,16);
}
};
publicStickyScrollView(Contextcontext){
this(context,null);
}
publicStickyScrollView(Contextcontext,AttributeSetattrs){
this(context,attrs,android.R.attr.scrollViewStyle);
}
publicStickyScrollView(Contextcontext,AttributeSetattrs,intdefStyle){
super(context,attrs,defStyle);
setup();
TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.StickyScrollView,defStyle,0);
finalfloatdensity=context.getResources().getDisplayMetrics().density;
intdefaultShadowHeightInPix=(int)(DEFAULT_SHADOW_HEIGHT*density+0.5f);
mShadowHeight=a.getDimensionPixelSize(R.styleable.StickyScrollView_stuckShadowHeight,
defaultShadowHeightInPix);
intshadowDrawableRes=a.getResourceId(R.styleable.StickyScrollView_stuckShadowDrawable,-1);
if(shadowDrawableRes!
=-1){
mShadowDrawable=context.getResources().getDrawable(shadowDrawableRes);
}
a.recycle();
}
/**
*Setstheheightoftheshadowdrawableinpixels.
*
*@paramheight
*/
publicvoidsetShadowHeight(intheight){
mShadowHeight=height;
}
publicvoidsetup(){
stickyViews=newArrayList();
}
privateintgetLeftForViewRelativeOnlyChild(Viewv){
intleft=v.getLeft();
while(v.getParent()!
=getChildAt(0)){
v=(View)v.getParent();
left+=v.getLeft();
}
returnleft;
}
privateintgetTopForViewRelativeOnlyChild(Viewv){
inttop=v.getTop();
while(v.getParent()!
=getChildAt(0)){
v=(View)v.getParent();
top+=v.getTop();
}
returntop;
}
privateintgetRightForViewRelativeOnlyChild(Viewv){
intright=v.getRight();
while(v.getParent()!
=getChildAt(0)){
v=(View)v.getParent();
right+=v.getRight();
}
returnright;
}
privateintgetBottomForViewRelativeOnlyChild(Viewv){
intbottom=v.getBottom();
while(v.getParent()!
=getChildAt(0)){
v=(View)v.getParent();
bottom+=v.getBottom();
}
returnbottom;
}
@Override
protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
super.onLayout(changed,l,t,r,b);
if(!
clipToPaddingHasBeenSet){
clippingToPadding=true;
}
notifyHierarchyChanged();
}
@Override
publicvoidsetClipToPadding(booleanclipToPadding){
super.setClipToPadding(clipToPadding);
clippingToPadding=clipToPadding;
clipToPaddingHasBeenSet=true;
}
@Override
publicvoidaddView(Viewchild){
super.addView(child);
findStickyViews(child);
}
@Override
publicvoidaddView(Viewchild,intindex){
super.addView(child,index);
findStickyViews(child);
}
@Override
publicvoidaddView(Viewchild,intindex,android.view.ViewGroup.LayoutParamsparams){
super.addView(child,index,params);
findStickyViews(child);
}
@Override
publicvoidaddView(Viewchild,intwidth,intheight){
super.addView(child,width,height);
findStickyViews(child);
}
@Override
publicvoidaddView(Viewchild,android.view.ViewGroup.LayoutParamsparams){
super.addView(child,params);
findStickyViews(child);
}
@Override
protectedvoiddispatchDraw(Canvascanvas){
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){
intleft=0;
intright=currentlyStickingView.getWidth();
inttop=currentlyStickingView.getHeight();
intbottom=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
publicbooleandispatchTouchEvent(MotionEventev){
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);
}
}elseif(currentlyStickingView==null){
redirectTouchesToStickyView=false;
}
if(redirectTouchesToStickyVi