I'm writing an Android app that has a ListView with CheckedTextView items in it. It's basically a question with a variable number of answers. If you select one answer you can press "next" and see the next question.
I've written all the code to save the answers and go to the next questions, and I've also provided the user with a "previous" button to go back to the previous question. If the previous question already has an answer, I want that answer to be selected. And that's where the problems start.
Right now, I can get the position of the selected answer and I call listView.setItemChecked(pos, true)
but the radio button is not selected.
if(selectedAnswer != null) {
int pos = mAnswerAdapter.getPosition(selectedAnswer);
if(pos != -1) {
listAnswers.setItemChecked(pos, true);
}
}
Only when I do something else, like drag down the statusbar does the view seem to refresh and draw the selected state of the radiobutton.
I fill the list with answers like this:
mAnswerAdapter = new AnswerArrayAdapter(getContext(), R.layout.listitem_answer, currentQuestion.getAnswers(), user.getLanguage());
listAnswers.setAdapter(mAnswerAdapter);
mAnswerAdapter.notifyDataSetChanged();
For reference:
The list_item layout:
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/listPreferredItemHeightSmall"
android:drawableStart="@drawable/questionnaire_answer_radio"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:id="@+id/questionnaire_answer_checkbox"/>
The adapter:
package be.iminds.mresist;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import java.util.List;
import be.iminds.mresist.models.QuestionnaireDefinition;
import butterknife.BindView;
import butterknife.ButterKnife;
public class AnswerArrayAdapter extends ArrayAdapter<QuestionnaireDefinition.Answer> {
private String mLang;
private Context mContext;
public AnswerArrayAdapter(Context context, int textViewResourceId, List<QuestionnaireDefinition.Answer> answers, final String lang) {
super(context, textViewResourceId, answers);
this.mContext = context;
this.mLang = lang;
}
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AnswerArrayAdapter.ViewHolder holder = null;
QuestionnaireDefinition.Answer item = getItem(position);
if (convertView == null) {
convertView = inflater.inflate(R.layout.listitem_answer, parent, false);
holder = new AnswerArrayAdapter.ViewHolder(convertView);
convertView.setTag(holder);
} else
holder = (AnswerArrayAdapter.ViewHolder) convertView.getTag();
holder.answerText.setText(item.getAnswer(mLang));
return convertView;
}
static class ViewHolder {
@BindView(R.id.questionnaire_answer_checkbox) CheckedTextView answerText;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}
The relevant fragment code (first call to openQuestion happens onStart):
/**
* Opens the question with the corresponding index
* @param questionIdx
*/
private void openQuestion(int questionIdx)
{
currentQuestionIdx = questionIdx;
final Question currentQuestion = getCurrentQuestion();
txtQuestionTitleCounter.setText(String.format(getString(R.string.questionnaire_counter), questionIdx + 1, qAssignment.getDefinition().getQuestions().size()));
txtQuestion.setText(currentQuestion.getQuestion(user.getLanguage()));
mAnswerAdapter = new AnswerArrayAdapter(getContext(), R.layout.listitem_answer, currentQuestion.getAnswers(), user.getLanguage());
listAnswers.setAdapter(mAnswerAdapter);
mAnswerAdapter.notifyDataSetChanged();
//If it's not the last question and there are more questions than one, make the next button visible
if(!isLastQuestion(currentQuestionIdx) && qAssignment.getDefinition().getQuestions().size() > 1)
mNextButton.setVisibility(View.VISIBLE);
else
mNextButton.setVisibility(View.GONE);
//If it's not the first question and there are more questions than one, make the previous button visible
if(!isFirstQuestion(currentQuestionIdx) && qAssignment.getDefinition().getQuestions().size() > 1)
mPreviousButton.setVisibility(View.VISIBLE);
else
mPreviousButton.setVisibility(View.GONE);
//If the question's already been answered, fill in the answer
markPreviousAnswer(currentQuestion);
}
/**
* Marks a previously selected answer
* @param currentQuestion
*/
private void markPreviousAnswer(Question currentQuestion) {
if(qAssignment.getAnswerValues() != null && qAssignment.getAnswerValues().containsKey(currentQuestion.getQuestionKey())) {
//Value of answer
Integer value = qAssignment.getAnswerValues().get(currentQuestion.getQuestionKey());
QuestionnaireDefinition.Answer selectedAnswer = null;
for(QuestionnaireDefinition.Answer answer: currentQuestion.getAnswers()) {
if(answer.getValue() == value) {
selectedAnswer = answer;
break;
}
}
if(selectedAnswer != null) {
int pos = mAnswerAdapter.getPosition(selectedAnswer);
if(pos != -1) {
listAnswers.setItemChecked(pos, true);
}
}
}
}
CheckedTextView selector drawable:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="oval">
<size android:width="16dp" android:height="16dp"/>
<solid android:color="@color/colorPrimaryDarker"/>
<stroke android:color="@android:color/white" android:width="2dp"/>
</shape>
</item><!-- pressed -->
<item android:state_checked="true">
<shape android:shape="oval">
<size android:width="16dp" android:height="16dp"/>
<solid android:color="@color/colorPrimaryDarker"/>
<stroke android:color="@android:color/white" android:width="2dp"/>
</shape>
</item> <!-- checked -->
<item>
<shape android:shape="oval" android:innerRadius="10dp">
<size android:width="16dp" android:height="16dp"/>
<stroke android:color="@android:color/white" android:width="1dp"/>
</shape>
</item> <!-- default -->
</selector>
I think you should call notifyDataSetChanged()
after setting the answer to true so that the ListView
can refresh its views immediatly
int pos = mAnswerAdapter.getPosition(selectedAnswer);
if(pos != -1) {
listAnswers.setItemChecked(pos, true);
mAnswerAdapter.notifyDataSetChanged()
}
For the simple checkbox, the attribute button will be used:
android:button="@drawable/questionnaire_answer_radio"
And for the CheckedTextView use the attribute checkMark:
android:checkMark="@drawable/questionnaire_answer_radio"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With