如何添加自定义button状态

例如,默认button在其状态和背景图像之间具有以下依赖关系:

<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/btn_default_normal" /> <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/btn_default_normal_disable" /> <item android:state_pressed="true" android:drawable="@drawable/btn_default_pressed" /> <item android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/btn_default_selected" /> <item android:state_enabled="true" android:drawable="@drawable/btn_default_normal" /> <item android:state_focused="true" android:drawable="@drawable/btn_default_normal_disable_focused" /> <item android:drawable="@drawable/btn_default_normal_disable" /> </selector> 

我怎样才能定义我自己的自定义状态(像android:state_custom smth),那么我可以用它dynamic地改变我的button视觉外观?

@(Ted Hopp)表示的解决scheme工作正常,但需要稍微纠正一下:在select器中,项目状态需要一个“app:”前缀,否则inflater将无法正确识别名称空间,并会默默失败。 至less这是发生在我身上的事情。

请允许我在这里报告整个解决scheme,并提供一些详细信息:

首先,创build文件“res / values / attrs.xml”:

 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="food"> <attr name="state_fried" format="boolean" /> <attr name="state_baked" format="boolean" /> </declare-styleable> </resources> 

然后定义你的自定义类。 例如,它可能是一个类“FoodButton”,从类“Button”派生。 你将不得不实现一个构造函数; 实现这个,这似乎是一个使用的inflater:

 public FoodButton(Context context, AttributeSet attrs) { super(context, attrs); } 

在派生类之上:

 private static final int[] STATE_FRIED = {R.attr.state_fried}; private static final int[] STATE_BAKED = {R.attr.state_baked}; 

另外,你的状态variables:

 private boolean mIsFried = false; private boolean mIsBaked = false; 

还有几个二传手:

 public void setFried(boolean isFried) {mIsFried = isFried;} public void setBaked(boolean isBaked) {mIsBaked = isBaked;} 

然后覆盖函数“onCreateDrawableState”:

 @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 2); if (mIsFried) { mergeDrawableStates(drawableState, STATE_FRIED); } if (mIsBaked) { mergeDrawableStates(drawableState, STATE_BAKED); } return drawableState; } 

最后,这个难题最细腻的一块; select器定义了你将用作你的Widget的背景的StateListDrawable。 这是文件“res / drawable / food_button.xml”:

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.mydomain.mypackage"> <item app:state_baked="true" app:state_fried="false" android:drawable="@drawable/item_baked" /> <item app:state_baked="false" app:state_fried="true" android:drawable="@drawable/item_fried" /> <item app:state_baked="true" app:state_fried="true" android:drawable="@drawable/item_overcooked" /> <item app:state_baked="false" app:state_fried="false" android:drawable="@drawable/item_raw" /> </selector> 

注意“app:”前缀,而对于标准的android状态,你可以使用前缀“android:”。 XML命名空间对于inflater的正确解释至关重要,取决于您添加属性的项目types。 如果是应用程序,请将com.mydomain.mypackagereplace为应用程序的实际包名称(不包括应用程序名称)。 如果它是一个库,你必须使用“http://schemas.android.com/apk/res-auto”(并使用工具R17或更高版本),否则你会得到运行时错误。;

几个注意事项:

  • 看来你不需要调用“refreshDrawableState”函数,至less在我的情况下,解决scheme运行的很好

  • 为了在布局xml文件中使用自定义类,您必须指定完全限定的名称(例如com.mydomain.mypackage.FoodButton)

  • 你可以使用自定义状态来混合标准状态(例如android:pressed,android:启用,android:select),以表示更复杂的状态组合

此线程显示如何将自定义状态添加到button等。 (如果您在浏览器中看不到新的Google群组,则在这里有一个线程副本。)

请不要忘记在UI线程中调用refreshDrawableState

 mHandler.post(new Runnable() { @Override public void run() { refreshDrawableState(); } }); 

我花了很多时间来弄清为什么我的button不改变它的状态,即使一切看起来都正确。