关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »谈谈 Android Material Design 中的 Tint(着色)

    谈谈 Android Material Design 中的 Tint(着色)

    2015-08-02 00:00:00 出处:掏富小牛
    分享

    什么是Tint

    当我开始接触Tint这个词的时候,其实是蛮不理解它的意思的,以及并不清楚Google发明它的目的,它一般搭配Background配合使用,但是现在已经有了Background,为什么还需要Tint呢?

    Tint 翻译为着色。

    着色,着什么色呢?和背景有关,当然是着背景的色。当我开发客户端,使用了appcompat-v7包的时候,为了实现Material Design的效果,我们会去设置主题里的几个颜色,重要的比如primaryColor,colorControlNormal,colorControlActived等等,而我们使用的一些组件,比如EditText就会自动变成我们想要的背景颜色,在背景图只有一张的情况下,这样的做法极大的减少了我们apk包的大小。

    实现的方式就是用一个颜色为我们的背景图片设置tint(着色)。

    例子:

    谈谈Android Material Design 中的Tint(着色)

    看看即将发布的SegmentFault for Android 2.7中,发布问题功能,这个EditText的颜色和我们的主要颜色相同。它利用了TintManager这个类,为自己的背景进行着色(绿色)。

    那么这个原始图是什么样子呢?我们从appcompat-v7包中找到了这个图,是一个.9图,样子如下:

    谈谈Android Material Design 中的Tint(着色)

    其实它只是一个黑色的条,通过绿色的着色,变成了一个绿色的条。 就是这样的设计方式,使得我们在Material Design中省了多少资源文件呀!

    好了,既然理解了tint的含义,我们赶紧看下这一切是如何实现的吧。

    其实底层特别简单,了解过渲染的同学应该知道PorterDuffColorFilter这个东西,我们使用SRC_IN的方式,对这个Drawable进行颜色方面的渲染,就是在这个Drawable中有像素点的地方,再用我们的过滤器着色一次。

    实际上假如要我们自己实现,只用获取view的backgroundDrawable之后,设置下colorFilter即可。

    看下最核心的代码就这么几行

    javaif (filter == null) {
    	// Cache miss, so create a color filter and add it to the cache
    	filter = new PorterDuffColorFilter(color, mode);
    }
    d.setColorFilter(filter);

    通常情况下,我们的mode一般都是SRC_IN,假如想了解这个属性相关的资料,这里是传送门: http://blog.csdn.net/t12x3456/article/details/10432935 (中文)

    由于API Level 21以前不支持background tint在xml中设置,于是提供了ViewCompat.setBackgroundTintList方法和ViewCompat.setBackgroundTintMode用来手动更改需要着色的颜色,但要求相关的View继承TintableBackgroundView接口。

    源码解析

    看下源码是如何实现的吧,我们以AppCompatEditText为例:

    看下构造函数(省略无关代码)

    javapublic AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    	super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
    	...
    	ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1)); //根据背景的resource id获取内置的着色颜色。
    	if (tint != null) {
    		setInternalBackgroundTint(tint); //设置着色
    	}
    	...
    }
    private void setInternalBackgroundTint(ColorStateList tint) {
    	if (tint != null) {
    		if (mInternalBackgroundTint == null) {
    			mInternalBackgroundTint = new TintInfo();
    		}
    		mInternalBackgroundTint.mTintList = tint;
    		mInternalBackgroundTint.mHasTintList = true;
    	} else {
    		mInternalBackgroundTint = null;
    	}
    	//上面的代码是记录tint相关的信息。
    	applySupportBackgroundTint();  //对背景应用tint
    }
     private void applySupportBackgroundTint() {
    	if (getBackground() != null) {
    		if (mBackgroundTint != null) {
    			TintManager.tintViewBackground(this, mBackgroundTint);
    		} else if (mInternalBackgroundTint != null) {
    			TintManager.tintViewBackground(this, mInternalBackgroundTint); //最重要的,对tint进行应用
    		}
    	}
    }

    然后我们进入tintViewBackground看下TintManager里面的源码

    javapublic static void tintViewBackground(View view, TintInfo tint) {
    	final Drawable background = view.getBackground();
    	if (tint.mHasTintList) {
    		//假如设置了tint的话,对背景设置PorterDuffColorFilter
    		setPorterDuffColorFilter(
    				background,
    				tint.mTintList.getColorForState(view.getDrawableState(),
    						tint.mTintList.getDefaultColor()),
    				tint.mHasTintMode   tint.mTintMode : null);
    	} else {
    		background.clearColorFilter();
    	}
    	if (Build.VERSION.SDK_INT <= 10) {
    		// On Gingerbread, GradientDrawable does not invalidate itself when it's ColorFilter
    		// has changed, so we need to force an invalidation
    		view.invalidate();
    	}
    }
    private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) {
    	if (mode == null) {
    		// If we don't have a blending mode specified, use our default
    		mode = DEFAULT_MODE;
    	}
    	// First, lets see if the cache already contains the color filter
    	PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
    	if (filter == null) {
    		// Cache miss, so create a color filter and add it to the cache
    		filter = new PorterDuffColorFilter(color, mode);
    		COLOR_FILTER_CACHE.put(color, mode, filter);
    	}
    	// 最最重要,原来是对background drawable设置了colorFilter 完成了大家要的功能。
    	d.setColorFilter(filter);
    }

    以上是对API21以下的兼容。

    假如大家要实现自己的AppCompat组件实现tint的一些特性的话,我们就可以指定好ColorStateList,利用TintManager对自己的背景进行着色,当然需要对外开放设置的接口的话,我们还要实现TintableBackgroundView接口,然后用ViewCompat.setBackgroundTintList进行设置,这样能完成对v7以上所有版本的兼容。

    实例

    比如我现在要对一个自定义组件实现对Tint的支持,其实只用继承下,加一些代码就好了,代码如下(几乎通用):

    public class AppCompatFlowLayout extends FlowLayout implements TintableBackgroundView {
    	private static final int[] TINT_ATTRS = {
    			android.R.attr.background
    	};
    	private TintInfo mInternalBackgroundTint;
    	private TintInfo mBackgroundTint;
    	private TintManager mTintManager;
    	public AppCompatFlowLayout(Context context) {
    		this(context, null);
    	}
    	public AppCompatFlowLayout(Context context, AttributeSet attributeSet) {
    		this(context, attributeSet, 0);
    	}
    	public AppCompatFlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
    		super(context, attributeSet, defStyle);
    		if (TintManager.SHOULD_BE_USED) {
    			TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attributeSet,
    					TINT_ATTRS, defStyle, 0);
    			if (a.hasValue(0)) {
    				ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1));
    				if (tint != null) {
    					setInternalBackgroundTint(tint);
    				}
    			}
    			mTintManager = a.getTintManager();
    			a.recycle();
    		}
    	}
    	private void applySupportBackgroundTint() {
    		if (getBackground() != null) {
    			if (mBackgroundTint != null) {
    				TintManager.tintViewBackground(this, mBackgroundTint);
    			} else if (mInternalBackgroundTint != null) {
    				TintManager.tintViewBackground(this, mInternalBackgroundTint);
    			}
    		}
    	}
    	@Override
    	protected void drawableStateChanged() {
    		super.drawableStateChanged();
    		applySupportBackgroundTint();
    	}
    	private void setInternalBackgroundTint(ColorStateList tint) {
    		if (tint != null) {
    			if (mInternalBackgroundTint == null) {
    				mInternalBackgroundTint = new TintInfo();
    			}
    			mInternalBackgroundTint.mTintList = tint;
    			mInternalBackgroundTint.mHasTintList = true;
    		} else {
    			mInternalBackgroundTint = null;
    		}
    		applySupportBackgroundTint();
    	}
    	@Override
    	public void setSupportBackgroundTintList(ColorStateList tint) {
    		if (mBackgroundTint == null) {
    			mBackgroundTint = new TintInfo();
    		}
    		mBackgroundTint.mTintList = tint;
    		mBackgroundTint.mHasTintList = true;
    		applySupportBackgroundTint();
    	}
    	@Nullable
    	@Override
    	public ColorStateList getSupportBackgroundTintList() {
    		return mBackgroundTint != null   mBackgroundTint.mTintList : null;
    	}
    	@Override
    	public void setSupportBackgroundTintMode(PorterDuff.Mode tintMode) {
    		if (mBackgroundTint == null) {
    			mBackgroundTint = new TintInfo();
    		}
    		mBackgroundTint.mTintMode = tintMode;
    		mBackgroundTint.mHasTintMode = true;
    		applySupportBackgroundTint();
    	}
    	@Nullable
    	@Override
    	public PorterDuff.Mode getSupportBackgroundTintMode() {
    		return mBackgroundTint != null   mBackgroundTint.mTintMode : null;
    	}
    }

    赶快去试试吧~

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    SQL汉字转换为拼音的函数

    windows 7系统无法运行Photoshop CS3的解决方法

    巧用MySQL加密函数对Web网站敏感数据进行保护

    MySQL基础知识简介

    Windows7和WinXP下如何实现不输密码自动登录系统的设置方法介绍

    windows 7系统ip地址冲突怎么办?windows 7系统IP地址冲突问题的

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。