Android 换肤方案详解(二)

概述

Android 换肤方案详解(一)主要围绕Android-Skin-Loader讲解了换肤的原理。接下来这篇文章主要分享换肤过程中遇到的问题和心得。

可能会遇到的问题及解决方案

实践中发现该方案仍存在一些局限

  1. 只支持xml静态注册view
  2. 每增加一个换肤属性需要实现一个类
  1. 动态添加view?
    因为以上方案是在onCreate()创建view时自定义了LayoutInflater.Factory,所以我们可以获取onCreate()中创建的view和attr集,但代码中通过addView()方式添加的view没办法保存进去
    设计思路:重写viewGroup的addView()方法,在addView()中将childView注册到换肤集中。为了方便使用流程,又封装了view.setSkinTag(属性1,属性2,…)方法,该方法内部根据view和attr构建换肤实体、调用childview.setTag(tagId, 换肤实体),重写的addView()只需根据childview.getTag()便能将换肤实体注册到换肤集
    封装过程:略
    使用流程:addView()前调用setSkinTag()
    1. 注册背景、文本、文字颜色、图片资源的换肤
    // 注册背景
    view.setSkinTag(bgResId = R.drawable.shape_tab_launcher_g_focused_bg)
    
    1. 注册自定义属性的换肤
    // 注册自定义属性
    view.setSkinTag(TabViewProcess.textColor, R.color.selector_tab_text_color_g, SkinAttr.RES_TYPE_NAME_COLOR)
    
    object TabViewProcess: CustomAttrAction<TabView> {
    
        val textColor = CustomAttrEntity(TabView::class.java.name, attrName = "textColor", this)
    
        override fun apply(t: TabView, attrName: String, resId: Int) {
            if (attrName == "textColor" && resId != 0) {
                t.initColorStateList(resId)
                SkinUtil.getColorStateList(resId)?.let { t.setTextColor(it) }
            }
        }
    }
    
  2. 代码中有修改属性值,例如view获取焦点时改变背景?
    设计思路:封装一个方法转换皮肤资源,在修改属性值时调用该方法
    封装过程:略
    使用流程:view.setImageDrawable(SkinUtil.getDrawable(resID))、…
  3. 代码中修改过属性值,一键换肤时会将注册时的属性值(xml中的属性值或代码动态添加view的属性值)设置给view?
    设计思路:SkinUtil.getDrawable(resID)只是动态修改属性,并未修改换肤集该view的值,故可以封装一行代码修改换肤集该view的属性值
  4. 自定义view中自定义的attr怎么换肤?
    每增加一个属性需要实现一个类,对于常规控件还好,但自定义view属性值一多,除了修改lib代码外(耦合),还需实现很多类(繁杂),不便管理
    设计思路:通过注册方式自行管理view下所有属性的换肤功能,且不影响view的代码(解耦)
    封装过程:略
    使用流程:重写registerCustomAttr()
    // 对于自定义view---CircleTextImage,换肤需要改变三个属性,只需三行代码注册
    override fun registerCustomAttr() {
        add(CircleTextImageProcess.textColor)
        add(CircleTextImageProcess.circleColor)
        add(CircleTextImageProcess.strokeColor)
    }
    
    // 对于自定义view---CircleTextImage,换肤所有事项自行处理
    object CircleTextImageProcess: CustomAttrAction<CircleTextImage> {
    
        val textColor = CustomAttrEntity(CircleTextImage::class.java.name, attrName = "textColor", this)
    
        val circleColor = CustomAttrEntity(CircleTextImage::class.java.name, attrName = "circleColor", this)
    
        val strokeColor = CustomAttrEntity(CircleTextImage::class.java.name, attrName = "strokeColor", this)
    
        override fun apply(t: CircleTextImage, attrName: String, resId: Int) {
            if (attrName == "textColor" && resId != 0) {
                t.setTextColor(SkinUtil.getColor(resId))
            } else if (attrName == "circleColor" && resId != 0) {
                t.setCircleColor(SkinUtil.getColor(resId))
            } else if (attrName == "strokeColor" && resId != 0) {
                t.setStrokeColor(SkinUtil.getColor(resId))
            }
        }
    }
    

优化项

资源包瘦身

因为资源包实际上就是一个应用,所以可以参考应用瘦身方案,此外可根据实际继续瘦身
资源包只需要拥有res下换肤R文件即可,不一定需要跑起来,所以应删尽删
参考我之前的文章-Android应用瘦身

实践:前15M+,后207K

  1. 开启混淆、资源压缩(普通应用瘦身手段)
    不开混淆可能会导入android自带的一些库,例如下图classes.dex、classes2.dex、classes3.dex、classes4.dex
    在这里插入图片描述
  2. AndroidManifest.xml尽量简洁(皮肤包瘦身手段)
    去除icon、banner、lable、theme这些信息,同时清除icon、banner、lable、theme引用的res文件。尤其是theme,占用空间挺大的,只保留以下几行即可
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.xxx.xxx">
        <application
            android:allowBackup="true"
            android:supportsRtl="true" />
    </manifest>
    
  3. 将dependencies节点下所有引用删除(皮肤包瘦身手段)
    // 下面代码都可以删除
    dependencies {
        implementation 'androidx.core:core-ktx:1.7.0'
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.5.0'
        testImplementation 'junit:junit:4.+'
        androidTestImplementation 'androidx.test.ext:junit:1.1.3'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    }
    

资源规范

  1. 整理资源时,某view引用R.drawable.selector_xxx
    • R.drawable.selector_xxx中引用了R.drawable.xxx,需修改R.drawable.xxx
    • R.drawable.selector_xxx中引用了R.color.xxx,需修改R. color.xxx
      总之修改最后一层引用的资源文件即可
  2. 多个view引用同一个R文件(例如R.color.xxx),需要实现Aview换肤,Bview无变化,这时需要增加R文件以示区分(例如R.color.xxx_skin)
  3. 删除无关换肤的R文件

皮肤包制作流程

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值