Android多界面切换不同的状态栏模式封装

3,016 阅读4分钟

效果图

分析

  • 下拉刷新根据下拉距离设置状态栏模式。
  • 滑动时根据滑动距离,更改TopBar的背景颜色和状态栏模式。
  • 切换到分类界面后将状态栏改变成分类界面需要显示的模式。
  • 切换回主页界面时回退到切换之前的模式。

前面两点很容易实现。第三点根据分类界面(或者其它界面)设置即可。关键是第四点,再一次显示主页时,根据主页的状态切换回它应该的模式。

isLightMode

在主页,每次下拉刷新或者滑动后更改状态栏模式后,我们用这个变量记录下模式,切换到其他界面回来显示时我们拿这个判断显示即可。

实现

上面的分析,都有同一个词:当界面显示时。然后再去改变状态栏的模式。

我们都接触过Fragment的懒加载模式:

private boolean isFirstInit = true;

public void onLazyResume(){
	if(isFirstInit){
    	onLazyInit();
    }
}


public void onLazyInit(){
	isFirstInit = false;
	// 懒加载代码	
}

同样的,我们也可以把状态栏的切换放在显示时

   /**
     * 状态栏模式
     */
    private boolean isLightMode;

    public void onLazyResume(){
        updateStatusBarMode(isLightMode);
        ....
    }

    /**
     * 更新状态栏模式
     */
    protected void updateStatusBarMode(boolean isLightMode) {
        if (isLight) {
            QMUIStatusBarHelper.setStatusBarLightMode(getBaseFragmentActivity());
        } else {
            QMUIStatusBarHelper.setStatusBarDarkMode(getBaseFragmentActivity());
        }
    }

这样每次显示时都能根据isLightMode显示出正确的状态栏模式了。

封装

上面的分析还存在一个问题:

  • isLightMode默认值应该是什么呢?

既然是全局的封装,那么最常见的布局是什么呢?

  • TopBar+内容层

那么我们直接拿TopBar的背景颜色作为判断

/**
 *  获取全局设置的TopBar的背景是不是白色背景,然后设置默认的状态栏模式
 */
public class StatusBarUtil {

    public static boolean isWhiteBg(View view) {
        if (null == view) return false;
        int bgColor;
        try {
            bgColor = QMUISkinHelper.getSkinColor(view, R.attr.qmui_skin_support_topbar_bg);
        } catch (Exception e) {
            bgColor = Color.WHITE;
        }
        // 这里的判断应该是一个区间范围,有些灰色背景时状态栏也应该是黑色图标
        return bgColor == Color.WHITE;
    }

}

这只是一个默认值,如果像上面一样,需要动态的去改变,那么我们上面的方法要改成:

     /**
     * @return 是否设置状态栏LightMode 深色图标 白色背景
     */
    protected boolean isStatusBarLightMode() {
        return StatusBarUtil.isWhiteBg(mRootView);
    }
    
    public void onLazyResume() {
        updateStatusBarMode(isStatusBarLightMode());
    }
    

子类记录一个变量

  • isLightMode 重写isStatusBarLightMode方法,返回这个变量即可:
    @Override
    protected boolean isStatusBarLightMode() {
        return isLightMode;
    }

但是我们不可能每个Fragment每次都要去调用这个方法,当出现上面的界面:

  • 主界面Fragment: ViewPager+TabLayout
  • ItemFragment:可能是需要变化的【主页】,有可能又是包含了ViewPager的【分类】,也可能是普通的【我的】。 像这种情况毕竟很少,我们只抓基本情况,只有根Fragment拥有更改状态栏的权限,其他的(比如ViewPager里的)都是属于它的子Fragment。 如果出现少数情况,那么子类重写上面的方法,自己解决即可。

那么问题又来了:

  • 怎么判断是不是根Fragment呢?
    /**
     * 是否为根Fragment
     */
    protected boolean isIndexFragment = false;

想想,一般子Fragment都是用在哪里呢?

  • ViewPager?
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        isIndexFragment = container instanceof ViewPager;
        return super.onCreateView(inflater, container, savedInstanceState);
    }

是啊,他的父容器是ViewPager是可以的。但是还有一种没用ViewPager的情况:

  • 用布局包裹起来,点击后进行切换 这种情况很常见吧。更有一种情况:
  • 直接嵌入作为布局的一部分(其实和上一种是一样的) 那么这两种情况上面的判断完全是不成立的。 回过头看那句话:
  • 其他的都是属于它的子Fragment 子Fragment都是有父Fragment的,那么没有父Fragment的不就是根Fragment了么?
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        isIndexFragment =  null == getParentFragment();
        return super.onCreateView(inflater, container, savedInstanceState);
    }

上面的判断就应该写成:

    /**
     * @return 是否要进行对状态栏的处理
     * @remark 默认当为根fragment时才进行处理
     */
    protected boolean isNeedChangeStatusBarMode() {
    	// onCreateView 里对这个值进行赋值的,而这个方式第一次是在onLazyResume里调用的,所以是可以直接这样写的
        return isIndexFragment;
    }

    public void onLazyResume() {
        if (isNeedChangeStatusBarMode()) {
            updateStatusBarMode(isStatusBarLightMode());
        }
    }

PS

  • 懒加载不也是可以用这个判断么? 懒加载分为两种情况:
  • 作为根Fragment时,在动画结束后开始进行耗时的初始化操作,避免页面切换卡顿。
  • 作为子Fragment时,显示时才开始进行数据请求。 同样,我们也可能用isIndexFragment判断对懒加载进行直接封装:
   private void checkLazyInit() {
       if (mIsFirstLayInit) {
           mIsFirstLayInit = false;
           onLazyInit();
       }
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
   public void onLazyResume() {
   	...
       if (!isIndexFragment) {
           checkLazyInit();
       }
   }

   @Override
   protected void onEnterAnimationEnd(@Nullable Animation animation) {
       super.onEnterAnimationEnd(animation);
       if (isIndexFragment) {
           checkLazyInit();
       }
   }