漫谈透明状态栏和沉浸式状态栏

今天来写一个类似于qq空间的那种沉浸式效果。先来看看qq空间的这种效果


我们看到,头部局上拉的时候有个头布局的透明是从0变化到1,当你下拉的时候,头部局透明度又从1变化到0了。始终效果看起来还是不错的,当然这种效果要配合透明状态栏才好看。而且我们可以再很多应用各种会看到这种广告遮住头布局的方式。比如160的软件。



看起来效果还是挺酷炫的。现在我们就来讲讲他的实现方式吧。首先来看下demo实现的效果图

漫谈透明状态栏和沉浸式状态栏_第1张图片


居然说到了透明状态栏,也说一下关于透明状态栏和沉浸式状态栏吧。我们首先说一下他的概念。


1.沉浸式全屏模式
隐藏status bar(状态栏)使屏幕全屏,让Activity接收所有的(整个屏幕的)触摸事件。相当于就是隐藏状态栏,让手机浏览进入全屏模式


2.透明化系统状态栏
透明化系统状态栏,使得布局侵入系统栏的后面,必须启用fitsSystemWindows属性来调整布局才不至于被系统栏覆盖。


透明状态栏的意思是指布局从状态栏开始。然后状态栏的一些东西比如电量那些基本信息会覆盖在布局上面。但透明状态栏是android 4.4及以上版本才有这种效果,4.4以下是不支持透明状态栏的。我们先看下不设置透明状态栏的效果。

布局文件是这样的


    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/title_bg_color"
    >
            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"
   />


效果图:

漫谈透明状态栏和沉浸式状态栏_第2张图片


此时没有设置状态栏,当我们设置透明状态栏的时候,会不一样。来看下设置透明状态栏的效果

漫谈透明状态栏和沉浸式状态栏_第3张图片

设置透明状态栏中的两种方式

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.4系统没有方法自定义状态栏的颜色。5.1以上有方法可以设置状态栏的颜色,等会我会说。不过有个很好的开源库帮我们实现了。我们可以用它来自定义状态栏的颜色。就是SystemBarTinManger。关于这个开源库的使用方法也很简单。只需要导包使用就行,使用它的前提要前提是先要设置透明状态栏。有兴趣的大家去网上看看使用方法就行了,很简单的。接下来我们看下5.0之后设置状态栏颜色的方法。


1.在Manifest文件中配置Activity的theme,设置状态栏颜色,主题颜色等。

漫谈透明状态栏和沉浸式状态栏_第5张图片

 

然后在文件中设置相应颜色即可。 


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);
    }
}

关键代码就是这个方法了
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,android,透明状态栏,沉浸式状态栏)