Android 着色器 Tint 研究
Tint 这个东西 主要用来减少apk体积的,比如说我现在有一个textview,他的背景图 有两种,一种是当获得焦点时显示的a图,另一种是 失去焦点时显示的b图。
相信大家开发的时候 这种需求做过很多次了,我们一般都会发现 这种a图和b图 除了颜色不一样,其他都是一样的,但是我们做的时候呢,通常是找ui要了两张图。
如果要适配分辨率的话 很有可能图片会更多,而且在切换的时候 因为是重新加载一次bitmap 效率也会下降很多。所以谷歌就给了一套解决方案 这个就是tint了。
他的目的就是当你发现有这种需求的时候,只需要 放一张图 在apk里即可,当你需要改变背景图的颜色的时候 就用Tint即可!
下面就来简单说一下,tint的使用 以及需要注意的地方。
首先 我们定义一个简单的布局文件:

我们发现这2个imageview 都是引用的同样一个drawable资源,并且 在studio这个xml编辑界面里面 我们很明显的 能看出来 这个图片的颜色是黑色的 对吧!
那 现在 我们想改一下,想把iv1 这个imageview的 背景色 改成绿色的! 我们想当然的 当然会这么写:
iv1 = (ImageView) this.findViewById(R.id.iv1);
iv2 = (ImageView) this.findViewById(R.id.iv2); final Drawable originBitmapDrawable = getResources().getDrawable(R.drawable.ic_account_circle_black_18dp);
iv1.setImageDrawable(tintDrawable(originBitmapDrawable, ColorStateList.valueOf(Color.GREEN)));
应该很好理解对吧,代码就不解释了。但是我们运行以后发现:

卧槽 怎么2个都变绿色了!
回顾一下 我们的代码 我们应该能明白 2个imageview 都是引用的同样的一个drawable,要知道 既然是一个drawable,那系统肯定为了优化资源 把这2个drawable 在内存里的拷贝弄成了一份!
还记得 我们以前讲的bitmap优化那篇么?http://www.cnblogs.com/punkisnotdead/p/4881771.html 和这个里面的inBitmap 属性有异曲同工之妙,如果还不理解 你看下面的图就理解了:

所以才会造成上面的情况。你修改了共同变量,所以2个图就都被影响了。
解决方法 其实也很简单:
final Drawable originBitmapDrawable = getResources().getDrawable(R.drawable.ic_account_circle_black_18dp).mutate();
修改以后 我们再看:

你看这么做就一切正常了。
那有人就要问了,卧槽 你这么做 不是把谷歌给我们做好的图片内存优化方案给损坏了么,其实这种担心是多余的,这个http://android-developers.blogspot.hk/2009/05/drawable-mutations.html
这个地址会告诉你 其实我们做 只是把单独的受到影响的那部分 内存给单独拿出来了,其他没受到影响的还是共享的数据!换句话说 我们内存里 会另外存放的就是一些纯的标志位 之类的 类似于状态值这种东西。
大部分的内存还是公用的!
然后接着来,我们看下一个例子 关于editext的。

你看这个edittext 的颜色是这样的。那现在我们来修改一下 这个edittext的背景色
et1 = (EditText) this.findViewById(R.id.et);
final Drawable originBitmapDrawable = et1.getBackground();
et1.setBackgroundDrawable(tintDrawable(originBitmapDrawable, ColorStateList.valueOf(Color.GREEN)));

背景色是修改成功了 但是这个光标的颜色 还没变 非常不协调, 有人又要说了 我们可以用:

这个xml 属性来修改呀,当然了这个方法确实是可以的 但是你想 你这么做的话 又要增加资源文件了,不是与我们的tint 背道而驰了么?
所以 这个地方 我们就要想办法 突破一下。其实很多人都能想到方法了,对于android 没有 提供给我们的api 比如那些private 函数,
我们通常都是通过反射的方法 去调用的。 这里也是一样,稍微想一下 我们就能明白, 这个地方 我们就先通过反射来获取到这个cursorDrawable
然后给他着色,然后在反射调用方法 给他set进去不就行了么?
首先我们都知道 editext 实际上就是textview,所以我们看一下textview 的源码 找找看 这个属性到底叫啥名字。经过一番努力发现 在这:
// 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 {@link #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();
} }
最后调用这个方法以后看一下效果:

