List of questions, each question with a list of answers

Asked

Viewed 1,444 times

4

I’m implementing a question and answer app.

For my listing I am using a Recyclerview in which I load several Cardviews (coming from loading a list). Each Cardview shows a question. Below the text of the question I want to show my list of alternatives. There is no limit to alternatives, so I can’t leave it fixed in my layout.

This is where the problem lies. I can load the Cardviews with the text of the questions but I still can’t load the list of alternatives, although I can print them in the Console right after loading each question.

I don’t know if I can explain it very well just with words, so I’m going to show you how my structure is and what I wanted to do. The image below shows how my app is and what I’ve been able to do:

Como está atualmente

The image below shows what I need to do now (which is why I’m realizing this question).

O que preciso fazer agora

In one another question I asked I was directed to insert another Recyclerview in my Cardview layout. I tried to accomplish this but when I run the application I get a lot of errors as it shows this print I took from the Console:

Erros Console

Another Stackoverflow user needed to do something similar and also came across these errors. He was told to specify the height of the Recyclerview, which I also did but the errors continue.

I should also mention how is my structure to perform the loading of Cardviews.

As I am using Recyclerview, I had to create an Adapter class that extends from the Recyclerview.Adapter. 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 this load it is possible to 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 (I hope I’ve managed to do it right, because I suck at it), I would like to finish by saying that I need to present a list of alternatives right below the text where I present the question. This list is not fixed. There may be two, three or more alternatives.

I stay here, and I thank all those who have the patience to have it all by the end =) o/

  • Recyclerview within Recyclerview never will be an alternative. Your list of alternatives actually has to be views loaded dynamically.

  • I’ll try to answer over the weekend.

  • I get it, @Androiderson. I’ll think of other approaches.

  • Thanks for being there for me, @ramaral. Really serious =)

  • Have you seen my answer? Answer what you want or need something more?

  • @ramaral Awesome guy. Thanks even for the help. I’m making some changes to the code to allow some extra features. I was going to post the code here before I marked your answers, but I think what I’m going to add escapes a little bit from the question I raised first. Anyway, I’ll complement this thread later with some information that might be useful to other people. Again, thank you very much!!!

Show 1 more comment

3 answers

3


In fact use a scrollable view inside of another scrollable view brings problems.
Maybe that’s why Android provides the component Expandablelistview which is no more than a Listview whose items are lists.
So a solution to your problem would be to use a.

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

Question.java
Responsible for saving the text of the question, 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 reply.

public class Answer {

    private final String answerText;

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

    public String getText() {
        return answerText;
    }

}  

The way to deal with a Expandablelistview is similar to that of a Listview. The difference is that beyond a Adpater and a view for the 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. Contains the text of the question and a sign to indicate whether the question has been correctly answered.

<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 the answers, it represents each of the sub-items within each of the Expandablelistview. Contains a Radiobutton with the text of the reply.

<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 gap between data and 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 left to submit 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 taste.

Githubgist

  • I had to adapt my code to be able to implement this suggestion but everything worked correctly. I had some doubts at first, but with the answer was so detailed that a little attention (and the collaboration of a friend who has helped me a lot) was enough that everything worked out. THANK YOU!!! I was already standing in the same place because of this question for weeks. Finally another step forward!!!

  • Glad you can help. This answer was also helpful to me because as I had never implemented a Expandablelistview, was a way to learn how to do it.

  • I posted a new question about the difficulty I’m facing manipulating the behavior of Radiobuttons. Is there any chance you could take a look and assist me? This link is: http://answall.com/questions/101676/marcar-desmar-componente-radiobutton-expandedlistview Abração!!!

1

I’ve done something similar these days, I hope I can help you. In my problem I dynamically add an amount of textView’s, 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 serve you is related to context I’m calling in the method createTextView, this context serves to reference where the textView I’m creating will be added, in the example we have it added below addLanguage. Follows 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 linear layout I set the horientação 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 have been added below the add Language button, follow picture:

inserir a descrição da imagem aqui

1

Place a Linear Layout inside the cardView or some Layout that is suitable to your need. Don’t use a Recycler View inside Cardview, because the card_questoes are inside a Recycler view ja.

Be careful not to put two components that have Scroll one inside the other.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.