QMUI版本要求: v2.0.0-alpha05+

官方 Android 10 Dark Mode 适配方案

Android 10 提供了 Dark Mode 适配提供的 API:

提供了 values-night、drawable-night 资源目录,与我们做屏幕适配一样,App 会根据不同模式去不同文件夹取资源。

在 configChange 里加入了 uiMode,因而我们可以通过 onConfigurationChanged 来监听夜间模式的打开和关闭,从而做一些自定义的处理。

css 支持 prefers-color-scheme 媒体查询,从而支持 Webview 内容的夜间模式切换。

夜间模式只是配置的一种,其设计思路同横竖屏旋转等走相同的方案:

Android 官方团队认为 UI 应该是需要时创建,并且可以随意销毁重建的,因此在默认情况下,夜间模式切换后, Activity 是会被销毁而后重建的,这样重新从资源文件夹里按当前配置而实现夜间模式,这就是第一个 API 存在的意义了。

而某些场景,例如视频播放,我们可能要自定义做一些处理,那我们就可以通过配置 configChanges, 不销毁 Activity 而走 onConfigurationChanged 通知,完全由业务方接管, 这便是第二个 API 存在的意义了。

Activity 销毁重建在一些比较轻量的 UI 上效果很好,但是如果 UI 比较重,或者 Activity 没有处理数据状态保存于恢复工作的话,重建 Activity 显得有点笨拙,甚至可能出现界面黑屏的现象,体验不是很好。

QMUI 换肤提供的 API

QMUISkinManager: 存储肤色配置,并且派发当前肤色给它管理的Activity、Fragment、Dialog、PopupWindow。 它通过 QMUISkinManager.of(name, context) 获取,是可以多实例的。 因而一个 App 可以在不同场景执行不同的换肤管理, 例如阅读产品阅读器的换肤和其它业务模块 uiMode 切换的区分管理。

QMUISkinValueBuilder: 用于构建一个 View 实例的换肤配置(textColor、background、border、separator等)

QMUISkinHelper: 一些辅助工具方法,最常用的为 QMUISkinHelper.setSkinValue(View, QMUISkinValueBuilder),将 QMUISkinValueBuilder 的配置应用到一个 View 实例。 如果使用 kotlin 语言,可以通过 View.skin { ... } 来配置 View 实例。

QMUISkinLayoutInflaterFactory: 用于支持 xml 换肤配置项解析。

IQMUISkinDispatchInterceptor: View 可以通过实现它,来拦截 skin 更改的派发。

IQMUISkinHandlerView: View 可以通过实现它,来完全自定义不同 skin 的处理。

IQMUISkinDefaultAttrProvider: View 可以通过实现它, 提供 View 默认的默认换肤配置,从组件层面提供换肤支持。

QMUI 换肤流程

1. 定义 attr 以及其实现 style

这一步需要我们与设计师协作,整理一套颜色、背景资源等供 App 使用。之后我们在 xml 里以 attr 的形式给它命名,例如:

...

然后用多套 style 实现上述定义的 attr 值:

#fff

#000

...

@drawable/xxx

#ccc

...

#000

...

style 是支持继承的, 以上述为例,app_skin_3 继承自 app_skin_1, 在通过 attr 寻找其值时,如果在 app_skin_3 没找到,那么它就会去 app_skin_1 寻找。 因此我们可以把 App 的 theme 作为我们的一个 skin, 其它 skin 都继承自这个 skin。

2. 向 QMUISkinManager 里添加 skin

public static final int SKIN_1 = 1;

public static final int SKIN_2 = 2;

public static final int SKIN_3 = 3;

QMUISkinManager skinManager = QMUISkinManager.defaultInstance(context);

skinManager.addSkin(SKIN_1, R.style.app_skin_1);

skinManager.addSkin(SKIN_2, R.style.app_skin_2);

skinManager.addSkin(SKIN_3, R.style.app_skin_3);

3. 向 UI 界面注入 QMUISkinManager:

1. 向 Activity 注入 QMUISkinManager:

QMUIFragmentActivity 与 QMUIActivity 默认注入了默认的 QMUISkinManager, 如果你需要更改 QMUISkinManager, 通过 setSkinManager 更改:

class MyActivity extend QMUIActivity{

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// setSkinManager(null); // 这样可以去除这个 Activity 的换肤支持

setSkinManager(...);

}

}

