1. 一个Icon图标支持两种配色
1.1 通过已有的图片处理成变色图片
方案就是作图的时候控制显示内容的透明度,
第一步:
Bitmap mColorBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(mColorBitmap);
Paint mPaint = new Paint();
mPaint.setColor(mColor);
第二步:
//从原位图中提取只包含alpha的位图
Bitmap alphaBitmap = mBitmap.extractAlpha();
//在画布上通过透明度的部分控制显示颜色
mCanvas.drawBitmap(alphaBitmap, 0, 0, mPaint);
ps:使用把图片做成字体,这样可以直接setTextColor
其实这和第二种的原理是相同的,只不过是UI和Android系统帮我们做了
1.2 ImageBitmap 和倍率的关系
如果不设置Density 直接把一个bitmap 设置到ImageVIew上去 就是默认density。
但是可能图片都是三倍图,这时候如果设置到2倍分辩率的手机上就会出现缩小的情况,这时候就要设置图片的density。
bitmap.setDensity(480);
然后再设置到imageView上去
关于图片的缩放系数:http://jayfeng.com/2016/03/22/Android-Bitmap%E9%9D%A2%E9%9D%A2%E8%A7%82/
2.一个轻量级的换肤方案
https://github.com/hongyangAndroid/AndroidChangeSkin
原理是定义可替换的皮肤时候带上一个后缀
<color name="bg_theme">#07172d</color>
<color name="bg_theme_light">@color/color_eeeeee</color>
对于自定义drawble和图片资源也是一样
discover_icon.png
discover_icon_light.png
使用时候:
<ImageView
android:drawableTop="@drawable/discover_icon"
android:tag="skin:drawableTop:drawableTop|skin:tab_color:textColor"
/>
原理每次换肤时候遍历所有的activity,找出所有有skin标签的控件
private void notifyChangedListeners() {
long time = System.currentTimeMillis();
LinkedList<Activity> activityStack = LycApplication.getInstance().getActivityStack();
for (int i = 0, size = activityStack.size(); i < size; i++) {
if (null != activityStack.get(i)) {
Activity act= activityStack.get(i);
if (act instanceof StoryGroundActivity || act instanceof ReviewActivity
|| act instanceof CommentActivity) {
apply(activityStack.get(i));
}
}
}
ELog.w(" Notify ALL SKIN COST: ", (System.currentTimeMillis() - time));
}
找每个元素的过程:
1.递归遍历所有的子元素
public static SkinView getSkinView(View view) {
Object tag = view.getTag(R.id.skin_tag_id);
if (tag == null) {
tag = view.getTag();
}
if (tag == null)
return null;
if (!(tag instanceof String))
return null;
String tagStr = (String) tag;
List<SkinAttr> skinAttrs = parseTag(tagStr);
if (!skinAttrs.isEmpty()) {
changeViewTag(view);
return new SkinView(view, skinAttrs);
}
return null;
}
2.解析出每个SkinView里面的自定义主题 SkinAttr
private static List<SkinAttr> parseTag(String tagStr) {
List<SkinAttr> skinAttrs = new ArrayList<SkinAttr>();
if (TextUtils.isEmpty(tagStr)) return skinAttrs;
String[] items = tagStr.split("[|]");
for (String item : items) {
if (!item.startsWith(SkinConfig.SKIN_PREFIX))
continue;
String[] resItems = item.split(":");
if (resItems.length != 3)
continue;
String resName = resItems[1];
String resType = resItems[2];
SkinAttrType attrType = getSupprotAttrType(resType);
if (attrType == null) continue;
SkinAttr attr = new SkinAttr(attrType, resName);
skinAttrs.add(attr);
}
return skinAttrs;
}
3.应用新的主题
SkinView apply主题的方法:
public void apply() {
if (view == null) return;
for (SkinAttr attr : attrs) {
attr.apply(view);
}
}
SkinAttr 具体设置主题的方法
HintCOLOR("textColorHint") {
@Override
public void apply(View view, String resName) {
ColorStateList colorlist = getResourceManager().getColorStateList(resName);
if (colorlist == null)
return;
((EditText) view).setHintTextColor(colorlist);
}
},
ps: SkinAttrType写的方式很不错,值得学习
踩坑笔录
- 区别drawable 是color 还是 drawable:
BACKGROUND("background") {
@Override
public void apply(View view, String resName) {
String targetResName = getResourceManager().appendSuffix(resName);
Resources mResources=view.getContext().getResources();
try {
int colorResult= mResources.getColor(mResources.getIdentifier(targetResName, "color", view.getContext().getPackageName()));
view.setBackgroundColor(colorResult);
}catch ( Exception e1){
try {
int resId=mResources.getIdentifier(targetResName, "drawable", view.getContext().getPackageName());
Drawable drawableResult= mResources.getDrawable(resId);
if (drawableResult == null)
return;
view.setBackgroundResource(resId);
}catch (Exception e2){
ELog.e("not found:"+targetResName);
}
}
}
},
- SkinManager 没有保存SkinView的引用
实际上每次应用主题时候 都是遍历存在Application的Activity列表然后find 每个SkinView 然后应用主题皮肤