Пользовательский проверяемый вид, который отвечает на селектор

У меня есть группа FrameLayout который я хочу чтобы можно было проверить / выбрать,

То есть после клика мне бы хотелось FrameLayout отображать как checked - при повторном нажатии я бы хотел, чтобыchecked, Более того, я хочу, чтобы эта визуальная очередь была описана как обычно через использование <selector>,

Я не могу заставить это работать - я не уверен, что мне не хватает:

public class CheckableFrameLayout extends FrameLayout implements Checkable {
    private boolean mChecked = false;
    public CheckableFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setChecked(boolean checked) {
        mChecked = checked;
        refreshDrawableState();
    }

    public boolean isChecked() {
        return mChecked;
    }

    public void toggle() {
        setChecked(!mChecked);
    }
}

Макет CheckableFrameLayout:

<com.test.view.CheckableFrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/selector_horizontal"
    android:clickable="true" >

Селектор, поддерживающий его (selector_horizont.xml):

<item android:drawable="@drawable/selector_vertical_selected" android:state_pressed="false" android:state_checked="true"/>  
<item android:drawable="@drawable/selector_vertical_pressed" android:state_pressed="true" android:state_checked="false"/>
<item android:drawable="@drawable/selector_vertical_normal" android:state_pressed="false" android:state_checked="false"/>

Используя приведенный выше код, "state_pressed" работает нормально, но само представление не проверяется (это не Checkable код, вызываемый как обнаруженный с помощью отладки).

6 ответов

Решение

Добавление следующего кода в Checkable Класс позволяет селекторам работать:

private static final int[] CheckedStateSet = {
    android.R.attr.state_checked,
};

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    if (isChecked()) {
        mergeDrawableStates(drawableState, CheckedStateSet);
    }
    return drawableState;
}

@Override
public boolean performClick() {
    toggle();
    return super.performClick();
}

Вот полный рабочий пример для CheckableButton. Это также работает на Android 4.2.

public class CheckableButton extends Button implements Checkable {

    private static final int[] CheckedStateSet = { android.R.attr.state_checked };

    private boolean mChecked = false;

    public CheckableButton(Context context) {
        super(context);
    }

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

    public CheckableButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean isChecked() {
        return mChecked;
    }

    @Override
    public void setChecked(boolean checked) {
        mChecked = checked;
        refreshDrawableState();
    }

    @Override
    public void toggle() {
        mChecked = !mChecked;
        refreshDrawableState();
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CheckedStateSet);
        }
        return drawableState;
    }
}

В дополнение к ответу Грэма, приведенному выше, сделайте следующую модификацию toggle()

@Override
public void toggle() {
    mChecked = !mChecked;
    refreshDrawableState();
}

Решение от Graeme не сработало от меня под Android 4.0.3 (и я полагаю, что оно не будет работать под 4.0 тоже). Вместо этого вы можете изменить state_checked в state_activated:

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/activated_image" android:state_pressed="false" android:state_activated="true" />
    <item android:drawable="@drawable/not_activated_image" android:state_pressed="false" android:state_activated="false"/>

</selector>

и использовать setActivated внутри вашего setChecked:

@Override
public void setChecked(boolean checked) {
    mChecked = checked;
    setActivated(checked);
}

и это все. Внедрение onCreateDrawableState здесь не требуется

Попробуйте изменить порядок тегов в вашем selector_horizontal.xml Они оцениваются сверху вниз. Похоже, что в вашем случае либо android:state_pressed="false" or android:state_pressed="true" применяется, и оценка никогда не достигает третьей строки с android:state_checked="true" Попробуйте переместить первую строку в последнюю:

<item android:drawable="@color/HighlightColor" android:state_pressed="true"/>
<item android:drawable="@color/selected_color" android:state_checked="true"/>
<item android:drawable="@android:color/transparent" android:state_pressed="false"/>

Попробуйте использовать android:state_activated,

<item android:drawable="@color/HighlightColor"      android:state_activated="true"/> 

Из документов:

Значение состояния для StateListDrawable, устанавливается, когда представление или его родительский элемент были "активированы", что означает, что пользователь в настоящее время пометил его как представляющий интерес.

Другие вопросы по тегам