×

关于使用BGASwipeBackLayout-Android结合StatusBarUtil的问题

96
Hankkinn
2018.08.24 23:52* 字数 866

背景

在写Reading的过程中,想实现仿IOS右滑关闭Activity的效果,在网上找到了BGASwipeBackLayout-Android
,看着效果不错,刚巧这个库也使用的猴子的StatusBarUtil
,在使用的过程中,效果确实不错,同时这个库也支持了右滑关闭Activity时候沉浸式通知栏,但是Reading里面用到了DrawerLayout,结合BGASwipeBackLayout-Android使用的时候出现了下面的问题,就是当从主Activity进入二层界面再返回的时候,底部Tab会弹一下再上来,效果有些炸眼....

2018-08-25 00_05_55.gif

解决

想要解决这个问题无非从两个角度去思考

  • BGASwipeBackLayout-Android
  • StatusBarUtil

浩哥的BGA是通过修改support-v4 包中 SlidingPaneLayout 的源码来实现滑动返回布局,源码在BGASwipeBackLayout.java这里,大概过了一下,感觉还是挺复杂的。放弃了,然后通过查看浩哥支持的为滑动界面设置状态栏颜色的方法

/**
     * 为滑动返回界面设置状态栏颜色
     *
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     */
    public static void setColorForSwipeBack(Activity activity, int color) {
        setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA);
    }
 /**
     * 为滑动返回界面设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */
    public static void setColorForSwipeBack(Activity activity, @ColorInt int color,
                                            @IntRange(from = 0, to = 255) int statusBarAlpha) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

            ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content));
            View rootView = contentView.getChildAt(0);
            int statusBarHeight = getStatusBarHeight(activity);
            if (rootView != null && rootView instanceof CoordinatorLayout) {
                final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView;
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    coordinatorLayout.setFitsSystemWindows(false);
                    contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
                    boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight;
                    if (isNeedRequestLayout) {
                        contentView.setPadding(0, statusBarHeight, 0, 0);
                        coordinatorLayout.post(new Runnable() {
                            @Override
                            public void run() {
                                coordinatorLayout.requestLayout();
                            }
                        });
                    }
                } else {
                    coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha));
                }
            } else {
                contentView.setPadding(0, statusBarHeight, 0, 0);
                contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
            }
            setTransparentForWindow(activity);
        }
    }

这段代码我们可以看到通过获取根布局,如果跟布局是CoordinatorLayout的话,设置paddingTop,并且设置颜色重绘。否则给contentView设置了paddingTop并且设置颜色。参考这段代码,我们找到了猴子的为DrawerLayout提供的沉浸式代码

/**
     * 为DrawerLayout 布局设置状态栏变色
     *
     * @param activity       需要设置的activity
     * @param drawerLayout   DrawerLayout
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */
    public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color,
                                               @IntRange(from = 0, to = 255) int statusBarAlpha) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
        } else {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        // 生成一个状态栏大小的矩形
        // 添加 statusBarView 到布局中
        ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
        View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID);
        if (fakeStatusBarView != null) {
            if (fakeStatusBarView.getVisibility() == View.GONE) {
                fakeStatusBarView.setVisibility(View.VISIBLE);
            }
            fakeStatusBarView.setBackgroundColor(color);
        } else {
            contentLayout.addView(createStatusBarView(activity, color), 0);
        }
        // 内容布局不是 LinearLayout 时,设置padding top
        if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {
            contentLayout.getChildAt(1)
                    .setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(),
                            contentLayout.getPaddingRight(), contentLayout.getPaddingBottom());
        }
        // 设置属性
        setDrawerLayoutProperty(drawerLayout, contentLayout);
        addTranslucentView(activity, statusBarAlpha);
    }

从代码来看,一眼看到代码注释//生成一个状态栏大小的矩形,也就是说支持DrawerLayout沉浸的原理是为跟布局设置 android:fitsSystemWindows="true",让整个屏幕都可以放组件,同时加了一个状态栏大小的矩形,并设置了颜色,这样就满足了DrawerLayout的沉浸式。

  • 1.fitsSystemWindow 默认是true,就是组件都在屏幕内,但是不包括statusBar。设置成false后,整个屏幕都可以放置组件,没有statusBar和window之分。
  • 2.android:fitsSystemWindows=“true”在布局中占有最高权限,如果明确设置为true,style设置fits为false是无效的;同理,只在布局中设置fits而没有设置style也是无效的。

好了,分析到这里我们不难想出来解决办法了,从浩哥提供的滑动关闭界面的沉浸代码中可以看到滑动关闭的Activity并没有设置android:fitsSystemWindows="true",那么这个Activity是有statusBar和window的区分的,通过设置了paddingTop来实现的,而猴子的setDrawerLayout支持的前提是为跟布局设置了android:fitsSystemWindows="true",也就是没有了statusBar,所以最终看到的效果就是上面的动态图,进入二级界面为了实现左滑界面的沉浸式,设置了浩哥的setColorForSwipeBack方法,当返回到主Activity时,由于主界面没有了statusBar,所以你会看到底部的tab会向上移动了一个statusBar的高度,最后解决的代码如下:

/**
     * 为滑动返回界面设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */
    public static void setColorForSwipeBackDrawerLayout(Activity activity, @ColorInt int color,
                                                        @IntRange(from = 0, to = 255) int statusBarAlpha) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

            ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content));
            View rootView = contentView.getChildAt(0);
            int statusBarHeight = getStatusBarHeight(activity);
            if (rootView != null && rootView instanceof CoordinatorLayout) {
                final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView;
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    coordinatorLayout.setFitsSystemWindows(false);
                    contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
                    boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight;
                    if (isNeedRequestLayout) {
                        contentView.setPadding(0, statusBarHeight, 0, 0);
                        coordinatorLayout.post(new Runnable() {
                            @Override
                            public void run() {
                                coordinatorLayout.requestLayout();
                            }
                        });
                    }
                } else {
                    coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha));
                }
            }
            setTransparentForWindow(activity);
        }
    }

为主Activity设置setColorForSwipeBackDrawerLayout方法,只需要把setPaddingTop删除掉就可以了,运行看效果我们发现从二级滑动返回到主Activity后底部Tab不会再上移,但是DrawerLayout不支持沉浸了,所以我们再调用一下猴子的代码 就解决了该问题。

      /**
     * 为DrawerLayout 布局设置状态栏颜色,纯色
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     * @param color        状态栏颜色值
     */
    public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {
        setColorForDrawerLayout(activity, drawerLayout, color, 0);
    }

最后感谢两位大佬的开源...

王浩
BGASwipeBackLayout-Android

猴子
StatusBarUtil

Android干货
Web note ad 1