问题
In my application am displaying 20 multiple choice questions with the help of RecyclerView
.
If I change the value of first RadioGroup
and scrolls down, again scrolls up removing the selected value in RecycelarView
and also i want to use that selected RadioButton
value further, I was also checked link1 but i did't understood what he is doing.
Here is my sample code snippet let me know if you need any clarification.
package com.testing.survey;
import java.util.List;
import android.annotation.SuppressLint;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.TextView;
@SuppressWarnings("rawtypes")
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private List<Student> stList;
public DataAdapter(List<Student> students) {
this.stList = students;
}
// Create new views
@Override
public DataAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, null);
// create ViewHolder
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
@SuppressLint("UseValueOf") @Override
public void onBindViewHolder(ViewHolder viewHolder,int position) {
final int pos = position;
viewHolder.tvQuestionNumber.setText(stList.get(position).getQuestionNumber());
viewHolder.tvQuestion.setText(stList.get(position).getQuestion());
viewHolder.rbAns1.setText(stList.get(position).getAnswer1());
viewHolder.rbAns2.setText(stList.get(position).getAnswer2());
viewHolder.rbAns3.setText(stList.get(position).getAnswer3());
viewHolder.rbAns4.setText(stList.get(position).getAnswer4());
viewHolder.rbAns5.setText(stList.get(position).getAnswer4());
//viewHolder.rgAnswers.clearCheck();
viewHolder.rgAnswers.check(stList.get(position).getSelectedRadioButtonId());
viewHolder.rgAnswers.setTag(new Integer(position));
Log.v("select"+position,stList.get(position).getSelectedRadioButtonId()+"");
viewHolder.rgAnswers.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
// TODO Auto-generated method stub
int radioButtonID = group.getCheckedRadioButtonId();
View radioButton = group.findViewById(radioButtonID);
int clickedPos = ((Integer)group.getTag()).intValue();
//Student contact=(Student)group.getTag();
//contact.setSelectedRadioButtonId(radioButtonID);
stList.get(clickedPos).setSelectedRadioButtonId(radioButtonID);
Log.v("hello"+clickedPos,stList.get(clickedPos).getSelectedRadioButtonId()+"");
}
});
}
// Return the size arraylist
@Override
public int getItemCount() {
return stList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvQuestionNumber;
public TextView tvQuestion;
public RadioGroup rgAnswers;
public RadioButton rbAns1,rbAns2,rbAns3,rbAns4,rbAns5;
public Student singlestudent;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
tvQuestionNumber = (TextView) itemLayoutView.findViewById(R.id.tvQuestionNumber);
tvQuestion = (TextView) itemLayoutView.findViewById(R.id.tvQuestion);
rgAnswers=(RadioGroup)itemLayoutView.findViewById(R.id.rgAnswers);
rbAns1=(RadioButton)itemLayoutView.findViewById(R.id.rbAnswer1);
rbAns2=(RadioButton)itemLayoutView.findViewById(R.id.rbAnswer2);
rbAns3=(RadioButton)itemLayoutView.findViewById(R.id.rbAnswer3);
rbAns4=(RadioButton)itemLayoutView.findViewById(R.id.rbAnswer4);
rbAns5=(RadioButton)itemLayoutView.findViewById(R.id.rbAnswer5);
}
}
// method to access in activity after updating selection
public List<Student> getStudentist() {
return stList;
}
}
回答1:
I created a sample code that will work as per your req.
First we have to create the xml as follows :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerViewAppList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout>
Then we will create an interface as follows
public interface OnOptionSelected {
public void onOptionSelected(int position,int itemSelected);
}
Then we will create the model class as follows :
public class QuestionModel {
private String question;
private int seleectedAnswerPosition;
private boolean op1Sel,op2Sel,op3Sel; // options
public boolean isOp1Sel() {
return op1Sel;
}
public void setOp1Sel(boolean op1Sel) {
this.op1Sel = op1Sel;
if(op1Sel){ // To make sure only one option is selected at a time
setOp2Sel(false);
setOp3Sel(false);
}
}
public boolean isOp2Sel() {
return op2Sel;
}
public void setOp2Sel(boolean op2Sel) {
this.op2Sel = op2Sel;
if(op2Sel){
setOp1Sel(false);
setOp3Sel(false);
}
}
public boolean isOp3Sel() {
return op3Sel;
}
public void setOp3Sel(boolean op3Sel) {
this.op3Sel = op3Sel;
if(op3Sel){
setOp2Sel(false);
setOp1Sel(false);
}
}
public int getSeleectedAnswerPosition() {
return seleectedAnswerPosition;
}
public void setSeleectedAnswerPosition(int seleectedAnswerPosition) {
this.seleectedAnswerPosition = seleectedAnswerPosition;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
}
Then we will create the view for row :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/question"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:id="@+id/radoptionOne"
android:text="May Be"
android:checked="false"
android:saveEnabled="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:checked="false"
android:id="@+id/radoptionTwo"
android:text="NO"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:checked="false"
android:id="@+id/radoptionThree"
android:text="Yes"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Adapter class as follows:
public class QuestionAdapter extends RecyclerView.Adapter<QuestionAdapter.ViewHolder> {
private List<QuestionModel> questionModels;
public void setOnOptionSelected(OnOptionSelected onOptionSelected) {
this.onOptionSelected = onOptionSelected;
}
private OnOptionSelected onOptionSelected;
public List<QuestionModel> getQuestionModels() {
return questionModels;
}
public void setQuestionModels(List<QuestionModel> questionModels) {
this.questionModels = questionModels;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView question;
RadioGroup radioGroup;
RadioButton op1, op2, op3;
ViewHolder(View view) {
super(view);
question = (TextView) view.findViewById(R.id.question);
//radioGroup=(RadioGroup)view.findViewById(R.id.radGroup);
op1 = (RadioButton) view.findViewById(R.id.radoptionOne);
op2 = (RadioButton) view.findViewById(R.id.radoptionTwo);
op3 = (RadioButton) view.findViewById(R.id.radoptionThree);
op1.setOnClickListener(this);
op2.setOnClickListener(this);
op3.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.radoptionOne:
onOptionSelected.onOptionSelected(getAdapterPosition(), 1);
break;
case R.id.radoptionTwo:
onOptionSelected.onOptionSelected(getAdapterPosition(), 2);
break;
case R.id.radoptionThree:
onOptionSelected.onOptionSelected(getAdapterPosition(), 3);
break;
}
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
// create a normal view
v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_view, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.question.setText(questionModels.get(position).getQuestion());
Log.e("POSITION" + position, "1" + questionModels.get(position).isOp1Sel());
Log.e("POSITION" + position, "2" + questionModels.get(position).isOp2Sel());
Log.e("POSITION" + position, "3" + questionModels.get(position).isOp3Sel());
viewHolder.op1.setChecked(questionModels.get(position).isOp1Sel());
viewHolder.op2.setChecked(questionModels.get(position).isOp2Sel());
viewHolder.op3.setChecked(questionModels.get(position).isOp3Sel());
}
@Override
public int getItemCount() {
if (questionModels != null) {
return questionModels.size();
}
return 0;
}
}
Then the Activity class :
public class MainActivity extends Activity implements OnOptionSelected{
private RecyclerView mRecyclerView;
private List<QuestionModel> questionModels;
private QuestionAdapter questionAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView =(RecyclerView)findViewById(R.id.recyclerViewAppList);
mRecyclerView.setHasFixedSize(true);
questionModels=new ArrayList<QuestionModel>();
for (int i=0;i<20;i++)
{
QuestionModel questionModel=new QuestionModel();
questionModel.setQuestion("Question " + (i + 1));
questionModels.add(questionModel);
}
questionAdapter =new QuestionAdapter();
questionAdapter.setOnOptionSelected(this);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
questionAdapter.setQuestionModels(questionModels);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setAdapter(questionAdapter);
}
@Override
public void onOptionSelected(int position, int itemSelected) {
questionModels.get(position).setSeleectedAnswerPosition(itemSelected);
switch (itemSelected){
case 1:
questionModels.get(position).setOp1Sel(true);
break;
case 2:
questionModels.get(position).setOp2Sel(true);
break;
case 3:
((QuestionModel)questionModels.get(position)).setOp3Sel(true);
break;
}
questionAdapter.setQuestionModels(questionModels);
questionAdapter.notifyDataSetChanged();
// mRecyclerView.setAdapter(questionAdapter);
}
}
I tried the solution with Radiogroup but it was not working properly( sometime it was retaining the old value). Then I changed the way of the selecting options in my model class.
回答2:
This problem occurs because of a bug in either RecyclerView
or somewhere in the Android SDK. Anyway, I took the advice from another SO question - onCheckedChanged called automatically.
For RadioGroup
, it will be a little different. There are two ways to go about. I recommend 1st one since it is plug-n-play.
set
OnCheckedChangeListener
onRadioGroup
:mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { if(checkedId == -1) { Log.v("onCheck", "Android bug since RadioButton doesn't get unchecked normally!"); } else { Log.v("onCheck", "Valid click. By user"); mMyListObjectArr[position].setChecked(checkedId); } } });
or set
onCheckedChangeListener
on allRadioButton
s inside theRadioGroup
:CompoundButton.OnCheckedChangeListener onCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(buttonView.isPressed()) { Log.v("onCheck", position + ", valid click by user"); mMyListObjectArr[position].setChecked(buttonView.getId()); } else { Log.v("onCheck", "Android bug"); } } }; mRadioButton1.setOnCheckedChangeListener(onCheckedChangeListener); mRadioButton2.setOnCheckedChangeListener(onCheckedChangeListener);
回答3:
Modify your onBindViewHolder()
to this
public void onBindViewHolder(ViewHolder viewHolder, int position) {
final int pos = position;
viewHolder.tvQuestionNumber.setText(stList.get(position).getQuestionNumber() + "");
viewHolder.tvQuestion.setText(stList.get(position).getQuestion());
viewHolder.rbAns1.setText(stList.get(position).getAnswer1());
viewHolder.rbAns2.setText(stList.get(position).getAnswer2());
viewHolder.rbAns3.setText(stList.get(position).getAnswer3());
viewHolder.rbAns4.setText(stList.get(position).getAnswer4());
viewHolder.rbAns5.setText(stList.get(position).getAnswer5());
//viewHolder.rgAnswers.clearCheck();
viewHolder.rgAnswers.setTag(position);
Log.v("select" + position, stList.get(position).getSelectedRadioButtonId() + "");
viewHolder.rgAnswers.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
// TODO Auto-generated method stub
int radioButtonID = group.getCheckedRadioButtonId();
View radioButton = group.findViewById(radioButtonID);
int clickedPos = (Integer) group.getTag();
stList.get(clickedPos).setSelectedRadioButtonId(radioButtonID);
// if you want to get selected button's info such as tag, text... etc.
RadioButton radioButton = (RadioButton) viewHolder.itemView.findViewById(radioButtonID);
if(radioButton != null) {
String customTag = radioButton.getTag().toString();
stList.get(clickedPos).setCustomTag(customTag);
}
Log.v("hello" + clickedPos, stList.get(clickedPos).getSelectedRadioButtonId() + "");
}
});
viewHolder.rgAnswers.check(stList.get(position).getSelectedRadioButtonId());
}
It should work. If any issues please let me know.
回答4:
You can do it in this way.....
inside your bean class add one field say
String final_answer;
public String getFinalAnswer(){
return final_answer;
}
public void setFinalAnswer(String final_answer){
this.final_answer = final_answer;
}
in adapter.....
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
final Student student = stList.get(position);
viewHolder.tvQuestionNumber.setText(student.getQuestionNumber());
viewHolder.tvQuestion.setText(student.getQuestion());
viewHolder.rbAns1.setText(student.getAnswer1());
viewHolder.rbAns2.setText(student.getAnswer2());
viewHolder.rbAns3.setText(student.getAnswer3());
viewHolder.rbAns4.setText(student.getAnswer4());
viewHolder.rbAns5.setText(student.getAnswer4());
viewHolder.rgAnswers.clearCheck();
if(student.getSelectedRadioButtonId()!=null)
viewHolder.rgAnswers.check(student.getSelectedRadioButtonId());
else
viewHolder.rgAnswers.clearCheck();
viewHolder.rgAnswers.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if(checkedId != -1){
student.setSelectedRadioButtonId(checkedId);
RadioButton rb = (RadioButton)viewHolder.rgAnswers.
findViewById(checkedId);
student.setFinalAnswer(rb.getText);
stList.get(position) = student;
}
}
});
}
To get selected answers finally...write this code in main activity
public void getSelectedAnswers(){
for(int i=0;i<slist.length;i++){
//get selected answer
Log.i("answer",slist.get(i).getFinalAnswer());
}
}
回答5:
The problem is that the recycler view is recycling (like the name says) your views.
The system creates a certain amount of ViewHolders to fill your screen after reaching that amount it reuses the already existent ViewHolders.
If you set the button checked in your first listentry and it reuses the ViewHolder for your 7th listentry the button is still checked(because it got set in the ViewHolder).
To fix this problem you need to set the default appearance for your listentries in onBindViewHOlder every time.
Update:
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
final Student student = stList.get(position);
viewHolder.tvQuestionNumber.setText(student.getQuestionNumber());
viewHolder.tvQuestion.setText(student.getQuestion());
viewHolder.rbAns1.setText(student.getAnswer1());
viewHolder.rbAns2.setText(student.getAnswer2());
viewHolder.rbAns3.setText(student.getAnswer3());
viewHolder.rbAns4.setText(student.getAnswer4());
viewHolder.rbAns5.setText(student.getAnswer4());
viewHolder.rgAnswers.clearCheck();
viewHolder.rgAnswers.check(student.getSelectedRadioButtonId());
viewHolder.rgAnswers.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
student.setSelectedRadioButtonId(checkedId);
Log.v("hello"+position,checkedId+"");
}
});
}
I slightly updated your new approach, maybe this helps ?
回答6:
As i have seen your code...
just add single line
viewHolder.rgAnswers.setOnCheckedChangeListener(null);
in onBindViewHolder()
method above of this line
viewHolder.rgAnswers.check(stList.get(position).getSelectedRadioButtonId());
...
if it solved your problem ..let me know ... i will explain you a scenario why its happening .. Because i faced similer problem and solved using this solution.
回答7:
In your model class add
private int checkedId = -1;
public int getCheckedId() {
return checkedId;
}
public void setCheckedId(int checkedId) {
this.checkedId = checkedId;
}
and change onBindViewHolder in adapter like this:
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.tvQuestion.setText(stList.get(position).getQuestion());
viewHolder.rbAns1.setText(stList.get(position).getAnswer1());
viewHolder.rbAns2.setText(stList.get(position).getAnswer2());
viewHolder.rbAns3.setText(stList.get(position).getAnswer3());
viewHolder.rbAns4.setText(stList.get(position).getAnswer4());
viewHolder.rbAns5.setText(stList.get(position).getAnswer5());
viewHolder.radGrp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup rgp, int checkedId) {
// TODO Auto-generated method stub
stList.get(position).setCheckedId(rgp.getCheckedRadioButtonId());
}
});
viewHolder.radGrp.check(stList.get(position).getCheckedId());
}
回答8:
Inside your student class add a boolean variable isSelected. Now on checkedChangeListener of each radiogroup, change the value of isSelected to mark whether some value has been selected or no selection has been made. Now inside onBindViewHolder() just do
if(stList.get(position).isSelected){
viewHolder.rgAnswers.check(stList.get(position).getSelectedRadioButtonId());
}else{
viewHolder.rgAnswers.clearCheck();
}
At the end you can iterate over the whole list and check for which objects, isSelected is true
回答9:
RecyclerView came over ListView and its major property that is It Reuses cells while scrolling up and down. Due to this it do not flick while scrolling even if you have lots of data in your list. You are getting problem due to Reuse of cells in RecyclerView.
You can overcome to this problem by binding your RecyclerView list data to Modal class with getter and setter method.
You can display checkbox
checked or unchecked on basis of cell position and corresponding data.
In my sample i used setOnClickListener
instead of setOnCheckedChangeListenerto
to show checkbox checked or unchecked.
来源:https://stackoverflow.com/questions/34266871/selecting-one-radiobutton-value-and-scrolling-back-removing-the-selected-one-in