Android中动态的更改selector中某张图片的属性

简介: 在我们平常开发的时候会有许多状态按钮,比如state_pressed,android:state_checked,或者就正常状态等等,我们做这样的效果通常需要三个文件,一张是按下的图片,一张是正常状态的图片,一张是管理它们的selector文件,如果在不断更新迭代的过程中出现了很多这样的按钮,而且它们的颜色什么的都不一样,那我们的res/drawable文件夹下就会出现很多个这样的组合文件,导致我们的程序越来越大、越来越大,这肯定不是我们想看到的。

在我们平常开发的时候会有许多状态按钮,比如state_pressed,android:state_checked,或者就正常状态等等,我们做这样的效果通常需要三个文件,一张是按下的图片,一张是正常状态的图片,一张是管理它们的selector文件,如果在不断更新迭代的过程中出现了很多这样的按钮,而且它们的颜色什么的都不一样,那我们的res/drawable文件夹下就会出现很多个这样的组合文件,导致我们的程序越来越大、越来越大,这肯定不是我们想看到的。


那么,我现在要拿出什么来解决这个问题呢?就是动态的更改它们的属性,把所有的工作都放在代码里面,减少selector.xml文件的产生:

我们在写属性的时候尽量要使用这样的方式(如果美工给了图的话,尽量让提供颜色值就可以了,不要做那么多图):

<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true"><shape>
            <corners android:radius="5dp" />

            <solid android:color="#78909c" />
        </shape></item>
    <item><shape>
            <corners android:radius="5dp" />

            <solid android:color="#607d8b" />
        </shape></item>

</selector></span>

布局文件:

<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:text="Hello"
        android:textColor="#fff" />

</FrameLayout></span>

这个xml文件的正常显示效果是:


效果还是挺不错的,接下来我们要动态改变它的效果,怎么做呢:

<span style="font-size:12px;">package com.sahadev.activitythemetest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Bundle;
import android.widget.Button;

public class MainActivity2 extends Activity { // Hold a reference to the current

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_zoom);

		Button button = (Button) findViewById(R.id.button);

		Drawable backgroundDrawable = getResources().getDrawable(R.drawable.xml_background);
		button.setBackground(backgroundDrawable);

		StateListDrawable sld = (StateListDrawable) backgroundDrawable;// 通过向下转型,转回原型,selector对应的Java类为:StateListDrawable

		ConstantState cs = sld.getConstantState();

		try {
			Method method = cs.getClass().getMethod("getChildren", null);// 通过反射调用getChildren方法获取xml文件中写的drawable数组
			method.setAccessible(true);
			Object obj = method.invoke(cs, null);
			Drawable[] drawables = (Drawable[]) obj;

			for (int i = 0; i < drawables.length; i++) {
				// 接下来我们要通过遍历的方式对每个drawable对象进行修改颜色值
				GradientDrawable gd = (GradientDrawable) drawables[i];
				if (gd == null) {
					break;
				}
				if (i == 0) {
					// 我们对按下的状态做浅色处理
					gd.setColor(Color.rgb(155, 155, 155));
				} else {
					// 对默认状态做深色处理
					gd.setColor(Color.rgb(75, 75, 75));
				}
			}
			// 最后总结一下,为了实现这个效果,刚开始并没有看到setColor的方法,而是通过反射获取GradientDrawable对象的属性GradientState,
			// 再通过反射调用GradientState对象的setSolidColor方法去实现,效果不太理想。
			// 最后在仔仔细细一一看GradientDrawable对象的属性,发现属性Paint
			// mFillPaint,从名字就可以看出这个对象是用来绘制drawable的背景的,
			// 于是顺着往下找,发现setColor方法,于是bingo,这个过程也是挺曲折的。

		} catch (NoSuchMethodException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}

	}

}
</span>
方法就是这样,我们再来看看最后的实现效果:



怎么样,效果实现了我们需要的。

最后,贴上一个抽取出来的工具类:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;

