如何添加自定义按钮状态

Vit*_*nko 124 android state 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>
Run Code Online (Sandbox Code Playgroud)

如何定义自己的自定义状态(类似于smth android:state_custom),那么我可以使用它来动态更改我的按钮视觉外观?

Gio*_*esi 258

@(Ted Hopp)指出的解决方案有效,但需要稍微纠正:在选择器中,项状态需要一个"app:"前缀,否则inflater将无法正确识别命名空间,并将无声地失败; 至少这是发生在我身上的事情.

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

首先,创建文件"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>
Run Code Online (Sandbox Code Playgroud)

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

public FoodButton(Context context, AttributeSet attrs) {
    super(context, attrs);
}
Run Code Online (Sandbox Code Playgroud)

在派生类之上:

private static final int[] STATE_FRIED = {R.attr.state_fried};
private static final int[] STATE_BAKED = {R.attr.state_baked};
Run Code Online (Sandbox Code Playgroud)

另外,你的状态变量:

private boolean mIsFried = false;
private boolean mIsBaked = false;
Run Code Online (Sandbox Code Playgroud)

还有几个二传手:

public void setFried(boolean isFried) {mIsFried = isFried;}
public void setBaked(boolean isBaked) {mIsBaked = isBaked;}
Run Code Online (Sandbox Code Playgroud)

然后覆盖函数"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;
}
Run Code Online (Sandbox Code Playgroud)

最后,这个拼图中最精致的部分; 定义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>
Run Code Online (Sandbox Code Playgroud)

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

几个笔记:

  • 看来你不需要调用"refreshDrawableState"函数,至少解决方案效果不错,就我的情况而言

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

  • 你可以用自定义状态混淆标准状态(例如android:press,android:enabled,android:selected),以表示更复杂的状态组合

  • `refreshDrawableState`绝对是重要的.我不确定什么时候真的需要它.但就我而言,在以编程方式设置状态时需要它.我想它可能是在onTouchEvent中自动从View类调用的.我最好在setSelected方法中添加它. (17认同)
  • 更新:如果自定义类派生自TextView而不是Button,则需要调用refreshDrawableState,否则不会更新窗口小部件外观.呼叫应放在设置者中.我没有尝试过其他课程.在froyo设备上进行的测试. (3认同)
  • 但是,如何使用不是"布尔"的自定义状态?或者选择者只在布尔值上运行? (2认同)
  • 它是如何工作的?我的意思是,属性如何更新为状态true/false?谁更新了?合并drawablestate,只有当局部变量为true时,才更新属性的状态或值?究竟将更新R.attr.state_fried的代码? (2认同)
  • 似乎现在我们可以在任何地方使用 `xmlns:app="http://schemas.android.com/apk/res-auto"`,而不仅仅是在库中。/sf/answers/2143460791/ (2认同)
  • 事实上@Artyom是对的。我们应该使用 `xmlns:app="http://schemas.android.com/apk/res-auto"` 而不是 `"http://schemas.android.com/apk/res/com.mydomain.mypackage" ` (2认同)

Ted*_*opp 10

该线程显示了如何向按钮等添加自定义状态.(如果你不能看到在浏览器中新的谷歌群体,有螺纹的副本在这里.)

  • @Mitch——嗯,那太糟糕了。我看看是否能找到一些替换链接。如果没有,我会删除这个答案,因为它基本上没有用。同时,接受的答案包含所需的所有信息。 (2认同)

Nis*_*oni 5

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

mHandler.post(new Runnable() {
    @Override
    public void run() {
        refreshDrawableState();
    }
});
Run Code Online (Sandbox Code Playgroud)

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