Подтвердить что ты не робот

Элементы настраиваемого списка, не отвечающие на state_checked в селекторе

Начну с того, что я подробно прочитал почти каждый вопрос о SO, который я могу найти, связанный с пользовательскими проверяемыми элементами списка и селекторами. Многие из них имеют схожие проблемы, но ни один из ответов не решает мою проблему.

В момент моего приложения я представляю пользовательский список. Когда он создается, он извлекает набор статических данных из намерения, вызвавшего его, и передает эти данные ему адаптеру пользовательского массива. Каждый элемент списка представляет собой простой RelativeLayout, который реализует интерфейс Checkable. По умолчанию при нажатии на один из элементов отображается новое действие, которое отображает подробную информацию о выбранном контакте. Однако, если элемент в списке длинным щелчком, запускается ActionMode. Нажатие на элемент в списке в этот момент не отображает активность детали, оно просто устанавливает элемент для проверки. Затем, если пользователь выбирает один из элементов режима действия, он выполняет действие над отмеченными элементами.

Важно понять, что в обоих режимах выбора щелчок по элементу списка задает его для проверки.

Все, что я описал выше, отлично работает. Моя проблема только связана с тем, что элементы списка не выделяются, когда они установлены для проверки, даже с помощью селектора по умолчанию.

Что я хочу сделать, есть два селектора: один для каждого режима выбора. Во-первых, фон не изменяется, когда элемент проверяется, а во втором -. Я пробовал реализовать пользовательские селектора, но даже в состоянии state_checked игнорируется! Другие части селектора работают нормально, но не state_checked.

Моя реализация CheckableListItem включает идеи из множества разных примеров, поэтому, если я что-то делаю неправильно, или если есть лучший способ, дайте мне знать!

Примечание. Интересным моментом является то, что если я устанавливаю фон элементов списка в файле results_list_item.xml для моего селектора, вместо свойства listSelector в ListView, фоны изменяются, когда элемент проверено. Однако это приводит к тому, что переход с длинным нажатием в моем селекторе не работает.

ResultsActivity.java

public class ResultsActivity extends ListActivity implements OnItemLongClickListener {

    private ListView listView;          // Reference to the list belonging to this activity
    private ActionMode mActionMode;     // Reference to the action mode that can be started
    private boolean selectionMode;      // Detail mode or check mode

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_results);

        // When the home icon is pressed, go back
        ActionBar actionBar = getActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);

        // Get a reference to the list
        listView = getListView();

        // Initially in detail mode
        selectionMode = true;

        // Get the contacts from the intent data and pass them to the contact adapter
        @SuppressWarnings("unchecked")
        ArrayList<Contact> results = ((ArrayList<Contact>)getIntent().getSerializableExtra("results"));
        Contact[] contacts = new Contact[results.size()];
        ContactArrayAdapter adapter = new ContactArrayAdapter(this, results.toArray(contacts));
        setListAdapter(adapter);

        // We will decide what happens when an item is long-clicked
        listView.setOnItemLongClickListener(this);
    }

    /**
     * If we are in detail mode, when an item in the list is clicked
     * create an instance of the detail activity and pass it the
     * chosen contact
     */
    public void onListItemClick(ListView l, View v, int position, long id) {
        if (selectionMode) {
            Intent displayContact = new Intent(this, ContactActivity.class);
            displayContact.putExtra("contact", (Contact)l.getAdapter().getItem(position));
            startActivity(displayContact);
        }
    }

    public boolean onCreateOptionsMenu(Menu menu) {
        return super.onCreateOptionsMenu(menu);
    }

    /**
     * If the home button is pressed, go back to the
     * search activity
     */
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                Intent intent = new Intent(this, SearchActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /**
     * When an item is long-pressed, switch selection modes 
     * and start the action mode 
     */
    public boolean onItemLongClick(AdapterView<?> adapter, View view, int position, long i) {
        if (mActionMode != null)
            return false;

        if (selectionMode) {
            toggleSelectionMode();
            listView.startActionMode(new ListActionMode(this, getListView()));
            return true;
        }
        return false;
    }

    /**
     * Clear the list checked items and switch selection modes
     */
    public void toggleSelectionMode() {
        listView.clearChoices();
        ((ContactArrayAdapter)listView.getAdapter()).notifyDataSetChanged();
        if (selectionMode) {
            selectionMode = false;
        } else {
            selectionMode = true;
        }
    }
}

activity_results.xml

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:choiceMode="multipleChoice" 
    android:listSelector="@drawable/list_selector" />

list_selector.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true" android:drawable="@drawable/blue_transition" />
    <item android:state_checked="true" android:drawable="@drawable/blue" />
