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

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

换肤框架使用

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

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

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


    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 控件支持[可选]

直接照搬官网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 {
    @Override
    public 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);
            }
        }
    }

    @Override
    public void applySkin() {
        applyContentScrimResource();
        applyStatusBarScrimResource();
        if (mBackgroundTintHelper != null) {
            mBackgroundTintHelper.applySkin();
        }
    }
}

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

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

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

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值