QMUIFragmentActivity 与 QMUIActivity 也默认使用 QMUISkinLayoutInflaterFactory 来解析 xml,你也可以通过重写 useQMUISkinLayoutInflaterFactory() 来决定是否要使用它。

如果因为某些原因无法使用 QMUIActivity,那么你需要自己处理 QMUISkinManager 的注册:

class YourActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

// 使用 QMUISkinLayoutInflaterFactory

LayoutInflater layoutInflater = LayoutInflater.from(this);

LayoutInflaterCompat.setFactory2(layoutInflater,

new QMUISkinLayoutInflaterFactory(this, layoutInflater));

super.onCreate(savedInstanceState);

// 注入 QMUISkinManager

mSkinManager = QMUISkinManager.defaultInstance(this);

}

@Override

public void onStart() {

super.onStart();

if(mSkinManager != null){

mSkinManager.register(this);

}

}

@Override

protected void onStop() {

super.onStop();

if(mSkinManager != null){

mSkinManager.unRegister(this);

}

}

}

2. 向 Fragment 注入 QMUISkinManager:

如果 Activity 注入了 QMUISkinManager,那么它所管理的 Fragment 都会归这个 QMUISkinManager 管理。

如果你想以 Fragment 为单位管理来使用 QMUISkinManager,可以参照 Activity 来注入 QMUISkinManager。

3. 向 QMUIDialog 注入 QMUISkinManager:

new QMUIDialog.CheckableDialogBuilder(getActivity())

.setSkinManager(QMUISkinManager.of(name, context))

.xxx

.build()

.show()

MessageDialogBuilder、MenuDialogBuilder 等使用类似。

4. 向 QMUIBottomSheet 注入 QMUISkinManager:

new QMUIBottomSheet.BottomListSheetBuilder(getContext())

.setSkinManager(QMUISkinManager.of(name, context))

.xxx

.build()

.show()

BottomGridSheetBuilder 使用类似。

5. 向 QMUIPopup 注入 QMUISkinManager:

QMUIPopups.popup(context, width)

.skinManager(QMUISkinManager.of(name, context))

.xxx

.show(v);

QMUIFullScreenPopup、QMUIQuickAction 使用类似。

4. 为 View 配置 skin

配置项

QMUISkinValueBuilder 方法名

xml属性名

备注

背景

background

qmui_skin_background

字体颜色

textColor

qmui_skin_text_color

支持 TextView, QMUIQQFaceView, QMUIProgressBar

hint字体颜色

hintColor

qmui_skin_hint_color

支持 TextView, TextInputLayout

进度颜色

progressColor

qmui_skin_progress_color

支持 QMUIProgressBar,QMUISlider

src

src

qmui_skin_src

只支持 ImageView

边框

border

qmui_skin_border

支持 IQMUILayout 的实现者、QMUIRoundButton、QMUISlider.DefaultThumbView

分隔线

topSeparator, rightSeparator, bottomSeparator, leftSeparator

qmui_skin_separator_top, qmui_skin_separator_right, qmui_skin_separator_bottom, qmui_skin_separator_left

支持 IQMUILayout 的实现者

透明度

alpha

qmui_skin_alpha

着色

tintColor

qmui_skin_tint_color

支持 ImageView,QMUILoadingView,QMUIPullRefreshLayout.RefreshView

背景着色

bgTintColor

qmui_skin_bg_tint_color

支持 TintableBackgroundView 的实现者

下划线

underline

qmui_skin_underline

只支持 QMUIQQFaceView

「更多」背景

moreBgColor

qmui_skin_more_bg_color

只支持 QMUIQQFaceView

「更多」字体颜色

moreTextColor

qmui_skin_more_text_color

只支持 QMUIQQFaceView

CompoundDrawable着色

textCompoundTintColor

qmui_skin_text_compound_tint_color

只支持 TextView

CompoundDrawable src

textCompoundTopSrc,textCompoundRightSrc,textCompoundBottomSrc,textCompoundLeftSrc

qmui_skin_text_compound_src_left, qmui_skin_text_compound_src_top, qmui_skin_text_compound_src_right, qmui_skin_text_compound_src_bottom

只支持 TextView

1. 通过 java 配置

QMUISkinValueBuilder builder = QMUISkinValueBuilder.acquire();

builder.background(R.attr.app_skin_common_background);

builder.border(R.attr.qmui_skin_support_color_separator);

// more ....

QMUISkinHelper.setSkinValue(view, builder);

builder.release();

2. 通过 kotlin 配置