public class SelectorUtils {
	/**
	 * 动态修改selector中图片的背景颜色
	 * 
	 * @param drawable
	 *            selectorDrawable
	 * @param rgbColors
	 *            默认以及按下状态的颜色值
	 */
	public static void changeViewColor(StateListDrawable drawable, int[] rgbColors) {
		ConstantState cs = drawable.getConstantState();
		if (rgbColors.length < 2) {
			return;
		}
		try {
			Method method = cs.getClass().getMethod("getChildren", null);// 通过反射调用getChildren方法获取xml文件中写的drawable数组
			method.setAccessible(true);
			Object obj = method.invoke(cs, null);
			Drawable[] drawables = (Drawable[]) obj;

			for (int i = 0; i < drawables.length; i++) {
				// 接下来我们要通过遍历的方式对每个drawable对象进行修改颜色值
				GradientDrawable gd = (GradientDrawable) drawables[i];
				if (gd == null) {
					break;
				}
				if (i == 0) {
					// 我们对按下的状态做浅色处理
					gd.setColor(rgbColors[0]);
				} else {
					// 对默认状态做深色处理
					gd.setColor(rgbColors[1]);
				}
			}
			// 最后总结一下,为了实现这个效果,刚开始并没有看到setColor的方法,而是通过反射获取GradientDrawable对象的属性GradientState,
			// 再通过反射调用GradientState对象的setSolidColor方法去实现,效果不太理想。
			// 最后在仔仔细细一一看GradientDrawable对象的属性,发现属性Paint
			// mFillPaint,从名字就可以看出这个对象是用来绘制drawable的背景的,
			// 于是顺着往下找,发现setColor方法,于是bingo,这个过程也是挺曲折的。

		} catch (NoSuchMethodException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}

有疑问欢迎留言。

目录
相关文章
|
6月前
|
XML Android开发 数据格式
Android利用selector(选择器)实现图片动态点击效果
本文介绍了Android中ImageView的`src`与`background`属性的区别及应用,重点讲解如何通过设置背景选择器实现图片点击动态效果。`src`用于显示原图大小,不拉伸;`background`可随组件尺寸拉伸。通过创建`selector_setting.xml`,结合`setting_press.xml`和`setting_normal.xml`定义按下和正常状态的背景样式,提升用户体验。示例代码展示了具体实现步骤,包括XML配置和形状定义。
289 3
Android利用selector(选择器)实现图片动态点击效果
|
6月前
|
Android开发 开发者
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
本文详细介绍了如何通过自定义 `attrs.xml` 文件实现 Android 自定义 View 的属性配置。以一个包含 TextView 和 ImageView 的 DemoView 为例,讲解了如何使用自定义属性动态改变文字内容和控制图片显示隐藏。同时,通过设置布尔值和点击事件,实现了图片状态的切换功能。代码中展示了如何在构造函数中解析自定义属性,并通过方法 `setSetting0n` 和 `setbackeguang` 实现功能逻辑的优化与封装。此示例帮助开发者更好地理解自定义 View 的开发流程与 attrs.xml 的实际应用。
151 2
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
|
6月前
|
Java Android开发
Android图片的手动放大缩小
本文介绍了通过缩放因子实现图片放大缩小的功能,效果如动图所示。关键步骤包括:1) 在布局文件中设置 `android:scaleType=&quot;matrix&quot;`;2) 实例化控件并用 `ScaleGestureDetector` 处理缩放手势;3) 使用 `Matrix` 对图片进行缩放处理。为避免内存崩溃,可在全局配置添加 `android:largeHeap=&quot;true&quot;`。代码中定义了 `beforeScale` 和 `nowScale` 变量控制缩放范围,确保流畅体验。
203 8
|
6月前
|
缓存 编解码 Android开发
Android内存优化之图片优化
本文主要探讨Android开发中的图片优化问题,包括图片优化的重要性、OOM错误的成因及解决方法、Android支持的图片格式及其特点。同时介绍了图片储存优化的三种方式:尺寸优化、质量压缩和内存重用,并详细讲解了相关的实现方法与属性。此外,还分析了图片加载优化策略,如异步加载、缓存机制、懒加载等,并结合多级缓存流程提升性能。最后对比了几大主流图片加载框架(Universal ImageLoader、Picasso、Glide、Fresco)的特点与适用场景,重点推荐Fresco在处理大图、动图时的优异表现。这些内容为开发者提供了全面的图片优化解决方案。
249 1
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
198 20
Android经典面试题之图片Bitmap怎么做优化
|
数据处理 开发工具 数据安全/隐私保护
Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路
本文探讨了Android平台上推流模块中添加文字与PNG水印的技术演进。自2015年起,为了满足应急指挥及安防领域的需求,逐步发展出三代水印技术:第一代为静态文字与图像水印;第二代实现了动态更新水印内容的能力,例如实时位置与时间信息;至第三代,则优化了数据传输效率,直接使用Bitmap对象传递水印数据至JNI层,减少了内存拷贝次数。这些迭代不仅提升了用户体验和技术效率,也体现了开发者追求极致与不断创新的精神。
181 7
|
Android开发
AutoX——当Android中clickable属性显示为false,实际可点击的布局如何处理
AutoX——当Android中clickable属性显示为false,实际可点击的布局如何处理
231 0
|
XML Android开发 数据格式
Android中利用shape属性自定义设置Button按钮
Android中利用shape属性自定义设置Button按钮
498 0
|
Android开发
Android 获取include标签中的控件属性并设置事件
Android 获取include标签中的控件属性并设置事件
301 0

热门文章

最新文章