24

QMUI 2 终于来了

 4 years ago
source link: http://blog.cgsdream.org/2019/12/01/qmui-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

QMUI 2 提了很久很久了,在蜗牛般的开发速度下(加班过多,效率低下),终于来了首个对外版本。 你可以前往 官网 下载体验 QMUIDemo。

这个版本主要是做了以下几个主要修改:

  1. 支持库迁移到 Androidx, 最低支持版本升级到 API 19。
  2. 支持夜间模式,QMUI 做得更通用一点,提供了换肤功能。
  3. 重构 QMUIPopup 与 QMUITabSegment等组件,提供更丰富的功能。
  4. 移除旧的以背景切图来实现分隔线的实现方式与相关资源,采用 QMUILayout 方案为替代。

首先要说的是,这个版本与 1.x 版本并不完全兼容,有很多资源的增删改和组件接口与实现的变更,所以升级到 2.x 版本可能需要稍费一点时间,特别是资源配置的变更,可能导致虽然编译没问题,但是UI显示却不正确的现象。 (特别是在文档还没有完善的情况下,这个锅我背了,但还是不可能那么快完善)

引入 QMUI 2

def qmui_version = '2.0.0-alpha01'  
implementation "com.qmuiteam:qmui:$qmui_version"  
implementation "com.qmuiteam:arch:$qmui_version"  
kapt "com.qmuiteam:arch-compiler:$qmui_version" // use annotationProcessor if java

Androidx

迁移到 androidx 的呼声在 github 上是非常高的,一部分人是想要查看源码方便一点,另一部分人我估计是并不知道 jetifier 的方式可以在编译时将 support 包转换为 androidx。

另一个修改是最低支持版本提升到了 API 19。 目前都 2019 年,我都想直接提升到 API 21 了,但还是不敢太激进,只能慢慢来吧。

Dark Mode / 换肤

Rza6vev.png!webQZrEZrj.png!web

Android 10 提供了 Dark Mode 的支持,官方提供了两种实现方式:

  • 一种是在 value-night, drawable-night 放置 Dark Mode 资源。 当 Dark Mode 打开时, Activity 会被重新创建, 然后从 *-night 目录读取资源。
  • 另一种是在 AndroidManifest 文件中为 ActivityconfigChanges 加上 uiMode , 不重新重建 Activity , 而走 onConfigurationChanged 。在 onConfigurationChanged 里判断当前 uiMode , 然后做自定义处理。

第一种方式就是简单,但是它有两个弊端:

  • Dark Mode 切换时,它需要重建整个 Activity ,一般情况还好,你感知不到重建这个过程,但是如果 View 特别大,单单创建 View 就非常耗时了,那么就会明显感知到黑屏(很多产品喜欢把界面逻辑搞得特别复杂,例如 ViewPager 嵌套 ViewPager , 再嵌套 ViewPager , 而且每个 Pager 都还巨复杂)
  • 另外一点就是,既然已经支持一套皮肤,为何不能多支持几套?例如全黑白版(伪墨水屏)

因此,QMUI 推荐采取第二种方式,因此 QMUI 提供了换肤功能的支持,夜间模式只是在 onConfigurationChanged 是调用一下换肤接口而已。 当然,开发者也可以创建 *-night 文件夹,并在里面配置 qmui 的各种 color/drawable 设置,以第一种方式去完成夜间模式的适配。 QMUI 只是将组建资源取值 Attr 化,使用何种方式,都是由使用者自己决定。

常规使用方式, 在 Application.onCreate() 里加入你的 skin 支持项:

QMUISkinManager skinManager = QMUISkinManager.defaultInstance(this);  
skinManager.addSkin(1, R.style.app_skin_blue);  
skinManager.addSkin(2, R.style.app_skin_dark);  
skinManager.addSkin(3, R.style.app_skin_white);  
skinManager.changeSkin(isDarkMode ? 2 : 1);

