Странное поведение элемента ListView и фона состояния селектора - PullRequest
6 голосов
/ 09 июля 2011

У меня очень странное поведение ListView при использовании StateListDrawable в качестве фона. Я пытался проследить за ответом на этот пост, так как я не получал состояние state_checked, но теперь мой ListView сходит с ума.

Когда я нажимаю на элемент, он не сразу меняет цвет на элемент state_checked в селекторе. После небольшого щелчка многие представления внезапно переключатся на фон state_checked. Это кажется случайным.

Вот мой xml-код выбора состояния:

<?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true" >
        <shape>
            <gradient
                android:startColor="@color/grey"
                android:endColor="@color/darkgrey"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item android:state_focused="true" >
        <shape>
            <gradient
                android:endColor="@color/orange4"
                android:startColor="@color/orange5"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item android:state_checked="true">
        <shape>
            <gradient
                android:endColor="@color/brown2"
                android:startColor="@color/brown1"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item android:state_selected="true">
        <shape>
            <gradient
                android:endColor="@color/brown2"
                android:startColor="@color/brown1"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item>        
        <shape>
            <gradient
                android:startColor="@color/white"
                android:endColor="@color/white2"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

</selector>

А вот мой класс .java для моего пользовательского представления, реализующего checkable:

public class Entry extends LinearLayout implements Checkable {

    public Entry(Context context) {
        super(context, null);

        // Inflate this view
        LayoutInflater temp = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        temp.inflate(R.layout.entry, this, true);

        initViews();
    }

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

    private void initViews() {
        this.setBackgroundResource(R.drawable.listview_row);
    }

    public boolean isChecked() {
        return _checked;
    }

    public void toggle() {
        _checked = !_checked;
    }

    public void setChecked(boolean checked) {
        _checked = 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();
    }
}

Я искал несколько часов, пытаясь выяснить это, но, к сожалению, должен согласиться с просьбой о помощи. Может кто-нибудь увидеть что-то не так с приведенным выше кодом, который может привести к странному поведению ListView на элементах? Я могу выложить больше кода, если это необходимо.

Ответы [ 3 ]

21 голосов
/ 20 июля 2011

При работе с ListView очень важно всегда помнить, что представления - это представление , а адаптер - модель данных .

Это означает, что все ваше состояние должно быть в адаптере (модель данных), не в представлениях.

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

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

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

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

Решение состоит в том, чтобы поместить ваше состояние проверки в адаптер, и реализовать Adapter.getView(), чтобы установить проверенное состояние представления на основе состояния, которое вы сейчас имеете в адаптере. Таким образом, всякий раз, когда представление перерабатывается (и getView() вызывается для привязки к нему новой строки данных), вы обновляете его проверенное состояние, чтобы правильно следовать новым отображаемым данным.

3 голосов
/ 19 июля 2011

Не думаю, что проблема в коде выше. У меня была эта проблема раньше, и это было связано с переработкой представлений в списке. Это может иметь место для вас, если ваш список продолжается с экрана. Если это так, хороший способ исправить это - сохранить состояния элементов в списке, чтобы вы могли отслеживать их и основывать их состояния на основе созданного вами списка. Посмотрите на это и это для получения дополнительной информации об утилизации представлений.

0 голосов
/ 22 июля 2011

При выполнении чего-то похожего с довольно сложным набором ListViews я добавил дополнительный список в адаптер и добавил в него положение нажатых элементов. Затем getView (...) раздувает / перезагружает представление и непосредственно перед его завершением проверяет состояние элемента и состояние внутреннего адаптера, чтобы решить, какой фон применить.

Я также настроил xml-файл списка состояний, чтобы сделать фон прозрачным при нажатии в данный момент, чтобы селектор был виден, он работает как удовольствие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...