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 研究的更多相关文章

  1. android学习10——对顶点着器和片段着色器的理解

    图形都是点,线,面组成的.顶点着器指定了顶点的位置,大小和颜色. 看一个顶点着色器的代码 attribute vec4 a_Position; attribute float a_PointSize; ...

  2. Android OpenGL ES 开发(八): OpenGL ES 着色器语言GLSL

    前面的文章主要是整理的Android 官方文档对OpenGL ES支持的介绍.通过之前的文章,我们基本上可以完成的基本的形状的绘制. 这是本人做的整理笔记: https://github.com/re ...

  3. 【Android 应用开发】OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解

    最近开始关注OpenGL ES 2.0 这是真正意义上的理解的第一个3D程序 , 从零开始学习 . 案例下载地址 : http://download.csdn.net/detail/han120201 ...

  4. OpenGL笔记(五) 着色器渲染(以Android为例)

    一.Android平台上下文环境的创建及初始化 1. 首先实例化Android上下文环境,即EGL的初始化. bool EGLCore::init(EGLContext sharedContext) ...

  5. Android OpenGL ES 开发(十): 通过GLES20与着色器交互

    1. 获取着色器程序内成员变量的id(句柄.指针) GLES20.glGetAttribLocation方法:获取着色器程序中,指定为attribute类型变量的id. GLES20.glGetUni ...

  6. 【OPENGL】第三篇 着色器基础(二)

    在这一小节,主要学习GLSL的基本数据类型以及控制结构.GLSL具备了C++和Java的很多特性,我们会先了解所有着色阶段共有的特性,再了解各个着色器的专属特性. 1.着色器的基本结构 一个着色器程序 ...

  7. OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译

    首先声明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. 在Android.iOS等移动平台上 ...

  8. 关于着色器LinearGradient的使用

    LinearGradient我们可以将之译为线型渐变.线型渲染等,译成什么不重要,重要的是它的显示效果是什么样子,今天我们就一起来看看. 先来看看LinearGradient的构造方法: /** Cr ...

  9. 【Unity Shaders】Lighting Models —— 衣服着色器

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

随机推荐

  1. maven本地仓库.m2文件夹路径讲解

    Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Depen ...

  2. 搭建jenkins环境(linux操作系统)

    一.虚拟机安装 1)  Virtualbox安装 2)  新建镜像(将已有镜像导入) 3)   开通本地远程访问虚拟机的权限 3.1 通过本地的mac地址设置本地连接固定的ip地址.子网掩码.默认网关 ...

  3. Redis的Order Set操作

    有序集合 zadd key score1 value1 score2 value2 .. 添加元素 127.0.0.1:6379> zadd class 12 lily 13 lucy 18 l ...

  4. iOS:地图:MapKit和CoreLocation

    地图:MapKit和CoreLocation 简介: 现在很多的社交软件都引入了地图和定位功能,要想实现这2大功能,那就不得不学习其中的2个框架:MaKit和CoreLocation CoreLoca ...

  5. Linux任务前后台的切换

    Shell支持作用控制,有以下命令实现前后台切换: 1. command& 让进程在后台运行 2. jobs 查看后台运行的进程 3. fg %n 让后台运行的进程n到前台来 4. bg %n ...

  6. 设置PL/SQL Developer记住用户名密码

  7. python 调用 C++ code

    本文以实例code讲解python 调用 C++的方法. 1. 如果没有参数传递从python传递至C++,python调用C++的最简单方法是将函数声明为C可用函数,然后作为C code被pytho ...

  8. Linux系统时间与RTC时间【转】

    http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3637782 Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符 ...

  9. 《OD大数据实战》HBase整合MapReduce和Hive

    一.HBase整合MapReduce环境搭建 1. 搭建步骤1)在etc/hadoop目录中创建hbase-site.xml的软连接.在真正的集群环境中的时候,hadoop运行mapreduce会通过 ...

  10. 《OD学hadoop》第一周0626 作业二:Linux基础

    一.打包压缩 知识点: tar -zxvf -C PATH tar -jxvf tar -zcvf tar -jcvf tar:打包命令 -z 打包同时gzip压缩 -j 打包同时bzip2 -c 打 ...