Android一键更换主题套件

前言

前阵子看了bilibili上的一些技术相关影片,码牛学院的公开课程
Android动态加载技术的高级进阶,手写实现网易云主题换肤框架

影片中的讲者用Java初步实作了一个修改主题的框架
学习的过程动作做是个满重要的阶段
因此我把影片中的这个框架跟着教学手刻一次

刻完套用在我自己的测试专案上时发现还有些问题
接着就依照遇到的问题进行进一步的修改

在教学影片中看得到的逻辑规则,本篇文章就不赘述,我会直接说明我基於教学的框架增加或修改的部分
不想浪费时间看文章可以直接看我的Repository ChangeThemeSample

问题集

- 第一个遇到的问题是,依照我过去的开发习惯,专案经常会大量的使用到style来设定相同规格的物件

例如这样统一设定TextView的风格

<style name="style_button_text">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textColor">@color/white</item>
    <item name="android:layout_gravity">center</item>
    <item name="android:gravity">center</item>
    <item name="android:background">@drawable/selector_btn_circle_default</item>
    <item name="android:paddingBottom">10dp</item>
</style>

 <TextView
    style="@style/style_button_text" />

在使用style的情况下,透过AttributeSet getAttributeName只会取到"style"
而style_button_text里面有设定的textColor, background是取不到的
这样在後续设定主题时,使用style的写法就会失效

因此需要针对style的况状更深入的处理
首先我定义了,方便foreach确认属性是否存在

private static final int[] NATIVE_ATTRIBUTE_ID = {
        android.R.attr.textColor,
        android.R.attr.background,
        android.R.attr.src
};

逐一确认style中是否能够取到我们对应的属性

TypedArray typedArray = view.getContext().obtainStyledAttributes(attrs.getStyleAttribute(), NATIVE_ATTRIBUTE_ID);
if (typedArray.length() > 0){
    for(int ti = 0; ti < typedArray.length(); ti++){
        try{
            if(typedArray.hasValue(ti)){
                int resId = typedArray.getResourceId(ti, -1);
                if(resId != -1) {
                    addSkinItem(view, NATIVE_ATTRIBUTE_NAME[ti], resId, skinItems);
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}
typedArray.recycle();

这边有遇到一个问题还没深入去找原因,NATIVE_ATTRIBUTE_ID中将textColor及background顺序调换後,textColor会失效取不到resourceId,之後有空必须要查查

- 第二个问题是,不支援专案正在使用的物件或属性

解决方案当然就是要把物件及相应的属性也加入到判断的清单

private static final String[] THIRDPARTY_ATTRIBUTE_NAME = {
        "tabIndicator",
        "tabIndicatorColor"
};

private static final String[] THIRDPARTY_VIEW = {
        "com.google.android.material.tabs.TabLayout"
};

利用反射的方式将数值置换

Method setSelectedTabIndicator = view.getClass().getDeclaredMethod("setSelectedTabIndicator", Drawable.class);
                        setSelectedTabIndicator.invoke(view, SkinManager.getInstance().getDrawable(skinItem.getResId()));

- 第三个问题则是由第二个问题衍伸

Android的物件、属性千百种,实在不太可能每一个都加入我们的判断规则中,就算都整理进来多少也会有效能上的问题,例如每个onCreateView都要foreach跑一次包含2000项目的array大概受不了

因此需要增加一个可以让人扩充判断及设定的功能
这边以ProgressBar及progressDrawable属性作为范例
首先需要生成一个callback(CustomViewAttributeApplyListener),这个callback会将当下要置换主题的view、属性及目前取到的资源回传,开发人员收到此回传实再进行相对应的设定

这边比较需要注意点的点是,进行设定要取得资源时必须透过SkinManager中的getDrawable、getColor相关方法取得,如果直接使用当下的conetext取回资源则会取到原始APK的资源,而非主题包

CustomViewAttributeApplyListener listener = (view, fieldName, resId) -> {
    String viewName = view.getClass().getSimpleName();
    switch (viewName){
    case "ProgressBar":
        ((ProgressBar)view.findViewById(R.id.progressBar)).setProgressDrawable(SkinManager.getInstance().getDrawable(resId));
        break;
    }
};

SkinManager.getInstance().addCustomView(new SkinCustomView(ProgressBar.class.getSimpleName(), ProgressBar.class.getName(), new String[]{"progressDrawable"}, listener));

- 最後一个问题

大概是每个专案都一定会遇到的状况,某些UI上效果需要依照API回传的状态呈现
例如状态1文字使用颜色A、背景使用AA;状态2文字使用颜色B、背景使用BB

这个状况再设定时就需要参考问题三
同样是使用SkinManager中的getDrawable、getColor相关方法取得资源

总结

个人认为看完公开课程还算满有收获的,也算是有个现成的可以学习,也讲解得满详细的,後面进阶课销售相关的话真的很多。不过也可以透过他讲述的一些状况了解内地的行情可能也不是坏事

这个套件肯定还有许多需要优化的地方,以及Bug需要修改,有任何建议欢迎提出或加入一起维护


<<:  [Day 28] 系统开发导入(下)

>>:  变更管理和变更控制(Change Management and Change Control)

Day35:HTML(32)响应式网站(2)

响应文字大小 可以使用“ vw”单位设置文本大小,即“视口宽度”。 这样,文本大小将遵循浏览器窗口的...

[ 卡卡 DAY 16 ] - React Native Animated 入门 + useNativeDriver 好棒棒

React Native 提供了 Animated 及 LayoutAnimation API 给...

Day30 Let's ODOO: 总结

回顾 终於来到最後一天,在挑战期间刚好Odoo15也发布了,也有新的Document,期间我们介绍了...

【Day15】[资料结构]-二元搜寻树Binary Search Tree, BST

二元搜寻树(Binary Search Tree),也称有序/排序二元树,是一种特殊二元树结构,而节...

[Android Studio 30天自我挑战] ImageView元件介绍

现在在手机或是平板上都会许多图片的应用,这次介绍ImageView与ImageButton这两个元件...