如何添加自定义按钮状态


132

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

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


我想让EditText视图获得更多状态,以确定两个密码框何时匹配以显示一个小勾号。
内森·史威文2010年

Answers:


275

@(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>

然后定义您的自定义类。例如,它可以是派生自“ Button”类的“ FoodButton”类。您将必须实现一个构造函数。实现这一功能,这似乎是充气机使用的功能:

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 / 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名称空间对于充气机的正确解释至关重要,并且取决于要在其中添加属性的项目的类型。如果是应用程序,请将com.mydomain.mypackage替换为应用程序的实际包名称(不包括应用程序名称)。如果是库,则必须使用“ http://schemas.android.com/apk/res-auto”(并使用Tools R17或更高版本),否则会出现运行时错误。

一些注意事项:

  • 看来您不需要调用“ refreshDrawableState”函数,至少在我的情况下,该解决方案可以正常工作

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

  • 您可以将weel混淆的标准状态(例如android:pressed,android:enabled,android:selected)与自定义状态结合起来,以表示更复杂的状态组合


3
更新:如果自定义类是从TextView而不是Button派生的,则对refreshDrawableState的调用似乎是必需的,否则将不更新小部件的外观。该电话应置于二传手中。我没有尝试过其他课程。在froyo设备上执行的测试。
Giorgio Barchiesi 2011年

17
refreshDrawableState肯定是很重要的。我不确定何时真正需要它。但就我而言,以编程方式设置状态时是必需的。我猜它可能是从onTouchEvent中的View类自动调用的。我最好将其添加到setSelected方法中。
buergi 2011年

1
GiorgioBarchiesi,我有两个自定义按钮,当我尝试从一个按钮的onClick事件更改两个按钮的状态时,只有被单击的按钮将被更改,我认为@buergi是对的,在该菜单中调用了refreshDrawableState方法onClickEvent。再次感谢您的精彩教程:)
Bolton

2
但是,如何使用非定制状态boolean呢?还是选择器仅对布尔值起作用?
彼得德克

2
它是如何工作的?我的意思是,该属性如何更新为陈述真/假?谁更新它?仅当局部变量为true时,合并drawablestate才会更新属性的状态或值吗?究竟哪个代码将更新R.attr.state_fried?
kAmol

10

该线程显示了如何向按钮等添加自定义状态。(如果您在浏览器中看不到新的Google网上论坛,请在此处找到该线程的副本。)


+1非常感谢,Ted!现在麻烦的根源已经消失了,所以我没有去实际的实现。但是,如果我的客户再次回到此问题,我将尝试按照您的指示进行操作。
维特·胡登科

看起来完全符合我的需求,但是用于自定义状态的状态列表可绘制对象没有发生变化,我必须缺少一些东西……
Nathan Schwermann 2010年

您在调用refreshDrawableState()吗?
特德·霍普

链接已死。
米奇

@Mitch-太可惜了。我看看是否可以找到一些替换链接。如果没有,我将删除此答案,因为它基本上是没有用的。同时,接受的答案包含所有需要的信息。
特德·霍普

6

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

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

我花了很多时间弄清楚即使一切看起来正确,为什么我的按钮没有更改其状态。


我应该在哪里或何时发布此处理程序?
亚瑟·梅洛
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.