Engels Immanuel
Engels Immanuel

Reputation: 113

Issues adding custom layout within each Recycler view

I am building an app where publishers upload questions to database. Publishers can add as many questions they want, so i used a recycler view to handle that. When publisher clicks the button to add question a new layout is inflated as specified in my recyclerview adapter. TopicQuestionsAdapter is the name of my recycler view adapter

                             ...

RecyclerView addTopicOfTheDayWhereDynamicLayoutsWillBeAdded;
TopicQuestionsAdapter topicQuestionsAdapter;
ArrayList<TopicQuestions> topicQuestionsList;

addTopicOfTheDayWhereDynamicLayoutsWillBeAdded.setHasFixedSize(true);
addTopicOfTheDayWhereDynamicLayoutsWillBeAdded.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
topicQuestionsList = new ArrayList<>();

                              ...

addTopicOfTheDayAddQuiz.setOnClickListener(v29 -> {

                              ...

            // dynamically add questions layout on subsequent clicks
            // here with an x button in each layout to remove the
            // entire question layout

            topicQuestionsList.add(new TopicQuestions());
            topicQuestionsAdapter = new TopicQuestionsAdapter("Adapter", getContext(), topicQuestionsList);
            addTopicOfTheDayWhereDynamicLayoutsWillBeAdded.setAdapter(topicQuestionsAdapter);
            topicQuestionsAdapter.notifyItemInserted(topicQuestionsList.size());

        }
    });

Now the recycler view layout consists of four FABs to add the different types of questions and options, one textview and one image button to remove the whole view

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopicQuestionsViewHolder {
//R.layout.add_question_layout is the layout
    val itemView = LayoutInflater.from(parent.context).inflate(R.layout.add_question_layout, parent, false)
    return TopicQuestionsViewHolder(itemView)
}

/*this is where my problem is i observed that whenever i
    add a new question, the layouts i had already added to previous
    views get cleared. Recycler view destroys states*/

 override fun onBindViewHolder(holder: TopicQuestionsViewHolder, position: Int) {
    
    // the textview to show this question number
    holder.questionNumber.text = "${position + 1}."

    // the image button to remove the whole view
    holder.removeQuestion.setOnClickListener {
        topicQuestions.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, topicQuestions.size)
    }

    holder.addField.setOnClickListener {
        // need to fix this
        when (holder.theFields.visibility == View.GONE) {
            true -> {
                holder.theFields.visibility = View.VISIBLE
                holder.addField.setImageResource(R.drawable.close)
                Log.wtf(TAG, "true")
            }
            false -> {
                holder.theFields.visibility = View.GONE
                holder.addField.setImageResource(R.drawable.add)
                Log.wtf(TAG, "false")
            }
        }
    }

    // this button would add a question field to this view
    holder.addQuestionField.setOnClickListener {
        val lParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
        lParams.topMargin = 16
        val view = LayoutInflater.from(ctx).inflate(R.layout.add_question_field_to_question_layout, null)
        view.layoutParams = lParams
        holder.toAddViews.addView(view)
    }

    // this button would add an image question field to this view
    holder.addImageQuestionField.setOnClickListener {
        Log.wtf(TAG, "image question will be added here")
    }

    // this button would add toggle option to this view
    holder.addToggleOption.setOnClickListener {
        Log.wtf(TAG, "toggle option will be added here")
    }

    // this button would add check box option to this view
    holder.addCheckBoxOption.setOnClickListener {
        Log.wtf(TAG, "check box option will be added here")
    }
}

as i had hinted earlier in my code whenever i click on the addTopicOfTheDayAddQuiz button which is supposed to add another view below the already exiting one(s) all child views inside the already existing views are cleared. Please what is a better approach to fix this issue?

each question is a recycler view here

question 1 and 2 are recycler views

Upvotes: 1

Views: 103

Answers (1)

B&#246; macht Blau
B&#246; macht Blau

Reputation: 13019

Your Adapter is doing a lot of work in onBindViewHolder(): setting up lots of click listeners which in turn toggle the UI so the users can enter their question details without being bothered by UI elements they don't need.

BUT you do not seem to keep track anywhere of the selections the users made for each question. AND each time the user adds a new question, you throw away the existing Adapter:

topicQuestionsAdapter = new TopicQuestionsAdapter("Adapter", getContext(), topicQuestionsList); addTopicOfTheDayWhereDynamicLayoutsWillBeAdded.setAdapter(topicQuestionsAdapter);

Since you did not share enough code for me to debug your app and test my solution, I'll just sketch some ideas:

Instead of using a new Adapter each time a new question is created, just add the question to the list and then call (please note: topicQuestionsList.size() - 1)

topicQuestionsAdapter.notifyItemInserted(topicQuestionsList.size() - 1);

One other thing you need to do is keep track of the states of each list item (which Buttons etc. are visible, what is the content of EditTexts if there are any...). If the list gets longer and the users need to scroll, onBindViewHolder() will be called repeatedly even if you keep working with the same Adapter.

So you need a class - let's call it ListEntry - with fields to reflect the possible states of a list item. And inside the Adapter, you can keep a List<ListEntry> with one ListEntry for each item of your data list. Each time some Button becomes visible, you update the corresponding ListEntry. In onBindViewHolder() you evaluate the ListEntry for the given position and set the attributes of the child Views of holder.itemView accordingly.

Upvotes: 2

Related Questions