13

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

 4 years ago
source link: http://blog.cgsdream.org/2019/12/08/qmui-gank-01/
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.
neoserver,ios ssh client

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

<application  
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme"
    android:name=".GankApplication">
        <meta-data
            android:name="android.notch_support"
            android:value="true"/>

        <meta-data
            android:name="android.max_aspect"
            android:value="2.34"/>

        <meta-data
            android:name="notch.config"
            android:value="portrait|landscape"/>
</application>

为了适配全面屏和挖孔屏,我们需要添加一些 meta-data,这个没什么好说的,官方文档要求是这样,照着来就行了。

现在让我们把目光聚焦到 application 的 theme。QMUI 是支持各种配置的,而配置手段就是 theme。 因此必须要掌握它。

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

<style name="AppTheme" parent="QMUI.Compat.NoActionBar">  
    <item name="qmui_skin_support_topbar_bg">@color/app_topbar_bg_color</item>
    <item name="qmui_skin_support_topbar_title_color">@color/qmui_config_color_white</item>
    <item name="qmui_skin_support_topbar_subtitle_color">@color/qmui_config_color_75_white</item>
    <item name="qmui_skin_support_topbar_text_btn_color_state_list">@color/qmui_config_color_white</item>
    <item name="qmui_skin_support_topbar_image_tint_color">@color/qmui_config_color_white</item>
</style>

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.NoActionBarQMUI.Compat.NoActionBar 继承自 QMUI.CompatQMUI.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 注册它:

<activity  
    android:name=".LauncherActivity"
    android:theme="@style/AppTheme.Launcher">
    <intent-filter>
        <category android:name="android.intent.category.LAUNCHER"/>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
</activity>

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

<style name="AppTheme.Launcher">  
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowBackground">@drawable/launcher_bg</item>
</style>

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

<?xml version="1.0" encoding="utf-8"?>  
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
    <item android:drawable="@color/qmui_config_color_white"/>
    <item android:drawable="@mipmap/ic_launcher" android:gravity="center"/>
</layer-list>

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

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

<?xml version="1.0" encoding="utf-8"?>  
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
    <item android:drawable="@color/qmui_config_color_pure_black"/>
    <item android:drawable="@mipmap/ic_launcher" android:gravity="center"/>
</layer-list>

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

总结

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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK