Сохранение состояния SwitchCompat в окне рециркулятора при прокрутке - PullRequest
0 голосов
/ 06 мая 2018

У меня есть recyclerview, в котором его предметы содержат textView и switchCompat. И в том же упражнении у меня также есть textView, в котором есть числовое значение. Задача состоит в том, чтобы switchCompat включил текстовое представление над recyclerview, которое содержит числовое значение, должно увеличиваться на значение в recyclerview элементе textview. Я уже сделал это, но при прокрутке recyclerview switchCompat возвращается к состоянию по умолчанию, а числовое значение textview возвращается к старому значению

Любая помощь с этим?

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

Спасибо

1 Ответ

0 голосов
/ 07 мая 2018

Ключом к представлению переработчика или любого представления адаптера в Android является то, что адаптер адаптирует ваши модели к представлению. В вашем случае ваш вид - TextView плюс Switch, поэтому ваш адаптер должен адаптировать некоторую модель к этому виду. В этом случае я бы выбрал простую модель, подобную этой:

class ItemModel {
  String text;
  boolean on;
}

Я упустил геттеры и сеттеры для простоты

Эта модель содержит строку text, которая отражает текст в вашем текстовом представлении, и логическое значение on, которое отражает состояние переключателя. Когда true, переключатель проверен, а когда false, он не проверен.

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

Теперь давайте создадим адаптер, который может выполнять 2 действия: обновлять модели при нажатии переключателя и сообщать операции, что состояние переключателя изменилось. Вот один из способов сделать это:

public class ItemsAdapter extends 
  RecyclerView.Adapter<ItemsAdapter.ViewHolder> {
    @NonNull
    private final List<ItemModel> itemModels;
    @Nullable
    private OnItemCheckedChangeListener onItemCheckedChangeListener;

    ItemsAdapter(@NonNull List<ItemModel> itemModels) {
        this.itemModels = itemModels;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
        ItemModel item = itemModels.get(position);
        holder.text.setText(item.text);
        holder.switchCompat.setChecked(item.on);

        // Make sure we update the model if the user taps the switch
        holder.switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                int adapterPosition = holder.getAdapterPosition();

                ItemModel tapped = itemModels.get(adapterPosition);
                itemModels.set(adapterPosition, new ItemModel(tapped.text, isChecked));

                if (onItemCheckedChangeListener != null) {
                    onItemCheckedChangeListener.onItemCheckedChanged(adapterPosition, isChecked);
                }
            }
        });
    }

    @Override
    public void onViewRecycled(@NonNull ViewHolder holder) {
        super.onViewRecycled(holder);

        holder.switchCompat.setOnCheckedChangeListener(null);
    }

    @Override
    public int getItemCount() {
        return itemModels.size();
    }

    public void setOnItemCheckedChangeListener(@Nullable OnItemCheckedChangeListener onItemCheckedChangeListener) {
        this.onItemCheckedChangeListener = onItemCheckedChangeListener;
    }

    interface OnItemCheckedChangeListener {
        /**
         * Fired when the item check state is changed
         */
        void onItemCheckedChanged(int position, boolean isChecked);
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        TextView text;
        SwitchCompat switchCompat;

        ViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.item_text);
            switchCompat = itemView.findViewById(R.id.item_switch);
        }
    }
}

Есть много чего переварить, но давайте сосредоточимся на важных битах - метод onBindViewHolder. Первые 3 строки - это классическая переработка вида. Мы берем модель в правильном положении и устанавливаем элементы в представлении, которые соответствуют атрибутам модели.

Тогда становится интереснее. Мы устанавливаем OnCheckedChangeListener для обновления модели и активности каждый раз, когда коммутатор меняет состояние. Первые 3 строки изменяют модель в адаптере, а остальные используют пользовательский интерфейс OnItemCheckedChangeListener, чтобы уведомить слушателя об изменении коммутатора. Важно отметить, что внутри метода OnCheckedChangeListener вы больше не должны использовать position, а вместо этого использовать holder.getAdapterPosition. Это даст вам правильное положение в списке данных адаптера.

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

Важно удалить OnCheckedChangeListener, когда представление будет переработано - onViewRecycled. Это позволяет избежать путаницы в подсчете, когда адаптер устанавливает значение switchCompat в onBindViewHolder.

Вот пример того, как может выглядеть упражнение:

public class MainActivity extends AppCompatActivity {
    private int count = 0;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<ItemModel> data = new ArrayList<>();

        for (int i = 1; i <= 100; i++)
            data.add(new ItemModel("Item " + i, false));

        ItemsAdapter adapter = new ItemsAdapter(data);

        ((RecyclerView) findViewById(R.id.recyclerview)).setAdapter(adapter);

        final TextView countTextView = findViewById(R.id.count);

        drawCount(countTextView);

        adapter.setOnItemCheckedChangeListener(new ItemsAdapter.OnItemCheckedChangeListener() {
            @Override
            public void onItemCheckedChanged(int position, boolean isChecked) {
                if (isChecked)
                    count++;
                else
                    count--;

                drawCount(countTextView);
            }
        });
    }

    private void drawCount(TextView countTextView) {
        countTextView.setText(String.valueOf(count));
    }
}

Этот код предназначен для демонстрации идеи, а не для следования :) В любом случае, мы настраиваем все начальное состояние, а затем настраиваем настраиваемый прослушиватель OnItemCheckedChangeListener для обновления представления текста в действии.

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

Надеюсь, это поможет

...