换肤框架Android-Skin-Support问题记录

换肤框架Android-Skin-Support问题记录

    • 换肤框架Android-Skin-Support问题记录
  • 换肤框架使用
  • 问题 1:其他控件换肤正常ImageView无法正常换肤
    • 解决方案
  • 问题2,自定义控件适配换肤
  • 问题3,RadioButton使用drawableTop,drawableLeft,drawableRight,drawableBottom等方法加载图片资源无法换肤

换肤框架使用

最近自己玩手机的时候(自己用的是苹果,系统会自动跟随时间切换白天和深夜模式),突发奇想想实现一下主题切换的功能,于是git上找了一个stars 最多的项目 Android-skin-support(这是gitcode地址),Android-skin-support(gitHub地址)
使用方法主页内说明已经十分详细了,这里就不赘述了。接下来进入主题我遇到的问题,以及解决方案。

问题 1:其他控件换肤正常ImageView无法正常换肤

我自己的工程是AndroidX的工程,使用依赖如下:

api 'skin.support:skin-support:4.0.5'                // skin-supportapi 'skin.support:skin-support-appcompat:4.0.5'       // skin-support 基础控件支持api 'skin.support:skin-support-design:4.0.5'            // skin-support-design material design 控件支持[可选]api 'skin.support:skin-support-cardview:4.0.5'          // skin-support-cardview CardView 控件支持[可选]api 'skin.support:skin-support-constraint-layout:4.0.5' // skin-support-constraint-layout ConstraintLayout 控件支持[可选]

直接照搬官网demo的MineFragment快速应用换肤的功能
界面如图:

将依赖和对应的资源包“res-black,res-blue,res-green”等资源包拷贝到我的工程里,然后将对应需要切换皮肤的控件例如TextView,Button,ImageView的颜色值改成@color/colorPrimary具体参考主页说明和demo。
然后运行起来试了一下,我发现我的TextView,View,RelativeLayout等控件可以正常换肤,但是ImagView死活没反应一直是紫色(默认的颜色就是紫色)
然后我开始找问题,google,百度找了一下issues都没有找到解决方案,那求人不如求己只能自力更生了。

  1. 先是再度确认依赖是否正确,发现跟demo一模一样。
  2. 跑了一遍demo,关键位置处Debug了一下看了一下运行流程
  3. 又跑了一遍自己的工程,关键位置处Debug了一下看了一下运行流程
    然后我发现问题了 他喵的问题居然是源码的问题:在ImageView的替代类SkinCompatImageView(因为该框架原理是在view绑定时将对应的view控件替换成自己的换肤控件,实时去加载例如背景颜色,图片等皮肤资源)
    对比如图
    这是依赖拉下来的代码包

    这是源码:

    发现问题没,依赖下来的代码包里没有mSrcTintResId这个成员变量。我看了一下源码这个变量是根据Xml文件中控件定义时的字段tint去获取对图片进行着色的获取资源

    上图这里判断是否有tint资源有的话获取到资源id,然后在applySkin()里判断了一下这个字段是否是正常的资源id,如果有mSrcTintResId且不是无效id 然后调用了框架自己封装的方法SkinCompatResources.getColorStateList去获取当前皮肤包的皮肤资源。(这个方法就是根据你当前选择的皮肤名称去加载对应的皮肤包里的资源)
    然后拿到颜色之后调用了控件提供的方法ImageViewCompat.setImageTintList(mView, tintList);去对图片进行着色。

    ok找到问题,应该是这个仓库版本发版的问题,那么问题知道了,如何解决呢

解决方案

我这里因为自己有个性化的View,需要改源码里的东西,所以我直接一步到位源码依赖进我的工程里。
直接将整个源码里整个androidx的目录复制到我的工程里。可以参考该框架源码(源码也是本地依赖的,应该是也有这个问题)
目录如图

build.gradle依赖如下:

//    api 'skin.support:skin-support:4.0.5'                // skin-support
//    api 'skin.support:skin-support-appcompat:4.0.5'       // skin-support 基础控件支持
//    api 'skin.support:skin-support-design:4.0.5'            // skin-support-design material design 控件支持[可选]api 'skin.support:skin-support-cardview:4.0.5'          // skin-support-cardview CardView 控件支持[可选]api 'skin.support:skin-support-constraint-layout:4.0.5' // skin-support-constraint-layout ConstraintLayout 控件支持[可选]implementation project(':androidx:skin-support')implementation project(':androidx:skin-support-appcompat')implementation project(':androidx:skin-support-design')

然后运行ok正常换肤,问题解决。

问题2,自定义控件适配换肤

我工程里自定义了一个顶部导航栏

然后原本是继承自Toolbar实现的,但是始终无法换肤,然后查看源码发现
SkinMaterialViewInflater用于把原有控件动态替换成实现换肤功能的控件的类,

