List of questions, each question with a list of answers

3

I'm implementing a question and answer application.

For my listing I'm using a RecyclerView where I load several CardViews (from loading a list). Each CardView shows one question. Below the question text I want to show my list of alternatives. There is no limit on alternatives, so I will not be able to leave this fixed in my layout.

This is where the problem lies. I can load the CardViews with the question text but I still can not load the list of alternatives, even though I can print them on the Console just after loading each question.

I do not know if I can explain very well just with the words, so I'll show you how my structure is and what I wanted to do. The image below shows how my application is and what I've already been able to do:

TheimagebelowshowswhatIneedtodonow(whichiswhyIamaskingthisquestion).

Inanother question I asked I was asked to insert another RecyclerView into the layout of my CardView. I tried to accomplish this but when I run the application I get a lot of errors, as it shows the print I took from the Console:

AnotherStackOverflowuserneededto something similar and also encountered these errors. It was instructed to specify the height of the RecyclerView, which I also did but the errors continue.

I should also mention how my framework is for loading the CardViews.

As I'm using RecyclerView, I had to create an Adapter class that extends from the RecyclerView.Adapter class. To load the cards I had to "inflate" it in a method called onCreateViewHolder:

@Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    View v = mLayoutInflater.inflate(R.layout.card_perguntas, viewGroup, false);
    MyViewHolder mvh = new MyViewHolder(v);

    return mvh;
}

After performing this loading you can set the values in each component in the onBindViewHolder method:

@Override
public void onBindViewHolder(MyViewHolder myViewHolder, int position) {
    myViewHolder.tvPergunta.setText(mList.get(position).getTextoPergunta());
}

Well. After all this explanation (hopefully I got it right because I suck at it), it remains to finish saying that I need to present a list of alternatives just below the text in which I display the question. This list is not fixed. There may be two, three or more alternatives.

I'm here, and thanks to all who have the patience to have all this until the end =) o /

    
asked by anonymous 27.11.2015 / 20:56

3 answers

3

In fact use a scrollable view within another scrollable view behind problems.
Maybe that's why Android provides the ExpandableListView component that more is not what a ListView whose items are lists. So one solution to your problem would be to use one.

Define two classes, one to represent the question and another to represent the answer:

Question.java
Responsible for saving the question text, the answers, the correct answer and the selected answer.

public class Question {

    private final String questionText;
    private ArrayList<Answer> answers;
    private int selectedAnswer = -1;
    private int correctAnswer = -1;

    public Question(String text) {
        this.questionText = text;
        answers = new ArrayList<>();
    }

    public boolean isCorrectlyAnswered(){
        return correctAnswer == getSelectedAnswer();
    }

    public int getSelectedAnswer() {
        return selectedAnswer;
    }

    public void setSelectedAnswer(int selectedAnswer) {
        this.selectedAnswer = selectedAnswer;
    }


    public void setCorrectAnswer(int correctAnswer) {
        if(answers.size() <= correctAnswer){
            throw new IllegalArgumentException();
        }
        this.correctAnswer = correctAnswer;
    }

    public void addAnswer(String text){
        answers.add(new Answer(text));
    }

    public String getText(){
        return questionText;
    }

    public ArrayList<Answer> getAnswers(){
        return answers;
    }
}

Answer.java
Responsible for saving the text of the response.

public class Answer {

    private final String answerText;

    public Answer(String text){
        this.answerText = text;
    }

    public String getText() {
        return answerText;
    }

}  

The way to handle an ExpandableListView is similar to that of a ListView . The difference is that in addition to an Adpater and a view for items ( group ) it is necessary to define a view for the sub-items ( children ).

question_row.xml View for the questions, represents each of the ExpandableListView groups. Contains the question text and a sign to indicate if the question was answered correctly.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="75dp"
        card_view:cardCornerRadius="4dp">

        <TextView
            android:id="@+id/questionText"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="questionText"/>
        <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/correctMark"
            android:layout_gravity="right"
            android:clickable="false"
            android:focusable="false"
            android:checked="true"
            android:visibility="gone"/>
    </android.support.v7.widget.CardView>

</LinearLayout>

answer_row.xml View for responses, represents each of the sub items within each ExpandableListView group. Contains a RadioButton with the response text.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp">

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New RadioButton"
        android:id="@+id/answerText"
        android:clickable="false"
        android:focusable="false"/>
</LinearLayout>

ExpandListAdapter.java
Responsible for bridging the data and the ExpandableListView

public class ExpandListAdapter extends BaseExpandableListAdapter {

    private final LayoutInflater inflater;;
    private ArrayList<Question> questions;

    public ExpandListAdapter(Context context, ArrayList<Question> questions){
        inflater = LayoutInflater.from(context);
        this.questions = questions;
    }