</selector>

TwoLineArrayAdapter

public abstract class TwoLineArrayAdapter extends ArrayAdapter<Contact> {

    private int mListItemLayoutResId;

    public TwoLineArrayAdapter(Context context, Contact[] results) {
        this(context, R.layout.results_list_item, results);
    }

    public TwoLineArrayAdapter(Context context, int listItemLayoutResourceId, Contact[] results) {
        super(context, listItemLayoutResourceId, results);
        mListItemLayoutResId = listItemLayoutResourceId;
    }

    public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View listItemView = convertView;
        if (convertView == null) {
            listItemView = inflater.inflate(mListItemLayoutResId, parent, false);
        }

        // Get the text views within the layout
        TextView lineOneView = (TextView)listItemView.findViewById(R.id.results_list_item_textview1);
        TextView lineTwoView = (TextView)listItemView.findViewById(R.id.results_list_item_textview2);

        Contact c = (Contact)getItem(position);

        lineOneView.setText(lineOneText(c));
        lineTwoView.setText(lineTwoText(c));

        return listItemView;
    }

    public abstract String lineOneText(Contact c);

    public abstract String lineTwoText(Contact c);

}

ContactArrayAdapter

public class ContactArrayAdapter extends TwoLineArrayAdapter {

    public ContactArrayAdapter(Context context, Contact[] contacts) {
        super(context, contacts);
    }

    public String lineOneText(Contact c) {
        return (c.getLastName() + ", " + c.getFirstName());
    }

    public String lineTwoText(Contact c) {
        return c.getDepartment();
    }

}

CheckableListItem.java

public class CheckableListItem extends RelativeLayout implements Checkable {

    private boolean isChecked;
    private List<Checkable> checkableViews;

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

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

    public CheckableListItem(Context context, int checkableId) {
        super(context);
        initialise(null);
    }

    private void initialise(AttributeSet attrs) {
        this.isChecked = false;
        this.checkableViews = new ArrayList<Checkable>(5);
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean check) {
        isChecked = check;
        for (Checkable c : checkableViews) {
            c.setChecked(check);
        }
        refreshDrawableState();
    }

    public void toggle() {
        isChecked = !isChecked;
        for (Checkable c : checkableViews) {
            c.toggle();
        }
    }

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

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

    protected void onFinishInflate() {
        super.onFinishInflate();
        final int childCount = this.getChildCount();
        for (int i = 0; i < childCount; i++) {
            findCheckableChildren(this.getChildAt(i));
        }
    }

    private void findCheckableChildren(View v) {
        if (v instanceof Checkable) {
            this.checkableViews.add((Checkable) v);
        }
        if (v instanceof ViewGroup) {
            final ViewGroup vg = (ViewGroup) v;
            final int childCount = vg.getChildCount();
            for (int i = 0; i < childCount; i++) {
                findCheckableChildren(vg.getChildAt(i));
            }
        }
    }
}

results_list_item.xml

<com.test.mycompany.Widgets.CheckableListItem
    android:id="@+id/results_list_item"
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="5dp"
    android:paddingBottom="5dp" >

    <TextView android:id="@+id/results_list_item_textview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:textSize="20sp"
        android:textColor="#000000"
        android:focusable="false" />

    <TextView android:id="@+id/results_list_item_textview2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@id/results_list_item_textview1"
        android:textSize="16sp"
        android:textColor="@android:color/darker_gray" 
        android:focusable="false" />

</com.test.mycompany.Widgets.CheckableListItem>
4b9b3361

Ответ 1

Я изменил и добавил эти методы в CheckedListItem, и он работает для меня:

@Override
public boolean onTouchEvent( MotionEvent event ) {

    int action = event.getAction() & MotionEvent.ACTION_MASK;
    if ( action == MotionEvent.ACTION_UP ) {
        toggle();
    }

    return true;
}

public void toggle() {

    setChecked( !isChecked() );
}

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

Кажется, проблема заключалась в том, что при щелчке вы никогда не обрабатывали проверенное состояние представления.

Ответ 2

Если это еще не решено, попробуйте дать свой "проверяемый макет" фон, который можно выбрать с помощью селектора и состояний и определения цвета в нем. В противном случае drawableStateChanged ничего не сделает, потому что mBackground имеет значение null. (например, android: background = "@drawable/list_selector" )

Затем убедитесь, что вы используете listview.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE), чтобы разрешить проверку нескольких элементов.

Вам не нужно реализовывать setOnItemClickListener, чтобы проверить элементы, поскольку режим выбора настроек автоматически делает это уже. (НЕ устанавливайте параметр Checkable layout, который можно щелкнуть)

Ну, по крайней мере, так я и решил.