很完美 对吧~~
最后tintDrawable这个方法是用来向下兼容用的。你如果不考虑向下兼容的问题 用系统自带的方法 就可以了,这里就不做过多介绍了。
public static Drawable tintDrawable(Drawable drawable, ColorStateList colors) {
final Drawable wrappedDrawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTintList(wrappedDrawable, colors);
return wrappedDrawable;
}
当然你也可以用http://andraskindler.com/blog/2015/tinting_drawables/ 这个文章里的方法来做向下兼容:
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);
}
}
Android 着色器 Tint 研究的更多相关文章
- android学习10——对顶点着器和片段着色器的理解
图形都是点,线,面组成的.顶点着器指定了顶点的位置,大小和颜色. 看一个顶点着色器的代码 attribute vec4 a_Position; attribute float a_PointSize; ...
- Android OpenGL ES 开发(八): OpenGL ES 着色器语言GLSL
前面的文章主要是整理的Android 官方文档对OpenGL ES支持的介绍.通过之前的文章,我们基本上可以完成的基本的形状的绘制. 这是本人做的整理笔记: https://github.com/re ...
- 【Android 应用开发】OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解
最近开始关注OpenGL ES 2.0 这是真正意义上的理解的第一个3D程序 , 从零开始学习 . 案例下载地址 : http://download.csdn.net/detail/han120201 ...
- OpenGL笔记(五) 着色器渲染(以Android为例)
一.Android平台上下文环境的创建及初始化 1. 首先实例化Android上下文环境,即EGL的初始化. bool EGLCore::init(EGLContext sharedContext) ...
- Android OpenGL ES 开发(十): 通过GLES20与着色器交互
1. 获取着色器程序内成员变量的id(句柄.指针) GLES20.glGetAttribLocation方法:获取着色器程序中,指定为attribute类型变量的id. GLES20.glGetUni ...
- 【OPENGL】第三篇 着色器基础(二)
在这一小节,主要学习GLSL的基本数据类型以及控制结构.GLSL具备了C++和Java的很多特性,我们会先了解所有着色阶段共有的特性,再了解各个着色器的专属特性. 1.着色器的基本结构 一个着色器程序 ...
- OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译
首先声明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. 在Android.iOS等移动平台上 ...
- 关于着色器LinearGradient的使用
LinearGradient我们可以将之译为线型渐变.线型渲染等,译成什么不重要,重要的是它的显示效果是什么样子,今天我们就一起来看看. 先来看看LinearGradient的构造方法: /** Cr ...
- 【Unity Shaders】Lighting Models —— 衣服着色器
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
随机推荐
- JAVA类型信息——Class对象
JAVA类型信息——Class对象 一.RTTI概要 1.类型信息RTTI :即对象和类的信息,例如类的名字.继承的基类.实现的接口等. 2.类型信息的作用:程序员可以在程序运行时发现和使用类型信息. ...
- 字符模型和Windows等价程序
二者很明显的区别,dos和gui 字符模式模型 #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]){ print ...
- Android核心分析 之九Zygote Service
Zygote Service 在本章我们会接触到这两个单词: Zygote [生物] 受精卵, 接合子, 接合体 Spawn:产卵 通过这两个单词,我们就可以大体知道Zygote是干什么的了,就是叫老 ...
- My SQL InnoDB 1217 - Cannot delete or update a parent row:aforeign key constraint fals
InnoDB 允许有外键 MyISAM 不允许有外键 InnoDB修改成MyISAM 证明有外键 一张表如果有其他表的外键关联的是它 它也不能是MyISAM 来自为知笔记(Wiz)
- QT 多线程程序设计
参考:http://www.cnblogs.com/hicjiajia/archive/2011/02/03/1948943.html http://mobile.51cto.com/symbian- ...
- JAVA字符串格式化String.format()的使用
JAVA字符串格式化-String.format()的使用常规类型的格式化 String类的format()方法用于创建格式化的字符串以及连接多个字符串对象.熟悉C语言的同学应该记得C语言的sprin ...
- Java:多态性
Java的多态性:发送消息给某个对象,让该对象自行决定响应何种行为. 通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用. java的多态性要满足三个条件: 1.继承关系 2.在子类重写父类的 ...
- myeclipse 2016 激活,myeclipse 2016 激活
myeclipse 2016 激活: 找了好久,myeclipse 2016 终于激活了.myeclipse版本是下载的 myeclipse-2016-ci-0-offline-installer- ...
- USACO Section 3.1: Score Inflation
完全背包问题 /* ID: yingzho1 LANG: C++ TASK: inflate */ #include <iostream> #include <fstream> ...
- C# GUID的使用
GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.通常平台会提供生成GUID的API.生成算法很有意思,用到了以太网卡地址.纳秒级时间.芯片ID码和许多可 ...