Сделать RelativeLayout доступным для проверки

Я пытаюсь написать свой собственный CheckBox, используя RelativeLayout с TextView и FrameLayout (с селектором на фоне) внутри.

У меня есть setDuplicateParentStateEnabled(true) для FrameLayout, чтобы он брал проверяемый статус от родителя, но зачем делать RelativeLayout проверяемым, я не знаю.

public class MyCheckbox extends RelativeLayout implements OnClickListener, Checkable
{
private static final int ID_CHECKBOX = -1234411;
private static final int ID_TEXTVIEW = -1234412;
private static final int ID_PARENT = -1234413;

private TextView textView;
private FrameLayout checkBox;
private boolean isChecked;

public MyCheckbox(Context context)
{
    this(context, null);
}

public MyCheckbox(Context context, AttributeSet attrs)
{
    super(context, attrs);

    LayoutParams lp;

    setId(ID_PARENT);
    setOnClickListener(this);

    // checkBox
    checkBox = new FrameLayout(context);
    checkBox.setId(ID_CHECKBOX);
    checkBox.setDuplicateParentStateEnabled(true);

    // textView
    textView = new TextView(context);
    textView.setId(ID_TEXTVIEW);
    textView.setOnClickListener(this);


    boolean checkboxToRight = false;

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MaptrixCheckbox);

    for (int i = 0; i < a.getIndexCount(); i++)
    {
        int attr = a.getIndex(i);
        switch (attr)
        {
            case R.styleable.MyCheckbox_checkboxToRight:
                checkboxToRight = a.getBoolean(attr, false);
                break;

            case R.styleable.MyCheckbox_checkMark:
                checkBox.setBackgroundDrawable(a.getDrawable(attr));
                break;

            case R.styleable.MyCheckbox_text:
                textView.setText(a.getText(attr));
                break;
        }
    }
    a.recycle();

    if (checkboxToRight)
    {
        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        lp.addRule(ALIGN_PARENT_RIGHT);
        lp.addRule(CENTER_VERTICAL);
        lp.setMargins(margins, 0, margins, 0);
        addView(checkBox, lp);

        lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        lp.addRule(CENTER_VERTICAL);
        lp.addRule(LEFT_OF, ID_CHECKBOX);
        addView(textView, lp);
    }
    else
    {
        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        lp.addRule(CENTER_VERTICAL);
        lp.setMargins(margins, 0, margins, 0);
        addView(checkBox, lp);

        lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        lp.addRule(CENTER_VERTICAL);
        lp.addRule(RIGHT_OF, ID_CHECKBOX);
        addView(textView, lp);
    }

    refreshDrawableState();
}

@Override
public void onClick(View v)
{
    int id = v.getId();

    if (id == ID_PARENT || id == ID_TEXTVIEW) toggle();
}

public boolean isChecked()
{
    return isChecked;
}

public void setChecked(boolean chacked)
{
    isChecked = chacked;
    refreshDrawableState();
}

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

person Nik    schedule 02.08.2012    source источник


Ответы (4)


Похоже, что в Holo есть два шаблона пользовательского интерфейса для списков множественного выбора (в контексте ActionMode): использование флажков или выделение.

Если все, что вам нужно, это выделить, вы можете использовать этот более простой метод:

Сделайте так, чтобы ваш макет элемента списка использовал для корня класс, подобный этому:

public class CheckableRelativeLayout extends RelativeLayout implements Checkable {
private static final int[] STATE_CHECKABLE = {R.attr.state_pressed};
boolean checked = false;

public void setChecked(boolean checked) {
    this.checked = checked;
    refreshDrawableState();
}

public boolean isChecked() {
    return checked;
}

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

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    if (checked) mergeDrawableStates(drawableState, STATE_CHECKABLE);

    return drawableState;
}
}

и убедитесь, что корневой элемент в макете элемента списка использует

android:background="?android:attr/listChoiceBackgroundIndicator"

Я знаю, что на самом деле это не ответ на ваш вопрос (вам нужен был настоящий флажок), но я нигде не видел этого задокументированного.

ListView использует интерфейс Checkable, чтобы определить, может ли он полагаться на элемент элемента списка для отображения проверяемого состояния или должен попытаться отобразить его самостоятельно.

person Pierre-Luc Paour    schedule 23.11.2012
comment
Большое спасибо за это @Pierre-Luc! Я потратил на это так много времени за последние пару дней, и ваша реализация стала отличной отправной точкой. Мне также не нужен флажок, но мне нужно реализовать checkable. - person krider2010; 08.12.2013
comment
@ Пьер-Люк, вы легенда, сэр. Я следовал этому руководству, чтобы реализовать проверяемый RelativeLayout, но отсутствует refreshDrawableState(). К моим элементам ListView нужно было прикоснуться дважды, прежде чем элемент стал выбранным (выделенным). - person Blake Mumford; 03.01.2014
comment
Уверен, что это должен быть R.attr.state_pressed? - person android developer; 08.11.2015
comment
@androiddeveloper кажется, что это работает, поскольку цель этой реализации - имитировать нажатое состояние при выборе элемента списка. - person Pierre-Luc Paour; 08.11.2015
comment
@Pierre-LucPaour Но его имя можно проверить, а вопрос о проверке... - person android developer; 08.11.2015
comment
Извините… Я имел в виду имитировать нажатое состояние, когда элемент списка отмечен (но без флажка). В основной части моего ответа я отмечаю, что это может не отвечать на вопрос. - person Pierre-Luc Paour; 09.11.2015
comment
@androiddeveloper Это должно быть android.R.attr.state_pressed, но в остальном это работает как шарм! - person H. de Jonge; 22.09.2016

Мне просто нужен метод переопределения:

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

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

Теперь все работает.

person Nik    schedule 02.08.2012
comment
Можете ли вы опубликовать свой макет XML? У меня тоже проблемы. Вы случайно не установили android:clickable=true в макете? - person Tim Kist; 15.04.2014

Ответ Ника частично подходит для меня, вам нужно добавить android.R.attr.state_checkable в массив STATE_CHECKABLE и установить соответствующую длину (2), иначе он не сработает.

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

@Override
protected int[] onCreateDrawableState(int extraSpace)
{
    int[] result = super.onCreateDrawableState(extraSpace + CHECKED_STATE_SET.length);
    if(mChecked) mergeDrawableStates(result, CHECKED_STATE_SET);
    return result;
}
person FunGapApp    schedule 08.05.2013

http://developer.android.com/reference/android/view/View.html#setClickable%28boolean%29

public MyCheckbox(Context context)
{
    this(context, null);
    this.setClickable(true);
}

public MyCheckbox(Context context, AttributeSet attrs)
{
    super(context, attrs);
    this.setClickable(true);
//......
}
person Zelter Ady    schedule 02.08.2012
comment
не кликабельно! Мне нужно сделать мой взгляд доступным для проверки. Я уже решил свой вопрос. - person Nik; 02.08.2012