今天来写一个类似于qq空间的那种沉浸式效果。先来看看qq空间的这种效果
我们看到,头部局上拉的时候有个头布局的透明是从0变化到1,当你下拉的时候,头部局透明度又从1变化到0了。始终效果看起来还是不错的,当然这种效果要配合透明状态栏才好看。而且我们可以再很多应用各种会看到这种广告遮住头布局的方式。比如160的软件。
看起来效果还是挺酷炫的。现在我们就来讲讲他的实现方式吧。首先来看下demo实现的效果图
居然说到了透明状态栏,也说一下关于透明状态栏和沉浸式状态栏吧。我们首先说一下他的概念。
1.沉浸式全屏模式隐藏status bar(状态栏)使屏幕全屏,让Activity接收所有的(整个屏幕的)触摸事件。相当于就是隐藏状态栏,让手机浏览进入全屏模式2.透明化系统状态栏透明化系统状态栏,使得布局侵入系统栏的后面,必须启用fitsSystemWindows属性来调整布局才不至于被系统栏覆盖。
透明状态栏的意思是指布局从状态栏开始。然后状态栏的一些东西比如电量那些基本信息会覆盖在布局上面。但透明状态栏是android 4.4及以上版本才有这种效果,4.4以下是不支持透明状态栏的。我们先看下不设置透明状态栏的效果。
布局文件是这样的
效果图:
此时没有设置状态栏,当我们设置透明状态栏的时候,会不一样。来看下设置透明状态栏的效果
设置透明状态栏中的两种方式
1.style文件中设置如下属性
true
然后在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,设置状态栏颜色,主题颜色等。
true #FF6600 #e86053 #BFDF0F
然后在文件中设置相应颜色即可。
2.下面是一个设置系统工具栏的比较通用的代码:
if(VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { Windowwindow = getWindow(); //清除系统提供的默认保护色,After LOLLIPOP not translucent status bar window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); //设置系统UI的显示方式 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的滑动监听来设置。这样就可以看到头部局若隐若现的效果啦。
布局文件:
封装的帮助器类:
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(); } } } /** * 设置沉浸式交互 * @param height 沉浸的总高度 */ 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); } /** * 设置需要充当状态栏的视图高度 * @param viewStatus 视图 */ 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; } /** * 获取状态栏高度 * * @param context * @return */ public static int getStatusBarHeight(Context context) { /*Rect rect = new Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); return rect.top;*/ 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); } }