前阵子看了bilibili上的一些技术相关影片,码牛学院的公开课程
Android动态加载技术的高级进阶,手写实现网易云主题换肤框架
影片中的讲者用Java初步实作了一个修改主题的框架
学习的过程动作做是个满重要的阶段
因此我把影片中的这个框架跟着教学手刻一次
刻完套用在我自己的测试专案上时发现还有些问题
接着就依照遇到的问题进行进一步的修改
在教学影片中看得到的逻辑规则,本篇文章就不赘述,我会直接说明我基於教学的框架增加或修改的部分
不想浪费时间看文章可以直接看我的Repository ChangeThemeSample
例如这样统一设定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需要修改,有任何建议欢迎提出或加入一起维护
>>: 变更管理和变更控制(Change Management and Change Control)
响应文字大小 可以使用“ vw”单位设置文本大小,即“视口宽度”。 这样,文本大小将遵循浏览器窗口的...
React Native 提供了 Animated 及 LayoutAnimation API 给...
回顾 终於来到最後一天,在挑战期间刚好Odoo15也发布了,也有新的Document,期间我们介绍了...
二元搜寻树(Binary Search Tree),也称有序/排序二元树,是一种特殊二元树结构,而节...
现在在手机或是平板上都会许多图片的应用,这次介绍ImageView与ImageButton这两个元件...