カスタムボタンの状態を追加する方法


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>

独自のカスタム状態(smth like android:state_custom)を定義するにはどうすればよいですか。それを使用して、ボタンの外観を動的に変更できますか?


EditTextビューの追加の状態で、2つのパスワードボックスが一致して小さなチェックマークが表示されるタイミングを決定したかったのです。
Nathan Schwermann、2010

回答:


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」を使用する必要があり(ツールR17以降を使用している場合)、ランタイムエラーが発生します。

いくつかのメモ:

  • 「refreshDrawableState」関数を呼び出す必要がないようです。少なくとも、私の場合、ソリューションは問題なく機能します。

  • カスタムクラスをレイアウトxmlファイルで使用するには、完全修飾名を指定する必要があります(例:com.mydomain.mypackage.FoodButton)

  • より複雑な状態の組み合わせを表すために、カスタム状態と標準状態(例:android:pressed、android:enabled、android:selected)を組み合わせることができます。


3
更新:カスタムクラスがButtonではなくTextViewから派生している場合、refreshDrawableStateの呼び出しが必要であるように見えます。それ以外の場合、ウィジェットの外観は更新されません。呼び出しはセッターで行われます。他のクラスは試していません。Froyoデバイスで実行されたテスト。
Giorgio Barchiesi

17
refreshDrawableState確かに重要です。それが本当に必要なとき、私は絶対にわかりません。しかし、私の場合、プログラムで状態を設定するときに必要でした。onTouchEventでViewクラスから自動的に呼び出される可能性があります。setSelectedメソッドに追加した方がいいです。
buergi 2011

1
GiorgioBarchiesi、2つのカスタムボタンがあります。1つのボタンのonClickイベントから両方のボタンのステータスを変更しようとすると、クリックされたボタンのみが変更されます。@ buergiは、refreshDrawableStateメソッドがonClickEvent。素晴らしいチュートリアルをありがとうございます:)
ボルトン

2
しかし、そうでないカスタム状態をどのように使用できますbooleanか?または、セレクターはブール値でのみ動作しますか?
Peterdk 2013年

2
それはどのように機能しますか?つまり、属性がどのように更新されて真/偽の状態になるのですか?誰が更新しますか?drawablestateをマージすると、ローカル変数がtrueの場合のみ、属性の状態または値が更新されますか?R.attr.state_friedを正確に更新するコードはどれですか?
kAmol 2017

10

このスレッドは、ボタンなどにカスタム状態を追加する方法を示します。(ブラウザに新しいGoogleグループが表示されない場合は、ここにスレッドのコピーがあります。)


+1ありがとう、テッド!今はトラブルの原因がなくなったので、実際の実装には至りませんでした。しかし、私の顧客が再びこれに戻る場合は、あなたが私に指摘した方法を試します。
Vit Khudenko、2010

私が必要としているものとまったく同じに見えますが、カスタム状態の状態リスト
ドローアブル

refreshDrawableState()を呼び出していますか?
テッドホップ、2011年

リンクは死んでいます。
ミッチ

@ミッチ-まあ、それは残念だ。代替リンクを見つけることができるかどうかを確認します。そうでない場合は、基本的にそのままでは役に立たないため、この回答は削除します。その間、受け入れられた答えは必要なすべての情報を持っています。
テッドホップ

6

refreshDrawableStateUIスレッド内で呼び出すことを忘れないでください:

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

すべてが正しいように見えても、ボタンの状態が変化しない理由を理解するのに多くの時間を費やしました。


このハンドラーはいつどこに投稿すればよいですか?
Arthur Melo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.