这里的实现方案是根据包名+类名来进行替换的,所以我自己个性化的View虽然是继承的但是这里找不到对应的名称也就无法替换。
为了方便继承也能直接使用,有没有什么办法,判断一个类是否是某个类的子类呢?当然有:

 parentClass.isAssignableFrom(childClass)

于是我新建了个SkinMaterialViewInflaterNew继承自原来的SkinMaterialViewInflater ,在default语句里加入了我自己个性化的代码;
代码如下:

public class SkinMaterialViewInflaterNew extends SkinMaterialViewInflater {@Overridepublic View createView(@NonNull Context context, final String name, @NonNull AttributeSet attrs) {if ("androidx.coordinatorlayout.widget.CoordinatorLayout".equals(name)) {return new SkinMaterialCoordinatorLayout(context, attrs);}
//        if (!name.startsWith("com.google.android.material.")) {//            return null;
//        }View view = null;Class parentClass = null;switch (name) {case "com.google.android.material.appbar.AppBarLayout":view = new SkinMaterialAppBarLayout(context, attrs);break;case "com.google.android.material.tabs.TabLayout":view = new SkinMaterialTabLayout(context, attrs);break;case "com.google.android.material.textfield.TextInputLayout":view = new SkinMaterialTextInputLayout(context, attrs);break;case "com.google.android.material.textfield.TextInputEditText":view = new SkinMaterialTextInputEditText(context, attrs);break;case "com.google.android.material.navigation.NavigationView":view = new SkinMaterialNavigationView(context, attrs);break;case "com.google.android.material.floatingactionbutton.FloatingActionButton":view = new SkinMaterialFloatingActionButton(context, attrs);break;case "com.google.android.material.bottomnavigation.BottomNavigationView":view = new SkinMaterialBottomNavigationView(context, attrs);break;case "com.google.android.material.appbar.CollapsingToolbarLayout":view = new SkinMaterialCollapsingToolbarLayout(context, attrs);break;default:try {parentClass = Class.forName(name);if (Class.forName("com.google.android.material.appbar.CollapsingToolbarLayout").isAssignableFrom(parentClass)) {view = new SkinMaterialMergerStatus(context, attrs);} else {return null;}} catch (ClassNotFoundException e) {e.printStackTrace();}break;}return view;}
}

然后在Application里将原来的替换成自己新建的类

        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)SkinCompatManager.withoutActivity(this).addInflater(SkinAppCompatViewInflater())// 基础控件换肤初始化
//            .addInflater(SkinMaterialViewInflater()) // material design 控件换肤初始化[可选].addInflater(SkinMaterialViewInflaterNew()) // material design 控件换肤初始化[可选].addInflater(SkinConstraintViewInflater()) // ConstraintLayout 控件换肤初始化[可选].addInflater(SkinCardViewInflater()) // CardView v7 控件换肤初始化[可选].setSkinStatusBarColorEnable(false) // 关闭状态栏换肤,默认打开[可选].setSkinWindowBackgroundEnable(false) // 关闭windowBackground换肤,默认打开[可选].loadSkin()

需要支持换肤功能还需要自己实现换肤控件,这个控件需要继承自你个性化的View这里就是我个性化的MergerStatus如图:

public class SkinMaterialMergerStatus extends MergerStatus implements SkinCompatSupportable {private int mContentScrimResId = INVALID_ID;private int mStatusBarScrimResId = INVALID_ID;private SkinCompatBackgroundHelper mBackgroundTintHelper;public SkinMaterialMergerStatus(Context context) {this(context, null);}public SkinMaterialMergerStatus(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SkinMaterialMergerStatus(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.CollapsingToolbarLayout, defStyleAttr,R.style.Widget_Design_CollapsingToolbar);mContentScrimResId = a.getResourceId(R.styleable.CollapsingToolbarLayout_contentScrim, INVALID_ID);mStatusBarScrimResId = a.getResourceId(R.styleable.CollapsingToolbarLayout_statusBarScrim, INVALID_ID);a.recycle();applyContentScrimResource();applyStatusBarScrimResource();mBackgroundTintHelper = new SkinCompatBackgroundHelper(this);mBackgroundTintHelper.loadFromAttributes(attrs, 0);}private void applyStatusBarScrimResource() {mStatusBarScrimResId = SkinCompatHelper.checkResourceId(mStatusBarScrimResId);if (mStatusBarScrimResId != INVALID_ID) {Drawable drawable = SkinCompatVectorResources.getDrawableCompat(getContext(), mStatusBarScrimResId);if (drawable != null) {setStatusBarScrim(drawable);}}}private void applyContentScrimResource() {mContentScrimResId = SkinCompatHelper.checkResourceId(mContentScrimResId);if (mContentScrimResId != INVALID_ID) {Drawable drawable = SkinCompatVectorResources.getDrawableCompat(getContext(), mContentScrimResId);if (drawable != null) {setContentScrim(drawable);}}}@Overridepublic void applySkin() {applyContentScrimResource();applyStatusBarScrimResource();if (mBackgroundTintHelper != null) {mBackgroundTintHelper.applySkin();}}
}

