QMUI实战(一)—为何我们要使用 LauncherActivity?

QMUI 2 发布了,但是里面换肤等相关的很多东西,如果不讲,那么很多人估计就只能复制粘贴下 QMUIDemo 的代码,而并不能用好 QMUI, 或者是通过 QMUI 来提升自己的 UI 开发能力,毕竟现在很多 Android 开发都是轻 UI 而重数据流,遇到需要复杂 UI 的地方,在 Github 上随便找个组件套上去就行了,如果找得到刚好符合需求的还好,如果找到的不是那么切合需求,那一天的状态很可能就是“一壶茶、一包烟、几个间距颜色调一天”了。

我开这个系列(挖这个坑),不仅仅是写怎么用 QMUI 了,而是想通过 QMUI 来讲一些我个人在 QMUI 和项目开发过程中收获和心得。我开了一个新的 Github 仓库,名叫 GankWithQmui
,打算从零开始用 kotlin 去写一个 Demo 型 App ,以此来指出 Android UI 开发可能涉及到各种知识。当然更新时间或者更新主题都是不定的,个人比较随意,有了状态,才能操作拉满;没有状态,可能很快就被终结了。
每篇博文会以一个关键问题来作为标题,问题的背后可能就是我们进行最佳实践的原因了。也是希望大家看完博文能彻底掌握的知识点。不断累积,才能在今后的任何场景下得心应手。好了,我们正式开始今天的征途。

项目初始化

我们直接用 Android Studio(AS)生成无 Activity
的项目,语言选择 kotlin。我使用的 package 为 org.cgsdream.gankwithqmui
,的环境是:

  • Android Studio 4
  • kotlin 1.3.60-eap-25
  • gradle 4.0.0-alpha04

如果你 clone 代码,在你的 AS 上打不开,修改这些版本号应该就可以了。

引入QMUI依赖

我们引入 qmui 库,以及 arch 库, arch 库因为有一些使用注解进行代码生成的功能,因此还需要加入 arch-compliler 库,后面的博文会慢慢来解释它们的用途。
我们使用 kapt 来代替 annotationProcessor, 因此需要在 app/build.gradle 里添加下面这行代码:

apply plugin: 'kotlin-kapt'

然后在 app/build.gradle 的 dependencies 里加入 qmui 相关依赖:

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

Application 与 Theme

新建 Application
,我取名为 GankApplication
, QMUI 的手势返回需要注册 ActivityLifecycleCallbacks
, 因此我们需要关于它的初始化代码:

class GankApplication: Application() {

    override fun onCreate() {
        super.onCreate()
        QMUISwipeBackActivityManager.init(this)
    }
}

这个时候,我们去到 AndroidManifest.xml
文件, 将 application 标签的 name 指向 GankApplication

        

        

        

为了适配全面屏和挖孔屏,我们需要添加一些 meta-data,这个没什么好说的,官方文档要求是这样,照着来就行了。
现在让我们把目光聚焦到 application 的 theme。QMUI 是支持各种配置的,而配置手段就是 theme。 因此必须要掌握它。

我们点击 AppTheme
跳转过去,修改它的 parent 为 QMUI.Compat.NoActionBar
。QMUI 的很多组件就可以在里面设置了,例如需要更改 QMUITopBar
的相关设置:

  
    @color/app_topbar_bg_color
    @color/qmui_config_color_white
    @color/qmui_config_color_75_white
    @color/qmui_config_color_white
    @color/qmui_config_color_white

Theme 或者说 style 是具有继承性的, 我们声明了 AppTheme
的 parent 为 QMUI.Compat.NoActionBar
, 也就是说我们 AppTheme
默认拥有了 QMUI.Compat.NoActionBar
全部配置,而在 AppTheme
里添加新的 item,则是覆盖 parent 的配置,Android 系统自带的组件也有很多是可配置的,如果有特殊需求,也是可以更改的,例如更改默认 TextView
的颜色。
那么问题来了, theme 的继承规则是怎样的呢?
theme 的继承规则有两种:

  1. parent 继承,就是像我们上面使用的那样,用 parent 属性指定其父 theme。
  2. 点继承,例如 QMUI.Compat 这个 theme 是继承自 QMUI 的。

