如何添加自定义按钮状态

例如,默认按钮的状态和背景图像之间有以下依赖关系:

<?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) ,然后我可以使用它来动态更改我的按钮视觉外观?

67825 次浏览

这个线程 显示了如何向按钮等添加自定义状态。(如果你在浏览器中看不到新的 Google 群组,那里有一个线程 给你的副本。)

由@(Ted Hopp)表示的解决方案是可行的,但是需要一点修正: 在选择器中,条目状态需要一个“ app:”前缀,否则膨胀器将无法正确识别名称空间,并且会无声无息地失败; 至少这是发生在我身上的。

请允许我在此报告整个解决方案,并提供更多细节:

首先,创建文件“ 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”。您必须实现一个构造函数; 实现这个构造函数,这个构造函数似乎就是充气器所使用的构造函数:

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

还有,你的状态变量:

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

最后,这个难题中最微妙的部分: 定义 StateListDrawable 的选择器,您将使用它作为小部件的背景。这是文件“ res/draable/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 命名空间对于膨胀机的正确解释至关重要,并且取决于要在其中添加属性的项目类型。如果它是一个应用程序,将 Com.mydomain.mypackage替换为应用程序的实际包名(应用程序名不包括在内)。如果它是一个库,你必须使用“ http://schemas.android.com/apk/res-auto”(并且使用 Tools r17或更高版本) ,否则你会得到运行时错误。

一些注意事项:

  • 似乎不需要调用“ refshDrawableState”函数,至少在我的例子中,该解决方案工作得很好

  • 为了在布局 xml 文件中使用自定义类,必须指定完全限定名(例如 com.mydomain.mypackage)。食物按钮)

  • 您可以将标准状态(例如 android: press,android: enable,android: select)与自定义状态混合,以表示更复杂的状态组合

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

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

我花了很多时间才弄明白为什么我的按钮没有改变它的状态,即使一切看起来正常。