R.style.app_skin_blueR.style.app_skin_darkR.style.app_skin_white 等就是不同 skin 下的配置, 然后在 Application.onConfigurationChanged 里监听 uiMode 的信息:

if((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES){  
    QDSkinManager.changeSkin(QDSkinManager.SKIN_DARK);
}else if(QDSkinManager.getCurrentSkin() == QDSkinManager.SKIN_DARK){
    QDSkinManager.changeSkin(QDSkinManager.SKIN_BLUE);
}

当然,对于启动 LauncherActivity 的背景,是没有机会走到 QMUI 的换肤机制的,因此要退化到第一种 Dark Mode 的实现方案。

那么业务上我们如何对 View 来设置呢? 有以下方式:

  • 如果你采用的是 xml, 可以通过 QMUI 提供的 qmui_skin_xxx 的设置项来设置你 skin。
  • 如果你想在 java 代码中设置 skin, 那么需要采用 QMUISkinValueBuilder 来构建 value, 再通过 QMUISkinHelper.setSkinValue 来设置,稍显复杂,如果通过是 kotlin 的话,QMUI 提供了 View.skin 方法,可以快速实现。
  • 还有一种尚在开发的方式, 我把它叫做 skin-maker 。 目的是可以让设计师通过点点点的方式进行 skin 设置,然后导出一份配置文件,放到工程里,通过字节码注入完成 skin 设置,这样可以将整个配置工作交给设计,不用每做个新业务都要考虑换肤。 理想是美好的,现实是目前做了一个 Demo 版,有兴趣的可以在 QMUIDemo 上体验体验,流程是通的,但还有很多问题没解决。

QMUIPopup 重写

Zr2eayI.png!webqYNfqaf.png!web

QMUIPopup 之前的实现是比较糟糕的,采用了 .9.png 的方式实现了背景阴影,因为阴影是占位置的,所以边缘距离过大,如果 anchorView 在很边缘的位置,会出现箭头错位或者显示不理想的情况,很多人踩了这个坑,而解约方案只能是提供一个阴影小一点的背景图。

因此,这个版本完全重写了它,采用了 QMUILayout 的方式实现阴影,所以你想完全贴边也是可以的。 除此之外, 2.x 版本提供了 QMUIQuickActionQMUIFullScreenPopup , 前者样式来源于微信读书阅读器的快捷菜单,当然实现不一样(虽然微信读书上的那个也是我写的,但是当年的实现,不忍直视)。 后者可以用于实现微信读书各种想法浮层,去年读书各种浮层兴起,并且还有键盘交互,我算是有一些心得吧,也提取了出来。

一个简单的使用例子:

QMUIPopups.popup(getContext(), QMUIDisplayHelper.dp2px(getContext(), 250))  
        .preferredDirection(QMUIPopup.DIRECTION_BOTTOM)
        .view(textView)
        .edgeProtection(QMUIDisplayHelper.dp2px(getContext(), 20))
        .offsetX(QMUIDisplayHelper.dp2px(getContext(), 20))
        .offsetYIfBottom(QMUIDisplayHelper.dp2px(getContext(), 5))
        .shadow(true)
        .arrow(true)
        .animStyle(QMUIPopup.ANIM_GROW_FROM_CENTER)
        .onDismiss(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                Toast.makeText(getContext(), "onDismiss", Toast.LENGTH_SHORT).show();
            }
        })
        .show(v);

组织形式比以前的舒服多了吧。

更多修改

当然,除了 QMUIPopup 的重写,其它组件也都有很大的改动:

  1. QMUITabSegment 大面积重写,提供了两张图片切换、不同 Tab 不同设置等功能,扩展也更加灵活。
  2. QMUIBottomSheet.BottomListSheetBuilder 添加了很多人期待的居中设置
  3. QMUICommonListItemView 更改父类为 ConstraintLayout, 重写布局,可以设置高度 wrap_content 以支持超长标题或副标题
  4. ...

最后,目前的版本没有经过充分的测试和实践,可能会存在比较多的问题,欢迎大家提 issue 。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK