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

Одиночный выбор в RecyclerView

Я знаю, что в классе recyclerview нет методов выбора по умолчанию, но я пробовал следующим образом,

public void onBindViewHolder(ViewHolder holder, final int position) {
    holder.mTextView.setText(fonts.get(position).getName());
    holder.checkBox.setChecked(fonts.get(position).isSelected());

    holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if(isChecked) {
                for (int i = 0; i < fonts.size(); i++) {
                    fonts.get(i).setSelected(false);
                }
                fonts.get(position).setSelected(isChecked);
            }
        }
    });
}

Пробуя этот код, я получил ожидаемый вывод, но совсем нет.

Я объясню это с помощью изображений.

По умолчанию первый элемент выбран из моего адаптера

enter image description here

Затем я пытаюсь выбрать 2-й, а затем 3-й, а затем 4-й и, наконец, 5-й,

enter image description here

Здесь должен быть выбран только 5-й, но все пять выбираются.

Если я прокручиву список до дна и снова зайду,

enter image description here

Я получил то, что ожидал,

Как я могу преодолеть эту проблему? И иногда, если я прокручиваю список очень быстро, выбирается какой-то другой элемент. Как преодолеть эту проблему тоже?

Обновить

Пока я пытаюсь использовать notifyDataSetChanged() после fonts.get(position).setSelected(isChecked); Я получил следующее исключение,

java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
        at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:1462)
        at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:2982)
        at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:7493)
        at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:4338)
        at com.app.myapp.screens.RecycleAdapter.onRowSelect(RecycleAdapter.java:111)
4b9b3361

Ответ 1

Решение проблемы:

public class yourRecyclerViewAdapter extends RecyclerView.Adapter<yourRecyclerViewAdapter.yourViewHolder> {

private static CheckBox lastChecked = null;
private static int lastCheckedPos = 0;


public void onBindViewHolder(ViewHolder holder, final int position) {

    holder.mTextView.setText(fonts.get(position).getName());
    holder.checkBox.setChecked(fonts.get(position).isSelected());
    holder.checkBox.setTag(new Integer(position));

    //for default check in first item
    if(position == 0 && fonts.get(0).isSelected() && holder.checkBox.isChecked())
    {
       lastChecked = holder.checkBox;
       lastCheckedPos = 0;
    }

    holder.checkBox.setOnClickListener(new View.OnClickListener() 
    {
        @Override
        public void onClick(View v) 
        {
           CheckBox cb = (CheckBox)v;
           int clickedPos = ((Integer)cb.getTag()).intValue(); 

           if(cb.isChecked())
           {
              if(lastChecked != null)
              {
                  lastChecked.setChecked(false);
                  fonts.get(lastCheckedPos).setSelected(false);
              }                       

              lastChecked = cb;
              lastCheckedPos = clickedPos;
          }
          else
             lastChecked = null;

          fonts.get(clickedPos).setSelected(cb.isChecked);
       }
   });
}
}

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

Ответ 2

Слишком поздно его опубликовать может помочь кому-то еще Используйте ниже код в качестве ссылки для проверки отдельного элемента в RecyclerView

/**
 * Created by subrahmanyam on 28-01-2016, 04:02 PM.
 */
public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.ViewHolder> {

    private final String[] list;
    private int lastCheckedPosition = -1;

    public SampleAdapter(String[] list) {
        this.list = list;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = View.inflate(parent.getContext(), R.layout.sample_layout, null);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.choiceName.setText(list[position]);
        holder.radioButton.setChecked(position == lastCheckedPosition);
    }

    @Override
    public int getItemCount() {
        return list.length;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        @Bind(R.id.choice_name)
        TextView choiceName;
        @Bind(R.id.choice_select)
        RadioButton radioButton;

        public ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
            radioButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    lastCheckedPosition = getAdapterPosition();
 //because of this blinking problem occurs so 
 //i have a suggestion to add notifyDataSetChanged();
                 //   notifyItemRangeChanged(0, list.length);//blink list problem
                      notifyDataSetChanged();

                }
            });
        }
    }
}

Ответ 3

Похоже, здесь есть две вещи:

(1) Представления повторно используются, поэтому старый слушатель все еще присутствует.

(2) Вы меняете данные без уведомления адаптера об изменении.

Я буду рассматривать каждый отдельно.

(1) Просмотр повторного использования

В принципе, в onBindViewHolder вам предоставляется уже инициализированный ViewHolder, который уже содержит представление. Это ViewHolder может быть или не быть ранее привязанным к некоторым данным!

Обратите внимание на этот бит кода прямо здесь:

holder.checkBox.setChecked(fonts.get(position).isSelected());

Если держатель ранее был привязан, то флажок уже имеет слушатель, когда изменяется состояние проверки! Этот слушатель запускается в этот момент, что и вызывало ваш IllegalStateException.

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

(2) Уведомлять адаптер при изменении данных

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

RecyclerView.Adapter имеет множество вариантов выбора, включая notifyItemChanged, в котором говорится, что определенный элемент изменил состояние. Это может быть полезно для вашего использования.

if(isChecked) {
    for (int i = 0; i < fonts.size(); i++) {
        if (i == position) continue;
        Font f = fonts.get(i);
        if (f.isSelected()) {
            f.setSelected(false);
            notifyItemChanged(i); // Tell the adapter this item is updated
        }
    }
    fonts.get(position).setSelected(isChecked);
    notifyItemChanged(position);
}

Ответ 4

просто используйте mCheckedPosition сохранить статус

@Override
        public void onBindViewHolder(ViewHolder holder, int position) {

            holder.checkBox.setChecked(position == mCheckedPostion);
            holder.checkBox.setOnClickListener(v -> {
                if (position == mCheckedPostion) {
                    holder.checkBox.setChecked(false);
                    mCheckedPostion = -1;
                } else {
                    mCheckedPostion = position;
                    notifyDataSetChanged();
                }
            });
        }

Ответ 5

Это может помочь тем, кто хочет, чтобы работала одна кнопка → Radio Button RecycleView - Gist

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

View.OnClickListener listener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        notifyItemChanged(mSelectedItem); // to update last selected item.
        mSelectedItem = getAdapterPosition();
    }
};

ура

Ответ 6

Перед установкой setChecked() необходимо очистить OnCheckedChangeListener:

@Override
public void onBindViewHolder(final ViewHolder holder, int position) {

    holder.mRadioButton.setOnCheckedChangeListener(null);
    holder.mRadioButton.setChecked(position == mCheckedPosition);
    holder.mRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

            mCheckedPosition = position;
            notifyDataSetChanged();
        }
    });
}

Таким образом, это не приведет к ошибке java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling.

Ответ 7

Вот как это выглядит

Single selection recyclerview best way

Внутри вашего адаптера

private int selectedPosition = -1;

И на BindViewHolder

@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {

    if (selectedPosition == position) {
        holder.itemView.setSelected(true); //using selector drawable
        holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.white));
    } else {
        holder.itemView.setSelected(false);
        holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.black));
    }

    holder.itemView.setOnClickListener(v -> {
        if (selectedPosition >= 0)
            notifyItemChanged(selectedPosition);
        selectedPosition = holder.getAdapterPosition();
        notifyItemChanged(selectedPosition);
    });
}

Это оно! Как видите, я просто уведомляю (обновляю) предыдущий выбранный элемент и новый выбранный элемент.

Мой Drawable установить его в качестве фона для дочерних просмотров переработчика

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_selected="true">
    <shape android:shape="rectangle">
        <solid android:color="@color/blue" />
    </shape>
</item>

Ответ 8

            public class GetStudentAdapter extends 
            RecyclerView.Adapter<GetStudentAdapter.MyViewHolder> {

            private List<GetStudentModel> getStudentList;
            Context context;
            RecyclerView recyclerView;

            public class MyViewHolder extends RecyclerView.ViewHolder {
                TextView textStudentName;
                RadioButton rbSelect;

                public MyViewHolder(View view) {
                    super(view);
                    textStudentName = (TextView) view.findViewById(R.id.textStudentName);
                    rbSelect = (RadioButton) view.findViewById(R.id.rbSelect);
                }


            }

            public GetStudentAdapter(Context context, RecyclerView recyclerView, List<GetStudentModel> getStudentList) {
                this.getStudentList = getStudentList;
                this.recyclerView = recyclerView;
                this.context = context;
            }

            @Override
            public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                View itemView = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.select_student_list_item, parent, false);
                return new MyViewHolder(itemView);
            }

            @Override
            public void onBindViewHolder(final MyViewHolder holder, final int position) {
                holder.textStudentName.setText(getStudentList.get(position).getName());
                holder.rbSelect.setChecked(getStudentList.get(position).isSelected());
                holder.rbSelect.setTag(position); // This line is important.
                holder.rbSelect.setOnClickListener(onStateChangedListener(holder.rbSelect, position));

            }

            @Override
            public int getItemCount() {
                return getStudentList.size();
            }
            private View.OnClickListener onStateChangedListener(final RadioButton checkBox, final int position) {
                return new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (checkBox.isChecked()) {
                            for (int i = 0; i < getStudentList.size(); i++) {

                                getStudentList.get(i).setSelected(false);

                            }
                            getStudentList.get(position).setSelected(checkBox.isChecked());

                            notifyDataSetChanged();
                        } else {

                        }

                    }
                };
            }

        }

Ответ 9

Это происходит потому, что RecyclerView, как следует из названия, делает хорошую работу в переработке своих ViewHolders. Это означает, что каждый ViewHolder, когда он исчезает из поля зрения (на самом деле, он требует немного больше, чем скрывается из виду, но имеет смысл упростить его таким образом), он перерабатывается; это означает, что RecyclerView принимает этот ViewHolder, который уже завышен и заменяет его элементы элементами другого элемента в вашем наборе данных.

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

Вот почему вы должны сохранить внутреннюю переменную в вашем адаптере, которая запоминает состояние выбора каждого элемента. Таким образом, в методе onBindViewHolder вы можете узнать, был ли выбран элемент, чей ViewHolder в данный момент привязан или нет, и измените вид соответственно, в этом случае ваше состояние RadioButton (хотя я бы предложил использовать CheckBox, если вы план выбора нескольких элементов).

Если вы хотите узнать больше о RecyclerView и его внутренней работе, я приглашаю вас проверить FancyAdapters, проект, который я начал на GitHub, Это набор адаптеров, которые реализуют выбор, drag & drop элементов и салфетки, чтобы уволить возможности. Возможно, проверив код, вы сможете получить хорошее представление о том, как работает RecyclerView.

Ответ 10

Этот простой работал у меня

private RadioButton lastCheckedRB = null;
...
@Override
public void onBindViewHolder(final CoachListViewHolder holder, final int position) {
  holder.priceRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        RadioButton checked_rb = (RadioButton) group.findViewById(checkedId);
        if (lastCheckedRB != null) {
            lastCheckedRB.setChecked(false);
        }
        //store the clicked radiobutton
        lastCheckedRB = checked_rb;
    }
});

Ответ 11

Я хочу поделиться тем, чего достиг, возможно, это кому-то поможет. приведенный ниже код cardview(cvAddress) из приложения, чтобы выбрать адрес из списка адресов, отображаемых в просмотре cardview(cvAddress), чтобы при щелчке на конкретном item(cardview) imageView внутри элемента должен был установить другой ресурс (выбрать/отменить выбор)

    @Override
public void onBindViewHolder(final AddressHolder holder, final int position)
{
    holderList.add(holder);
    holder.tvAddress.setText(addresses.get(position).getAddress());
    holder.cvAddress.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            selectCurrItem(position);
        }
    });
}

private void selectCurrItem(int position)
{
    int size = holderList.size();
    for(int i = 0; i<size; i++)
    {
        if(i==position)
            holderList.get(i).ivSelect.setImageResource(R.drawable.select);
        else
            holderList.get(i).ivSelect.setImageResource(R.drawable.unselect);
    }
}

Я не знаю, это лучшее решение или нет, но это сработало для меня.

Ответ 12

Вот так выглядит класс Adapter:

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewHolder>{

Context context;
ArrayList<RouteDetailsFromFirestore> routeDetailsFromFirestoreArrayList_;
public int  lastSelectedPosition=-1;


public MyRecyclerViewAdapter(Context context, ArrayList<RouteDetailsFromFirestore> routeDetailsFromFirestoreArrayList)
{
    this.context = context;
    this.routeDetailsFromFirestoreArrayList_ = routeDetailsFromFirestoreArrayList;
}

@NonNull
@Override
public MyRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i)
{
    // LayoutInflater layoutInflater = LayoutInflater.from(mainActivity_.getBaseContext());
    LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
    View view = layoutInflater.inflate(R.layout.route_details, viewGroup, false);
    return new MyRecyclerViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull final MyRecyclerViewHolder myRecyclerViewHolder, final int i) {

    /* This is the part where the appropriate checking and unchecking of radio button happens appropriately */
    myRecyclerViewHolder.mRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            if(b) {
                if (lastSelectedPosition != -1) {
     /* Getting the reference to the previously checked radio button and then unchecking it.lastSelectedPosition has the index of the previously selected radioButton */  
                    //RadioButton rb = (RadioButton) ((MainActivity) context).linearLayoutManager.getChildAt(lastSelectedPosition).findViewById(R.id.rbRadioButton);
                    RadioButton rb = (RadioButton) ((MainActivity) myRecyclerViewHolder.mRadioButton.getContext()).linearLayoutManager.getChildAt(lastSelectedPosition).findViewById(R.id.rbRadioButton);
                    rb.setChecked(false);
                }
                    lastSelectedPosition = i;
                    /* Checking the currently selected radio button */
                    myRecyclerViewHolder.mRadioButton.setChecked(true);
                }
        }
    });
}

@Override
public int getItemCount() {
    return routeDetailsFromFirestoreArrayList_.size();
}
} // End of Adapter Class

Внутри MainActivity.java мы называем ctor класса Adapter следующим образом. Переданный контекст имеет MainActivity для ctor адаптера:

myRecyclerViewAdapter = new MyRecyclerViewAdapter(MainActivity.this, routeDetailsList);

Ответ 13

Следующие слова могут быть полезны для RecyclerView с единственным выбором.

Три шага для этого, 1) Объявите глобальную целочисленную переменную,

private int mSelectedItem = -1;

2) в onBindViewHolder

 mRadio.setChecked(position == mSelectedItem);

3) в onClickListener

mSelectedItem = getAdapterPosition();
notifyItemRangeChanged(0, mSingleCheckList.size());
mAdapter.onItemHolderClick(SingleCheckViewHolder.this);

Ответ 14

Пожалуйста, попробуйте это... Это работает для меня..

В адаптере возьмите разреженный булев массив.

SparseBooleanArray sparseBooleanArray;

В конструкторе инициализируйте это,

   sparseBooleanArray=new SparseBooleanArray();

В держателе связывания добавьте

@Override
public void onBindViewHolder(DispositionViewHolder holder, final int position) {
   holder.tv_disposition.setText(dispList.get(position).getName());
    if(sparseBooleanArray.get(position,false))
    {
        holder.rd_disp.setChecked(true);
    }
    else
    {
        holder.rd_disp.setChecked(false);


    }
    setClickListner(holder,position);
}

private void setClickListner(final DispositionViewHolder holder, final int position) {
    holder.rd_disp.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            sparseBooleanArray.clear();
            sparseBooleanArray.put(position, true);
            notifyDataSetChanged();


        }
    });
}

rd_disp - это переключатель в файле xml.

Поэтому, когда просмотр ресайклера загружает элементы, в bindView Holder он проверяет, содержит ли sparseBooleanArray значение "true", соответствующее его положению.

Если возвращаемое значение истинно, мы устанавливаем выбор переключателя true.Else мы устанавливаем значение false. В onclickHolder я очистил sparseArray и установил значение true, соответствующее этой позиции. Когда я вызываю notify datasetChange, он снова вызывает onBindViewHolder, и условие снова проверяется. Это делает наш выбор только для выбора определенного радио.

Ответ 15

Итак, потратив так много дней на это, я пришел к тому, что сработало, и это хорошая практика,

  1. Создайте интерфейс, назовите его "listener": SomeSelectedListener.
  2. Добавьте метод, который принимает целое число: void onSelect (int position);
  3. Инициализируйте прослушиватель в конструкторе адаптера утилиты как: a) сначала объявите глобально как: private SomeSelectedListener listener b) затем в конструкторе инициализируйте как: this.listener = listener;
  4. Внутри onClick() флажка внутри onBindViewHolder(): обновите метод интерфейса/слушателя, передав позицию в виде: listener.onSelect(position)
  5. В модели добавьте переменную для отмены выбора, скажем, mSelectedConstant и инициализируйте ее там равной 0. Это представляет состояние по умолчанию, когда ничего не выбрано.
  6. Добавьте геттер и сеттер для mSelectedConstant в той же модели.
  7. Теперь перейдите к вашему фрагменту/деятельности и реализуйте интерфейс слушателя. Затем переопределите его метод: onSelect (int position). В этом методе итерируйте свой список, который вы передаете адаптеру, используя цикл for, и установите дляSelectedConstant значение 0 для всех:

код

@Override
public void onTicketSelect(int position) {
for (ListType listName : list) {
    listName.setmSelectedConstant(0);
}

8. За пределами этого сделайте выбранную позицию постоянной 1:

код

list.get(position).setmSelectedConstant(1);
  1. Сообщите об этом изменении, вызвав: adapter.notifyDataSetChanged(); сразу после этого.
  2. Последний шаг: вернитесь к своему адаптеру и обновите внутри onBindViewHolder() после того, как onClick() добавит код для обновления состояния флажка,

код

if (listVarInAdapter.get(position).getmSelectedConstant() == 1) {
    holder.checkIcon.setChecked(true);
    selectedTicketType = dataSetList.get(position);} 
else {
    commonHolder.checkCircularIcon.setChecked(false);
}