原本我这里MergerStatus 是继承自ToolBar的但是左边始终有一个padding,看起来很不舒服,于是我这里改成继承自CollapsingToolbarLayout。然后就正常了。

问题3,RadioButton使用drawableTop,drawableLeft,drawableRight,drawableBottom等方法加载图片资源无法换肤

这个问题其实和问题一类似,留待大家自己思考一下。如果想偷懒不想看源码可以看我下一篇吧这一篇貌似篇幅有点长了。

换肤框架Android-Skin-Support问题记录相关推荐

  1. Android-skin-support 一款用心去做的Android 换肤框架

    介绍 Github地址: https://github.com/ximsfei/Android-skin-support Android-skin-support: 一款用心去做的Android 换肤 ...

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

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

  3. Leakcanary原理解析以及换肤框架skin的原理分析

    一.错误现场 java.lang.ClassCastException: androidx.appcompat.widget.TintContextWrapper cannot be cast to ...

  4. android的资源混淆和压缩工具,换肤框架

    介绍一款可以对android资源进行压缩的工具,超好用(已经在项目中使用到) https://github.com/shwenzhang/AndResGuard Android-skin-suppor ...

  5. Android换肤功能实现与换肤框架QSkinLoader使用方式介绍

    框架地址:https://github.com/qqliu10u/QSkinLoader 效果图 https://github.com/qqliu10u/QSkinLoader/raw/master/ ...

  6. android 换肤框架搭建及使用 (3 完结篇)

    本系列计划3篇: Android 换肤之资源(Resources)加载(一) setContentView() / LayoutInflater源码分析(二) 换肤框架搭建(三) - 本篇 tips: ...

  7. android方便简单的零侵入可扩展的换肤框架

    目前的插件化正如火如荼,插件化开源的也不少,比如360开源的Replugin,滴滴的VirtualApk等等,当然我们今天的主题并不是插件化,而是插件化换肤: android的换肤功能的实现基本有两种 ...

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

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

  9. android 换肤框架

    使用插件化换肤 大家可以先去网易云客户端体验一下个性换肤. 可能大家会疑惑,为什么换个皮肤,还用上了插件化,给大家解释一下. 网易云的个性换肤是可以下载皮肤然后再切换的,那么下载皮肤其实就是在一个皮肤 ...

最新文章

  1. 实例规格 ECS (共享计算型)和 (通用型-原独享)性能上有什么区别?...
  2. 科普| 3D视觉技术正在改变我们的生活
  3. 计算机科学与虚拟技术,虚拟现实与可视化技术
  4. android手势操作滑动效果触摸屏事件处理
  5. Android自定义顶部栏及侧滑菜单和fragment+viewpag滑动切换的实现
  6. 前端学习(693):for循环案例之求出偶数奇数之和
  7. 美国会不会禁用python_美国要是禁用Windows系统,该怎么办?国人会不会选择换系统呢?...
  8. 大学计算机张青答案,《大学计算机Ⅰ》实验报告实验一1
  9. java画方块_[求助]用Swing就画一个方块代码出错了
  10. java 调度器怎么调用_OpenSymphony的Quartz里的作业、调度器使用举例
  11. jvm讲解-jvm内存结构详解
  12. PS放大图片而不模糊的操作方法
  13. 一些模板(按字典序排序)
  14. 中金环境携手海通安恒,启动SAP二期项目
  15. 长角牛网络监听 arp欺骗
  16. html table 固定表头和列
  17. 新疆自治区谷歌地球高程DEM等高线下载
  18. 初识TradingView脚本语言PineScrpt 5
  19. 路上铺个“补丁”,智能汽车高速途中瞬间失控!应用最广自动驾驶技术被曝漏洞 | 字节跳动参与的新研究...
  20. 服务器电源模块型号,服务器电源管理模块

热门文章

  1. GitLab 禁用 Windows!
  2. 太牛了!知识图谱原理与实战
  3. python知识图谱问答系统代码_如何搭建一个基于知识图谱的问答系统(以医疗行业为例)...
  4. Windows11更新
  5. D19.1.0 对gluLookAt,gluPerspective和glOrtho的理解
  6. android studio checkstyle插件,Android Studio配置CheckStyle
  7. 夏爱婴早教中心经营问题
  8. java计算机毕业设计汽车金融人事管理源码+程序+lw文档+mysql数据库
  9. mPEG-AAN-NHS 甲氧基聚乙二醇-AAN-N-羟基琥珀酰亚胺 mPEG5K-AAN-NHS
  10. 【金阳光测试独家播出】-IOS专项测试体系预告-7.20号更新第一集