luizgrp / SectionedRecyclerViewAdapter

An Adapter that allows a RecyclerView to be split into Sections with headers and/or footers. Each Section can have its state controlled individually.
MIT License
1.68k stars 372 forks source link

Blank view when I build the project. #128

Closed douglasalipio closed 5 years ago

douglasalipio commented 6 years ago

Hey guys,

I'm trying to show only two section in my recycleView but the items in my section don't show. Just got a blank view. BTW I put a breakpoint on onBindItemViewHolder nothing happens. Here is the code.

`class ActiveFormSection(private val onEditItemClick: (Form) -> Unit,
                        private val onDeleteItemClick: (Form) -> Unit,
                        private val onShareItemClick: (Form) -> Unit,
                        private val titleSection: String,
                        private val forms: List<Form>) : StatelessSection(SectionParameters.builder()
        .itemResourceId(R.layout.active_form_item_view)
        .headerResourceId(R.layout.section)
        .build()) {

    override fun getContentItemsTotal() = forms.size

    override fun onBindItemViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
        val activeFormHolder = holder as ItemActiveFormHolder
        val currentTime = forms[position].date
        val title = forms[position].title
        activeFormHolder.bind(title, currentTime, editClickListener = { onEditItemClick(forms[it.adapterPosition]) },
                shareClickListener = { onShareItemClick(forms[position]) },
                deleteClickListener = { onDeleteItemClick(forms[position]) })
    }

    override fun getItemViewHolder(view: View?) = ItemActiveFormHolder(view)

    override fun getHeaderViewHolder(view: View): RecyclerView.ViewHolder {
        return HeaderViewHolder(view)
    }
    override fun onBindHeaderViewHolder(holder: RecyclerView.ViewHolder?) {
        val headerHolder = holder as HeaderViewHolder?
        headerHolder?.let { it.sectionText.text = titleSection }
    }
}

class ItemActiveFormHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {

    private val titleForm = itemView?.titleActiveForm
    private val currentTime = itemView?.currentTime
    private val edit = itemView?.editForm
    private val share = itemView?.shareForm
    private val delete = itemView?.deleteForm

    fun bind(title: String, timeNow: String,
             editClickListener: (ItemActiveFormHolder) -> Unit,
             deleteClickListener: (ItemActiveFormHolder) -> Unit,
             shareClickListener: (ItemActiveFormHolder) -> Unit) {

        titleForm?.let { it.text = title }
        currentTime?.let { it.text = timeNow }
        edit?.let { it -> it.setOnClickListener { editClickListener(this) } }
        share?.let { it -> it.setOnClickListener { shareClickListener(this) } }
        delete?.let { it -> it.setOnClickListener { deleteClickListener(this) } }
    }
}`
class AllFormSection(private val onItemClick: (Form) -> Unit,
                     private val titleSection: String,
                     private val forms: List<Form>) : StatelessSection(SectionParameters.builder()
        .itemResourceId(R.layout.all_form_item_view)
        .headerResourceId(R.layout.section)
        .build()) {

    override fun getContentItemsTotal() = forms.size

    override fun onBindItemViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
        val formHolder = holder as ItemAllFormHolder
        formHolder.bind(forms[position].title, clickListener = { onItemClick(forms[position]) })
    }

    override fun getItemViewHolder(view: View?) = ItemAllFormHolder(view)

    override fun getHeaderViewHolder(view: View): RecyclerView.ViewHolder {
        return HeaderViewHolder(view)
    }
    override fun onBindHeaderViewHolder(holder: RecyclerView.ViewHolder?) {
        val headerHolder = holder as HeaderViewHolder?
        headerHolder?.let { it.sectionText.text = titleSection }
    }
}

class ItemAllFormHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
    private val titleForm = itemView?.titleAllForm
    fun bind(title: String, clickListener: (ItemAllFormHolder) -> Unit) {
        titleForm?.let { it.text = title }
        itemView.setOnClickListener { clickListener(this) }
    }
}
class HeaderViewHolder internal constructor(headerView: View) : RecyclerView.ViewHolder(headerView) {
    val sectionText = headerView.section_text
}