如果同时存在点继承和 parent 继承,那么采用 parent 继承。

所以 app 的 theme 继承链为: AppTheme
继承自 QMUI.Compat.NoActionBar
QMUI.Compat.NoActionBar
继承自 QMUI.Compat
QMUI.Compat
继承自 QMUI

LauncherActivity

App 启动后我们的第一个界面是什么呢? 很多 App 会有一个闪屏广告页,另外一些会存在一个 Launcher 页,而不会直接去主界面。这是为什么呢?

App 启动分为冷启动和热启动, 热启动比较简单,其实就是后台进程切换到前台。 但冷启动不一样,它需要创建进程、走 Application
初始化等步骤,然后才轮到 Activity
的启动。这个流程是很复杂的,也比较耗时,如果业务上又在 Application.onCreate
里做一堆初始化逻辑,那么这个过程就更慢了。

我们做优化时有一种方式是:让用户感知不到慢,一般是展示一些东西给用户,例如进度条、loading动画等。 Android 官方也采取了这种方式,就是立刻让用户看到界面,而这个界面展示什么呢?这个时候 Activity
在 theme 里提供背景图就派上用途了。

Activity
默认的背景就是白色,如果你没加任何处理,你会看到你的 App 冷启动时会出现闪白的情况。 因此我们需要提供一个自定义的背景图片,让用户看到更多东西,而不只是一片白色, 这就是 LauncherActivity
的一个功能。

其次,在这段时间, Activity.onCreate
都没被触发的,因此我们没办法在这段时间做沉浸式状态栏,我们会看到状态栏那里是黑色,进入主界面时,还是有不协调的感觉,特别是挖孔屏盛行后,有的手机那里特别高。 我们没有特别好的方式解决它,一般的做法是让 LauncherActivity
配置成全屏,界面切换时,就不会那么的突兀了,而独立的 LauncherActivity
也不需要我们去处理跳转到主界面后非全屏的切换问题,在 theme 里配置就行了,这就是 LauncherActivity
的第二个功能。

再者,如果我们想做主界面的 A/B Test,或者某些场景产品希望调其它的界面,针对这种场景,我们可以在 LauncherActivity
里做跳转分发逻辑,大型 App 肯定会用到的,这应该是它的第三个功能。

说了这么多,让我们来构建 LauncherActivity
吧。

先创建 LauncherActivity
,暂时先不做到主界面的跳转逻辑 :

class LauncherActivity: QMUIActivity(){  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
}

然后在 AndroidManifest.xml
注册它:

    
        
        
    

我们加上 intent-filter,使得它作为默认启动的 Activity
。 然后通过 theme 去指定全屏和背景:

  
    true
    @drawable/launcher_bg

然后添加让我们的背景 drawable, 这里我写的 launcher_bg 只是把 logo 放出来了,一般会定制一下,并且可以同 layer-list 叠加多个图层:

  
  
    
    

以前的话, LauncherActivity
配置就完成了,但是现在不行,因为我们还需要适配 Dark Mode。 并且背景展示的时机太早了, QMUI 的 skin 机制还来不及执行,因此这个时候也只能走原生的机制:

Android Dark Mode 的适配和我们其它资源在不同屏幕宽度下适配的原理是一样的,都是根据不同的状态去不同的文件夹下取资源。而 Dark Mode 的特定文件夹就是 drawable-night
。 这个目录默认是不存在,需要我们创建,然后在里面放置一个同名的 launcher_bg.xml。 这样 Dark Mode 下就会默认显示这个背景了。里面你可以自定义背景展示,我这里只是改了下背景颜色。

  
  
    
    

到这里,整个 LauncherActivity
就编码完成了,App 也可以编译运行了。 (commit id 为 86771225
, 可以 checkout 查看)

总结

今天我们从头新建了一个项目,然后写了个开端,需要掌握的是:

  1. theme 以及 theme 的继承规则。
  2. 冷启动与热启动,以及如何设置启动界面的背景。
  3. Android 系统提供的 Dark Mode 适配机制。

下期博文:QMUI实战(二)—Activity 和 Fragment,我们该选择谁?

备注:我的博文会发表在个人博客, 掘金,和公众号 QMUITeam。若需转载,注明出处就行。