使用装饰器模式动态设置Drawable的ColorFilter
模式 设置 动态 装饰 Drawable 使用
2023-09-27 14:27:01 时间
使用装饰器模式动态设置Drawable的ColorFilter
欢迎各位关注我的新浪微博:微博
转载请标明出处(kifile的博客)
非常多时候我们都希望Android控件点击的时候,有按下效果,选中时有选中效果。通常我们都是通过使用selector来生成一个StateListDrawable来实现。
但是这样我们会面临一个问题,假设使用selector的xml文件生成。那么对于不同的状态,我们就会须要不同的图片,才可以实现drawable的动态改变。
但是有时候,我们的按下状态同普通状态之间唯一的差别仅仅是颜色的差异。
那么这个时候,我们真的有必要在resources中放入多个颜色不同的图片吗?
也许非常多人不会太在意几张图片的空间消耗,但是有时候,放着放着,包体就变大了。为了减小包体,我们真的有必要仅仅放置一张图片,然后设置他在不同状态下的色值。
首先附上我写的一个Drawable装饰器:
package com.kifile.android.drawable;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.StateSet;
/**
* 依据当前状态选择色值过滤器的Drawable.
* <p/>
* 使用{@link #addState(int[], int)} 加入色值.
*
* @author kifile
*/
public class ColorFilterStateListDrawable extends Drawable implements Drawable.Callback {
private Drawable mDrawable;
private StateListState mStateSets;
private int[] mCurrentState;
public ColorFilterStateListDrawable(Drawable drawable) {
if (drawable == null) {
throw new IllegalArgumentException("drawable cannot be null.");
}
mStateSets = new StateListState();
mDrawable = drawable;
mDrawable.setCallback(this);
}
public void addState(int[] stateSet, int color) {
mStateSets.addStateSet(stateSet, color);
}
@Override
public void draw(Canvas canvas) {
ColorFilter filter = selectColorFilter();
if (filter != null) {
mDrawable.setColorFilter(filter);
}
mDrawable.draw(canvas);
}
private ColorFilter selectColorFilter() {
if (mCurrentState == null) {
return null;
}
int index = mStateSets.indexOfStateSet(mCurrentState);
int color = mStateSets.getColor(index);
return new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
@Override
public boolean getPadding(Rect padding) {
return mDrawable.getPadding(padding);
}
@Override
public int getIntrinsicHeight() {
return mDrawable.getIntrinsicHeight();
}
@Override
public int getIntrinsicWidth() {
return mDrawable.getIntrinsicWidth();
}
@Override
public int getMinimumHeight() {
return mDrawable.getMinimumHeight();
}
@Override
public int getMinimumWidth() {
return mDrawable.getMinimumWidth();
}
@Override
public int getChangingConfigurations() {
return mDrawable.getChangingConfigurations();
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mDrawable.setBounds(bounds);
}
@Override
public void setAlpha(int alpha) {
mDrawable.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mDrawable.setColorFilter(cf);
}
@Override
public int getOpacity() {
return mDrawable.getOpacity();
}
@Override
protected boolean onStateChange(int[] state) {
mCurrentState = state;
return mDrawable.setState(state);
}
@Override
public boolean isStateful() {
return true;
}
@Override
public void invalidateDrawable(Drawable who) {
if (who == mDrawable && getCallback() != null) {
getCallback().invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (who == mDrawable && getCallback() != null) {
getCallback().scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
if (who == mDrawable && getCallback() != null) {
getCallback().unscheduleDrawable(this, what);
}
}
private static class StateListState {
private static final int INITIAL_SIZE = 5;
int mNumChildren;
int[][] mStateSets;
int[] mColors;
StateListState() {
mStateSets = new int[INITIAL_SIZE][];
mColors = new int[INITIAL_SIZE];
}
int addStateSet(int[] stateSet, int color) {
final int pos = addChild(color);
mStateSets[pos] = stateSet;
return pos;
}
public final int addChild(int color) {
final int pos = mNumChildren;
if (pos >= mColors.length) {
growArray(pos, pos + 10);
}
mColors[pos] = color;
mNumChildren++;
return pos;
}
private void growArray(int oldSize, int newSize) {
int[] newColors = new int[newSize];
System.arraycopy(mColors, 0, newColors, 0, oldSize);
mColors = newColors;
final int[][] newStateSets = new int[newSize][];
System.arraycopy(mStateSets, 0, newStateSets, 0, oldSize);
mStateSets = newStateSets;
}
int indexOfStateSet(int[] stateSet) {
final int[][] stateSets = mStateSets;
final int N = getChildCount();
for (int i = 0; i < N; i++) {
if (StateSet.stateSetMatches(stateSets[i], stateSet)) {
return i;
}
}
return -1;
}
public final int getChildCount() {
return mNumChildren;
}
public int getColor(int index) {
if (index >= 0 && index < mColors.length) {
return mColors[index];
}
return 0;
}
}
}
简介一下写这个装饰器的基本思路吧
这个类事实上也挺简单的,就是使用装饰器模式包装了一个Drawable对象,然后将涉及到会引起界面变化的类都分发到所包装的drawable里。
核心代码事实上在于,初始化drawable的时候,通过addState加入一个状态和该状态指定的Color色值,然后在draw()的时候,通过当前的状态去匹配当前应该显示的色值,然后通过setColorFilter设置应该显示的色值,从而令drawable显示的色值发生变化。
另外匹配当前状态的代码參考自StateListDrawable。
相关文章
- HeadFirst设计模式 之 C++实现(三):Decorator(装饰者模式)
- 使用X-UA-Compatible来设置IE8/IE9兼容模式
- 谷歌浏览器设置护眼模式,全黑主题
- VS报错:HTTP Error 500.23 - Internal Server Error 检测到在集成的托管管道模式下不适用的 ASP.NET 设置。
- chrome 设置黑暗模式
- win11的edge浏览器兼容模式的设置方法
- 《多核与GPU编程:工具、方法及实践》----2.3 分解模式
- PostgreSQL和Kingbase中设置当前模式
- 用最简单的例子理解策略模式(Strategy Pattern)
- RabbitMQ的6种工作模式详解
- 【GIS】Cesium地下模式设置
- 浏览器渲染模式设置
- Decorator模式(装饰器模式)
- 《JavaScript设计模式》——第9章 JavaScript设计模式9.1 Constructor(构造器)模式
- 《实现模式(修订版)》—第3章3.1节价值观
- SET CONSTRAINTS - 设置当前事务的约束模式
- Angular Universal的三种开发模式
- SwiftUI List 编辑模式修改列表内容(Textfield设置第一个响应)
- 《Storm分布式实时计算模式》——1.4 Storm的并发机制
- Bridge的VLAN接口模式
- 谷歌开发新通讯标准RCS 欲颠覆传统短信模式
- 单片机根据应答发送AT指令控制ESP8266设置为服务器—AP模式
- 开启反扑模式 AMD给服务器市场空投一枚名为“EPYC”的炸弹
- SQL练习之不破坏应用程序现有查询的修改模式
- HTML:浏览模式设置(meta标签)
- 分布式光伏补贴趋势及各地三种并网模式收益率排行