As I'm using Conductor lib, I have the inicialization in onAttach.


    override fun onAttach(view: View) {
        super.onAttach(view)
        presenter.onAttach(this)
        allFormRecycleView.layoutManager = LinearLayoutManager(view.context)
        allFormRecycleView.adapter = sectionAdapter
        presenter.submitLoadModelForms()
        presenter.submitLoadActiveForms()
    }

 override fun showModelForms(modelForms: List<Form>) {
        //allFormAdapter.updateForms(modelForms)
        sectionAdapter.addSection(AllFormSection(editClick, "Test A", modelForms))
    }

    override fun showActiveForms(activeForms: List<Form>) {
        //activeFormAdapter.updateForms(activeForms)
        sectionAdapter.addSection(ActiveFormSection(editClick, deleteClick, shareClick, "Test A", activeForms))
    }
douglasalipio commented 6 years ago

I have found the problem. I should set sectionAdapter.addSection before instantiate the section adapter `allFormRecycleView.adapter = sectionAdapter

How can I add a dynamic section since I can't addSection after instantiating the adapter? Am I missing something? `

yccheok commented 6 years ago

@douglasalipio

From my experience, to avoid NPE issue, it is easier to add all Sections during onCreateView (Doesn't matter whether they should be shown during the entire app life-cycle), and make all Sections as member variables of your Fragment (or Activity).

With such, you need not to concern, when I should instantiate a Section, and when I should nullify it. This greatly reduce the chance of being beaten by NPE.

Also, adding all Sections at once, even they should be shown on screen, seem redundant. However, the way of SectionedRecyclerViewAdapter handling Section visibility is pretty efficient. The following code block is commonly seen in SectionedRecyclerViewAdapter source code

// ignore invisible sections
if (!section.isVisible()) {
    continue;
}

I would argue that the overhead is neglect-able.

In the middle of your app life-cycle, if you decide to show/ hide a Section, simply use the following code snippet.

public static void showSection(SectionedRecyclerViewAdapter sectionedRecyclerViewAdapter, Section section) {
    if (section.isVisible()) {
        // Already visible.
        return;
    }

    section.setVisible(true);

    sectionedRecyclerViewAdapter.notifySectionChangedToVisible(section);
}

public static void hideSection(SectionedRecyclerViewAdapter sectionedRecyclerViewAdapter, Section section) {
    if (false == section.isVisible()) {
        // Already invisible.
        return;
    }

    int previousSectionPosition = sectionedRecyclerViewAdapter.getSectionPosition(section);

    section.setVisible(false);

    sectionedRecyclerViewAdapter.notifySectionChangedToInvisible(section, previousSectionPosition);
}

The above is for case, where you doesn't use DiffUtil. If you are using DiffUtil, you need to

this.messageSection.setVisible(false);

NoteDiffUtilCallback noteDiffUtilCallback = new NoteDiffUtilCallback(
    ....
);

DiffUtil.calculateDiff(noteDiffUtilCallback).dispatchUpdatesTo(this.sectionedRecyclerViewAdapter);

Using DiffUtil is a bit tricky as mentioned in https://github.com/luizgrp/SectionedRecyclerViewAdapter/issues/108 . However, once you get DiffUtil done right, using SectionedRecyclerViewAdapter is a lot easier. So far, I don't really have a generalized way, to develop DiffUtil for SectionedRecyclerViewAdapter. Some basic understanding on how SectionedRecyclerViewAdapter work internally, is required to implement a correct DiffUtil.

yccheok commented 6 years ago

@douglasalipio

If you still insist to call addSection/ removeSection dynamically, you still can do so. However, you need to make sure you call it in UI thread, and also perform correct notify on the adapter. If not, there will not be any add/ remove animation.

@luizgrp I think currently, we don't have methods called notifySectionAdded and notifySectionRemoved to assist developer. Do you think we should add both?

luizgrp commented 5 years ago

Hi @douglasalipio, @yccheok pointed you out on the right direction. You can also check example 8 to see how sections are added and removed dynamically.