最近自己玩手机的时候(自己用的是苹果,系统会自动跟随时间切换白天和深夜模式),突发奇想想实现一下主题切换的功能,于是git上找了一个stars 最多的项目 Android-skin-support(这是gitcode地址),Android-skin-support(gitHub地址)
使用方法主页内说明已经十分详细了,这里就不赘述了。接下来进入主题我遇到的问题,以及解决方案。
我自己的工程是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都没有找到解决方案,那求人不如求己只能自力更生了。
我这里因为自己有个性化的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正常换肤,问题解决。
我工程里自定义了一个顶部导航栏
然后原本是继承自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。然后就正常了。
这个问题其实和问题一类似,留待大家自己思考一下。如果想偷懒不想看源码可以看我下一篇吧这一篇貌似篇幅有点长了。