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

Proposal for indexOf(Section section) in SectionedRecyclerViewAdapter #136

Closed yccheok closed 5 years ago

yccheok commented 6 years ago

Is your feature request related to a problem? Please describe. Currently, in SectionedRecyclerViewAdapter, if I want to have stable Id behavior (So that I can achieve animation without flickering - https://stackoverflow.com/questions/33851370/applying-statelistanimator-in-recylerviews-item-will-cause-flickering-effect-wh), here's my current code.

public class StableIdSectionedRecyclerViewAdapter extends SectionedRecyclerViewAdapter {
    public StableIdSectionedRecyclerViewAdapter() {
        this.setHasStableIds(true);
    }

    @Override
    public long getItemId(int position) {
        if (SectionedRecyclerViewAdapter.VIEW_TYPE_ITEM_LOADED != this.getSectionItemViewType(position)) {
            return NO_ID;
        }

        int positionInSection = this.getPositionInSection(position);

        Section section = this.getSectionForPosition(position);

        if (section instanceof NoteSection) {
            return ((NoteSection) section).getNotes().get(positionInSection).getPlainNote().getId();
        }

        return NO_ID;
    }
}

Consider the following case

device-2018-09-20-202712

If you refer to my above code, we can return correct Id for Loaded item for NoteSection (which is based on the primary key of SQLite). But, we aren't able to return correct Id for Header for NoteSection

If we have indexOf(Section section) in SectionedRecyclerViewAdapter, we can write the code as


    @Override
    public long getItemId(int position) {
        Section section = this.getSectionForPosition(position);

        if (SectionedRecyclerViewAdapter.VIEW_TYPE_ITEM_LOADED != this.getSectionItemViewType(position)) {
            // -2, we want to avoid from conflict with NO_ID
            return -indexOf(section) - 2;
        }

        int positionInSection = this.getPositionInSection(position);

        if (section instanceof NoteSection) {
            return ((NoteSection) section).getNotes().get(positionInSection).getPlainNote().getId();
        }

        return NO_ID;
    }

Describe the solution you'd like Add function indexOf(Section section) in SectionedRecyclerViewAdapter

    /**
     * Returns the index of the first occurrence of the specified section in this adapter, or -1 if
     * this adapter does not contain the section.
     *
     * @param section section to search for
     * @return the index of the first occurrence of the specified section in this adapter, or -1 if
     *         this adapter does not contain the element
     */
    public int indexOf(Section section) {
        int index = 0;

        for (Map.Entry<String, Section> entry : this.sections.entrySet()) {
            if (entry.getValue() == section) {
                return index;
            }
            index++;
        }

        return -1;
    }

However, there's one thing I isn't sure. Should I consider the visibility of Section during index calculation? Or, visibility consideration (on/off) will become method parameter of indexOf.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Additional context Add any other context or screenshots about the feature request here.

yccheok commented 6 years ago

I made a pull request - https://github.com/luizgrp/SectionedRecyclerViewAdapter/pull/137

Thank you.

yccheok commented 6 years ago

Just for your reference, this is our final use case in production.

import io.github.luizgrp.sectionedrecyclerviewadapter.Section;
import io.github.luizgrp.sectionedrecyclerviewadapter.SectionedRecyclerViewAdapter;

import static android.support.v7.widget.RecyclerView.NO_ID;

// https://stackoverflow.com/questions/33851370/applying-statelistanimator-in-recylerviews-item-will-cause-flickering-effect-wh
public class StableIdSectionedRecyclerViewAdapter extends SectionedRecyclerViewAdapter {
    private static final long ID_FOR_MESSAGE_SECTION = Long.MAX_VALUE;
    private static final long BASED_ID_FOR_HEADER = Long.MAX_VALUE - 1;

    public StableIdSectionedRecyclerViewAdapter() {
        this.setHasStableIds(true);
    }

    @Override
    public long getItemId(int position) {
        Section section = this.getSectionForPosition(position);

        if (SectionedRecyclerViewAdapter.VIEW_TYPE_ITEM_LOADED != this.getSectionItemViewType(position)) {
            // We can have multiple headers.
            return BASED_ID_FOR_HEADER - indexOf(section);
        }

        if (section instanceof NoteSection) {
            int positionInSection = this.getPositionInSection(position);
            return ((NoteSection) section).getNotes().get(positionInSection).getPlainNote().getId();
        } else if (section instanceof MessageSection) {
            // So far, we only have max 1 MessageSection per adapter.
            return ID_FOR_MESSAGE_SECTION;
        }

        return NO_ID;
    }
}