    @Override
    public int getGroupCount() {
        return questions.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        ArrayList<Answer> answers = questions.get(groupPosition).getAnswers();
        if(answers == null){
            return 0;
        }
        return questions.get(groupPosition).getAnswers().size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return questions.get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return questions.get(groupPosition).getAnswers().get(childPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {

        View row = convertView;
        ViewHolder holder;

        if (row == null)
        {
            row = inflater.inflate(R.layout.question_row, parent, false);
            holder = new ViewHolder();
            holder.addView(row.findViewById(R.id.questionText))
                  .addView(row.findViewById(R.id.correctMark));
            row.setTag(holder);
        }else{
            holder = (ViewHolder) row.getTag();
        }

        TextView questionText = (TextView) holder.getView(R.id.questionText);
        CheckBox correctMark = (CheckBox) holder.getView(R.id.correctMark);
        Question question = questions.get(groupPosition);

        if(question.isCorrectlyAnswered()) {
            correctMark.setVisibility(View.VISIBLE);
        }else {
            correctMark.setVisibility(View.GONE);
        }

        questionText.setText(question.getText());
        return row;

    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

        View row = convertView;
        ViewHolder holder;

        if (row == null)
        {
            row = inflater.inflate(R.layout.answer_row, parent, false);
            holder = new ViewHolder();
            holder.addView(row.findViewById(R.id.answerText));
            row.setTag(holder);
        }else{
            holder = (ViewHolder) row.getTag();
        }

        RadioButton answerText = (RadioButton) holder.getView(R.id.answerText);

        if(questions.get(groupPosition).getSelectedAnswer() == childPosition){
            answerText.setChecked(true);
        }else{
            answerText.setChecked(false);
        }
        answerText.setText(questions.get(groupPosition).getAnswers().get(childPosition).getText());

        return row;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    static class ViewHolder
    {
        private HashMap<Integer, View> storedViews = new HashMap<Integer, View>();

        public ViewHolder addView(View view)
        {
            int id = view.getId();
            storedViews.put(id, view);
            return this;
        }

        public View getView(int id)
        {
            return storedViews.get(id);
        }
    }
}

Only Activity is missing to display the list of questions and answers.

activity_main-xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin">

    <TextView
        android:id="@+id/textView"
        android:text="Questionário"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="32dp"/>

    <ExpandableListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/expandableListView"
        android:layout_below="@+id/textView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>

MainActivity.java

Public class MainActivity extends AppCompatActivity {

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

        //Obtém a referência da ExpandableListView
        ExpandableListView expListView = (ExpandableListView) findViewById(R.id.expandableListView);

        //Obtém a lista de perguntas a ser apresentada
        final ArrayList<Question> questions = getQuestions();

        //Cria uma instância do adapter e atribui-o à ExpandableListView
        expListView.setAdapter(new ExpandListAdapter(this, questions));

        //Define um listener para tratar a selecção das respostas
        expListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {

            @Override
            public boolean onChildClick(ExpandableListView parent, View v,
                                        int groupPosition, int childPosition, long id) {

                //Indica que esta resposta foi seleccionada
                questions.get(groupPosition).setSelectedAnswer(childPosition);

                //Obtém a referência do adapter
                ExpandListAdapter adapter = (ExpandListAdapter) parent.getExpandableListAdapter();

                //Notifica o adapter da selecção para que ela seja representada visualmente
                adapter.notifyDataSetChanged();
                return true;
            }
        });
    }

    //Preenche um ArrayList com 10 perguntas e respectivas respostas
    private ArrayList<Question> getQuestions() {
        ArrayList<Question> questions = new ArrayList<>();
        Question question;
        int totalAnswers;
        Random rnd = new Random();

        for (int i = 1; i <= 10; i++){
            question = new Question("Question - " + i);
            //As perguntas pares têm 3 respostas as impar 5
            if(i%2 == 0){
                totalAnswers = 3;
            }else{
                totalAnswers = 5;
            }
            for (int j = 1; j <= totalAnswers; j++){
                question.addAnswer("Answer - " + j + " to question - " + i);
            }
            //Atribui aleatóriamente a resposta correcta
            question.setCorrectAnswer(rnd.nextInt(totalAnswers));
            questions.add(question);
        }

        return questions;
    }
}

Just adapt to your liking.

GitHubGist

    
28.11.2015 / 20:30
0

I've done something similar these days, hopefully I can help you. In my problem I add a number of textView's dynamically, this amount varies according to a size of an ArrayList. You can adapt to your problem. Follow the code

        for (int i = 0; i < u.getLanguage().size(); i++) {
            language.addView(createTextView(addLanguage.getContext(), u.getLanguage().get(i), i,new LanguagesFragment()));
        }

createTextView method:

 private TextView createTextView(Context context, String text, int tag, final android.support.v4.app.Fragment f) {
    Bundle b = new Bundle();
    b.putInt("Position",tag);
    f.setArguments(b);
    TextView t = new TextView(context);
    t.setText(text);
    t.setTag(tag);
    t.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.carreira_detail_container,f).addToBackStack(null).commit();
        }
    });
    return t;
}

Explaining: The main part that will work for you is the context that I am calling in the createTextView method, this context is used to reference where the textView I am creating will be added, in the example we have it will be added below addLanguage . Follow the xml:

 <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginLeft="30dp"
            android:id="@+id/containerEducation">
        <Button
            android:id="@+id/addEducation"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:text="@string/addEducation"
            android:textAllCaps="false"
            /></LinearLayout>

Note that in nonlinear layout I set the offset as vertical so only one element will appear per line in my case this element is the textView. The result is the list of languages that were added below the add language button, follow image:

    
28.11.2015 / 00:22
0

Place a Linear Layout inside the cardView or any layout that suits your needs. Do not use a Recycler View inside the CardView because the card_questions are inside a recycler view ja.

Take care not to put two components that have Scroll one inside the other.

    
28.11.2015 / 00:35