简介
主用减apk体积。如TextView两背景(获焦a、失焦b)。图a、b色外同。UI给两图,适配分辨率则图更多且切换时因重加载bitmap致效率大幅下降。矢量图(SVG)提性能但非绝佳选择。
方案
Tint(apk仅放一图)
使用
两ImageView引同drawable,背景黑。现改其中一ImageView背景为绿。如下:
iv1 = (ImageView) this.findViewById(R.id.iv1);
final Drawable originBitmapDrawable = getResources().getDrawable(R.drawable.ic_account_circle_black_18dp);
iv1.setImageDrawable(tintDrawable(originBitmapDrawable, ColorStateList.valueOf(Color.GREEN)));
问题
改后两背景同绿。
分析
两ImageView引同drawable,系统优化资源把两drawable于内存中拷贝合成一份。如图。改共同变量影响两图。
解决
final Drawable originBitmapDrawable = getResources().getDrawable(R.drawable.
ic_account_circle_black_18dp).mutate();
如此无损谷歌图片内存优化方案。所做仅拿出单独受影响部分内存,没受影响部分仍共享数据。内存另存一些纯标志位类(类似状态值)东西。大部分内存仍公用。
tintDrawable
该方法用来向下兼容。不考虑向下兼容则用系统自带方法即可。
public static Drawable tintDrawable(Drawable drawable, ColorStateList colors) {
final Drawable wrappedDrawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTintList(wrappedDrawable, colors);
return wrappedDrawable;
}
也可如下向下兼容:
public final class TintedBitmapDrawable extends BitmapDrawable {
private int tint;
private int alpha;
public TintedBitmapDrawable(final Resources res, final Bitmap bitmap, final int tint) {
super(res, bitmap);
this.tint = tint;
this.alpha = Color.alpha(tint);
}
public TintedBitmapDrawable(final Resources res, final int resId, final int tint) {
super(res, BitmapFactory.decodeResource(res, resId));
this.tint = tint;
this.alpha = Color.alpha(tint);
}
public void setTint(final int tint) {
this.tint = tint;
this.alpha = Color.alpha(tint);
}
@Override public void draw(final Canvas canvas) {
final Paint paint = getPaint();
if (paint.getColorFilter() == null) {
paint.setColorFilter(new LightingColorFilter(tint, 0));
paint.setAlpha(alpha);
}
super.draw(canvas);
}
}
另例
如下改EditText背景色:
et1 = (EditText) this.findViewById(R.id.et);
final Drawable originBitmapDrawable = et1.getBackground();
et1.setBackgroundDrawable(tintDrawable(originBitmapDrawable, ColorStateList.valueOf(Color.GREEN)));
问题
背景色变而光标色没变。xml属性android:textCursorDrawable="@drawable/xxx"
可改但如此又增资源文件而不符tint。
思路
android无提供api,如一些private函数需反射调。这里反射获cursorDrawable
并着色后反调方法set进去。
解决
EditText实际即TextView。查TextView源码现属性cursorDrawable
:
// Although these fields are specific to editable text, they are not added to Editor because
// they are defined by the TextView's style and are theme-dependent.
int mCursorDrawableRes;
且查Editor源码,同EditText息息相关:
/**
* EditText specific data, created on demand when one of the Editor fields is used.
* See {<a href="http://www.jobbole.com/members/57845349">@link</a> #createEditorIfNeeded()}.
*/
private Editor mEditor;
// 注意该段代码属editor
final Drawable[] mCursorDrawable = new Drawable[2];
反射:
// 参数即反射所改光标edittext对象
private void invokeEditTextCallCursorDrawable(EditText et) {
try {
Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes");
// 查源码知该变量非public,需设可访
fCursorDrawableRes.setAccessible(true);
// 获editext对象中mCursorDrawableRes属性值,查源码知int
int mCursorDrawableRes = fCursorDrawableRes.getInt(et);
// 获mEditor对象后通所获mEditor对象获最终mCursorDrawable之drawable
Field fEditor = TextView.class.getDeclaredField("mEditor");
fEditor.setAccessible(true);
Object editor = fEditor.get(et);
Class<?> clazz = editor.getClass();
Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable");
fCursorDrawable.setAccessible(true);
if (mCursorDrawableRes <= 0) {
return;
}
// 到此获默主题下edittext光标小图标之drawable
Drawable cursorDrawable = et.getContext().getResources().getDrawable(mCursorDrawableRes);
if (cursorDrawable == null) {
return;
}
// 获drawble并改
Drawable tintDrawable = tintDrawable(cursorDrawable, ColorStateList.valueOf(Color.GREEN));
// 前贴mCursorDrawable源码知这是一二维数组,造一全新二维数组
Drawable[] drawables = new Drawable[]{tintDrawable, tintDrawable};
// 通反射把该二维数组值放editor中
fCursorDrawable.set(editor, drawables);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}