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


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

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

<span ><?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 ><?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文件的正常显示效果是:

android AutoComplete动态 android动态图片_android


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

<span >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>


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

android AutoComplete动态 android动态图片_移动开发_02


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

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

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();
		}
	}
}


有疑问欢迎留言。