效果图
分析
- 下拉刷新根据下拉距离设置状态栏模式。
- 滑动时根据滑动距离,更改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();
}
}