skin {

background(R.attr.app_skin_common_background)

border(R.attr.qmui_skin_support_color_separator)

// more...

}

3. 通过 xml 配置

...

app:qmui_skin_border="?attr/qmui_skin_support_color_separator"

app:qmui_skin_background="?attr/app_skin_common_background"/>

需要注意的是,QMUISkinValueBuilder 所配置的属性并不是所有 View 都支持的,例如 border、separator 只支持 IQMUILayout 的实现者。 在不支持的情况下, QMUI 会给出 warn 信息,因此使用 QMUI 时最好调用 QMUILog.setDelegate() 来接收 QMUI 的一些日志信息。

5. 通知 skin 更改

QMUISkinManager.changeSkin(SKIN_2)

6. 适配 Dark Mode

首先我们要在 AndroidManifest 里将 uiMode 加入到 Activity 的 configChanges 里

android:name=".YourActivity"

android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|uiMode"

android:windowSoftInputMode="stateAlwaysHidden|adjustResize">

然后在 Application.onConfigurationChanged 里根据当前 uiMode 更改 skin

@Override

public void onConfigurationChanged(@NonNull Configuration newConfig) {

super.onConfigurationChanged(newConfig);

if((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES){

// 假设 SKIN_2 为 Dark Mode 下的 skin

QDSkinManager.changeSkin(SKIN_2);

}else if(QDSkinManager.getCurrentSkin() == QDSkinManager.SKIN_DARK){

QDSkinManager.changeSkin(SKIN_1);

}

}

QMUI 换肤扩展

1. 提供组件的默认换肤配置

一些通用组件,例如 TopBar, 换肤配置一般都是相同的,没必要每次实例化的时候都配置一次。因而 QMUI 提供了 IQMUISkinDefaultAttrProvider, 用于提供默认配置项, 以 QMUITopBar 为例:

class QMUITopBar implements IQMUISkinDefaultAttrProvider {

private static SimpleArrayMap sDefaultSkinAttrs;

static {

sDefaultSkinAttrs = new SimpleArrayMap<>(4);

sDefaultSkinAttrs.put(QMUISkinValueBuilder.BOTTOM_SEPARATOR, R.attr.qmui_skin_support_topbar_separator_color);

sDefaultSkinAttrs.put(QMUISkinValueBuilder.BACKGROUND, R.attr.qmui_skin_support_topbar_bg);

}

@Override

public SimpleArrayMap getDefaultSkinAttrs() {

return sDefaultSkinAttrs;

}

}

因而, 只需要我们的 skin style 里提供 `qmui_skin_support_topbar_bg`、`qmui_skin_support_topbar_separator_color` 等的值,所有的 `QMUITopBar` 实例都会走这个配置。

如果你有某一个 `QMUITopBar` 实例需要不同的配置,那么通过 `QMUISkinValueBuilder` 覆盖就可。

如果无法重写这个某些 View,也可以通过 QMUISkinHelper.setSkinDefaultProvider() 来设置默认配置, QMUITopBar 的左右按钮实际上就是用这种方式来走默认配置的。

2. 自定义 RuleHandler

在实现层面上,QMUISkinValueBuilder 实际上是设置换肤应该遵循的一些 rule 以及对应的 值。 QMUISkinManager 里存储了所有 rule 的处理器。

我们可以通过 QMUISkinValueBuilder.setRuleHandler(String name, IQMUISkinRuleHandler handler) 来覆盖默认的处理器, 或者提供新的处理器, 对于新的处理器, 可以通过 QMUISkinValueBuilder.custom(String name, int attr) 设置 rule 以及其对应的配置值。(或许叫 token 更合适?)

QMUI 组件换肤配置

QMUI 为许多组件提供了 skin 配置项,因而我们可以在 App 的各个 skin style 以及 AppTheme 里配置 QMUI 组件的肤色。 各个组件的配置项可查看 qmui_themes.xml 的的值,之后也会在 wiki 里组件文档里列举其配置项。

android+qq换肤实现,QMUI 换肤 · Tencent/QMUI_Android Wiki · GitHub相关推荐

  1. android 第三方登录界面,Android App集成第三方登录与换肤指南

    Android App集成第三方登录与换肤指南 文档编辑 概述 本文主要是介绍了如何通过开源框架快速支持QQ和微信登录,并介绍了如何实现app快速换肤 QQ登录接入 APP要支持QQ登录,需要先到腾讯 ...

  2. Android 手写实现插件化换肤框架 兼容Android10 Android11

    目录 一.收集所有需要换肤的view及相关属性 二.统一为所有Activity设置工厂(兼容Android9以上) 三.加载皮肤包资源 四.处理支持库或者自定义view的换肤 五.处理状态栏换肤 六. ...

  3. android换肤动画,Android动态换肤框架-实现换肤

    1.换肤流程 1 2.采集流程 2 3.Android资源查找流程 3 4.采集需要换肤的控件 换肤我们需要换所有可能需要换的页面控件,所以我们不可能在每个页面重新findviewById,这时就需要 ...

  4. Android通过Hook技术实现一键换肤

    目录 1.什么是一键换肤 2.界面上那些东西可以换肤 3.利用Hook实现一键换肤 4.Android创建视图源码分析 4.1.自定义Activity设置要显示的布局文件xml 4.2.调用兼容App ...

  5. 红橙Darren视频笔记 换肤框架4 换肤的功能完善 内存泄漏分析

    上一篇完成了换肤框架的基本搭建,这一次 我们继续补完上一次遗留的一些可以完善的部分 1.完善换肤 1.1退出后再进入应用 不会丢失上一次保存的皮肤 基本原理:将上一次切换的皮肤path保存在Share ...

  6. vue+element如何一键换肤和保存换肤

    因为有人问我如何一键换肤,我就随手做个案例,因为随手写的,里面命名就很随意了,但是不影响阅读. 1:assets目录下建立一个theme.scss 代码如下: .black{.alldiv{backg ...

  7. android9默认字体下载,iFont爱字体 v5.5.9 Android特别版-实用的手机换字体软件

    iFont爱字体 v5.5.9 Android特别版-实用的手机换字体软件 书法字体2015.09.28iFont iFont(爱字体)是安卓平台最强大.最专业的字体软件,精彩字体,随你所换!iFon ...

  8. android自定义app图标下载,安卓换图标

    安卓换图标app又叫换图标app,是一款可以快速为安卓手机用户替换手机图标的软件,换图标变得更简单哦!平台上有更多的手机app图标可以选择,也可以自己设置喜欢的图标图片,超级实用,打造个性化桌面,快来 ...

  9. android到iPhone换手机,从iPhone换成Android手机之后,将是一种怎样的不同体验!

    欢迎转载,请注明出处,抄袭必究! 随着国产手机的不断发展,性能提升,价格优势突出,不少的网友准备放弃苹果手机,选择加入到安卓阵营,那么从iPhone换成Android手机之后,将是一种怎样的不同体验! ...

最新文章

  1. 深度模拟java动态代理实现机制系类之三
  2. 自定义tt文本模板实现MySql指数据库中生成实体类
  3. websocket传输数据大小限制_WebSocket基础知识笔记
  4. Datawhale编程学习之数组和链表(1)
  5. 软件工程的极端所有权
  6. idea 谷歌翻译
  7. vue高拍仪拍照后上传服务器回显到Upload(记录)
  8. 微信公众号 | 适合程序员的公众号排版
  9. gulp-sass 使用报错Error:gulp-sass no longer has a default Sass compiler; please set one yourself
  10. 2021年flutter开发宝淘买菜前后端源码-免费开源
  11. 如何设置html的背景效果,背景图片的透明度如何设置(CSS)
  12. Confluence 7 删除页面和子页面
  13. xCAT安装配置文档
  14. 功放前级的左右_Altitude32前级功放题 - 什么是全景声功放_全景声功放有哪些
  15. Mybatis | Mybatis-plus配置多数据源,连接多数据库
  16. Facebook商务管理插件怎么使用
  17. phpcms选择文件无法加载插件怎么办_浏览器显示无法加载插件怎么办
  18. 通过PS把荒野草地变成大雪纷飞的雪景
  19. 服务器提示位置不可用 拒绝访问,关于解答Win10系统提示位置不可用拒绝访问的完全处理办法...
  20. 目标检测中对precision和recall的理解

热门文章

  1. 负载均衡的几种常用方案
  2. 黑马程序员java笔记之二-----多线程
  3. linux]ubuntu挂载U盘
  4. linux 文件与目录管理命令
  5. ORACLE限制IP访问数据库
  6. IBM收购Q1 Labs
  7. Bitcoin.com开发人员正创建一个用Rust语言编写的BCH全节点
  8. 比特币黄金(BTG)遭受51%双花攻击?——不亏
  9. windows auzre 笔记-1
  10. PHP无法编译undefined reference to `libiconv_open