Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Support] No expandable at startup and No sticky header with multiple Adapters #228

Closed
mdtuyen opened this issue Nov 15, 2016 · 12 comments
Closed

Comments

@mdtuyen
Copy link

mdtuyen commented Nov 15, 2016

My recycler view have element no expand when startup. Here is my code:

Header item:

public class ItemHeader
        extends BaseItem<ItemHeader.ViewHolder>
        implements IExpandable<ItemHeader.ViewHolder, BaseItem>,
        IHeader<ItemHeader.ViewHolder>

Generate data:

public List<BaseItem> generateContact(int showHeader, int onlyUsers, boolean needPhonebook, HashMap<Integer, TLRPC.User> ignoreUsers, boolean isAdmin) {
        List<BaseItem> mItems = new ArrayList<>();
        ItemHeader header = null;
        if (showHeader != 0) {
            if (showHeader == 1) {
                header = new ItemHeader("EH" + 0);
                header.setTitle("CurrentUser");
                ItemProfile subItem = new ItemProfile("profile" + header.getId() + "-SB" + 0, MessagesController.getInstance().getUser(UserConfig.getClientUserId()));
                subItem.setHeader(header);
                header.addSubItem(subItem);
                mItems.add(header);

                header = new ItemHeader("EH" + 1);
                header.setTitle("Group");
                for (int i = 0; i < MessagesController.getInstance().dialogsGroupsOnly.size(); i++) {
                    ItemGroup itemGroup = new ItemGroup("group" + header.getId() + "-SB" + i, MessagesController.getInstance().dialogsGroupsOnly.get(i));
                    itemGroup.setHeader(header);
                    header.addSubItem(itemGroup);
                }
                mItems.add(header);
            } else if (showHeader == 2) {
                mItems.add(new ItemAction(ItemAction.GROUP));
                mItems.add(new ItemAction(ItemAction.SETTING));
                mItems.add(new ItemAction(ItemAction.INVITE));
                mItems.add(new ItemAction(ItemAction.SEARCH));
            }
            header = new ItemHeader("EH" + 2);
            header.setTitle("Friend");
        }
        HashMap<String, ArrayList<TLRPC.TL_contact>> usersSectionsDict = onlyUsers == 2 ? ContactsController.getInstance().usersMutualSectionsDict : ContactsController.getInstance().usersSectionsDict;
        ArrayList<String> sortedUsersSectionsArray = onlyUsers == 2 ? ContactsController.getInstance().sortedUsersMutualSectionsArray : ContactsController.getInstance().sortedUsersSectionsArray;
        for (String section : sortedUsersSectionsArray) {
            ArrayList<TLRPC.TL_contact> arr = usersSectionsDict.get(section);
            for (TLRPC.TL_contact contact : arr) {
                ItemUser itemFriend = new ItemUser("user" + contact.user_id, contact);
                if (header != null) {
                    itemFriend.setHeader(header);
                    header.addSubItem(itemFriend);
                }else{
                    mItems.add(itemFriend);
                }
            }
        }
        if (needPhonebook) {
            for (ContactsController.Contact contact : ContactsController.getInstance().phoneBookContacts) {
                ItemContact itemContact = new ItemContact("contact" + contact.id, contact);
                if (header != null) {
                    itemContact.setHeader(header);
                    header.addSubItem(itemContact);
                }else {
                    mItems.add(itemContact);
                }
            }
        }
        if (header != null)
            mItems.add(header);
        return mItems;
    }

Base item:

public abstract class BaseItem<VH extends FlexibleViewHolder>
        extends AbstractFlexibleItem<VH>
        implements Serializable 

I check my implement with your example but do't see what is my wrong. I take today to check it.

@davideas
Copy link
Owner

davideas commented Nov 15, 2016

@mdtuyen, the only thing you have to check is the following structure:
Only if you need sticky headers:

  • HeaderItem extends AbstractExpandableHeaderItem
  • SubItem extends AbstractSectionableItem

Otherwise:

  • HeaderItem extends AbstractExpandableItem
  • SubItem extends AbstractFlexibleItem

HeaderItem starts with expanded = true.
HeaderItem has at least a subItem inside.
At startup, you add always and only the HeaderItem to the list.
Adapter calls expandItemsAtStartUp();

If your list of items is not composed by that structure I can't help.
You can check it by debugging or enabling logs (yes logs help you!).

PS. Sorry but you already had working the expandable in the past, so why you still need help again now?

@mdtuyen
Copy link
Author

mdtuyen commented Nov 15, 2016

Hi, this code I use on some project with one type of subitem, in this project subitem i have three type extend from BaseItem. I don't see any thing wrong but it not work.

@davideas
Copy link
Owner

davideas commented Nov 15, 2016

@mdtuyen, I don't know all your item classes, you should review them as I explained, you probably messed up with the types.

@mdtuyen
Copy link
Author

mdtuyen commented Nov 20, 2016

Hi, I found the root of reason, when onResume I recreate data then call adapter.notifyDatasetChange. That mean I nee use adapter.updateDataSet() instead of notifyDatasetChange ?

@mdtuyen
Copy link
Author

mdtuyen commented Nov 22, 2016

Hi @davideas How to get holder of cell when I implement OnItemClickListener in Fragment ?

@davideas
Copy link
Owner

@mdtuyen, indeed notifyDataSetChanged() is already called in updateDataSet(when animate=false), this method is the only correct way to update the items in the Adapter, otherwise what else you would do, because you don't have the reference of mItems, do you recreate the Adapter? I hope no.

Using this library, the developer should never call directly the methods notifyXXX() unless a particular situation requires it. Anyway, this is one of the main principle because this library exists.

Regarding, getting the holder of the cell in the fragment, can you elaborate your question? I really don't get it.

@mdtuyen
Copy link
Author

mdtuyen commented Nov 24, 2016

Hi, @davideas

This is my function to convert data use for this Adapter:

public List<BaseItem> generateContact(int showHeader, int onlyUsers, boolean needPhonebook, HashMap<Integer, TLRPC.User> ignoreUsers, boolean isAdmin) {
        List<BaseItem> mItems = new ArrayList<>();
        ItemHeader header = null;
        if (showHeader != 0) {
            **if (showHeader == 1) {**
                header = new ItemHeader("EH" + 0);
                header.setTitle("CurrentUser");
                ItemProfile subItem = new ItemProfile("profile" + header.getId() + "-SB" + 0, MessagesController.getInstance().getUser(UserConfig.getClientUserId()));
                subItem.setHeader(header);
                header.addSubItem(subItem);
                mItems.add(header);

                header = new ItemHeader("EH" + 1);
                header.setTitle("Group");
                for (int i = 0; i < MessagesController.getInstance().dialogsGroupsOnly.size(); i++) {
                    ItemGroup itemGroup = new ItemGroup("group" + header.getId() + "-SB" + i, MessagesController.getInstance().dialogsGroupsOnly.get(i));
                    itemGroup.setHeader(header);
                    header.addSubItem(itemGroup);
                }
                mItems.add(header);
            **} else if (showHeader == 2) {**
                mItems.add(new ItemAction(ItemAction.GROUP));
                mItems.add(new ItemAction(ItemAction.SETTING));
                mItems.add(new ItemAction(ItemAction.INVITE));
                mItems.add(new ItemAction(ItemAction.SEARCH));
            }
             **header = new ItemHeader("EH" + 2);
             header.setTitle("Friend");**
        }
        HashMap<String, ArrayList<TLRPC.TL_contact>> usersSectionsDict = onlyUsers == 2 ? ContactsController.getInstance().usersMutualSectionsDict : ContactsController.getInstance().usersSectionsDict;
        ArrayList<String> sortedUsersSectionsArray = onlyUsers == 2 ? ContactsController.getInstance().sortedUsersMutualSectionsArray : ContactsController.getInstance().sortedUsersSectionsArray;
        for (String section : sortedUsersSectionsArray) {
            ArrayList<TLRPC.TL_contact> arr = usersSectionsDict.get(section);
            for (TLRPC.TL_contact contact : arr) {
                ItemUser itemFriend = new ItemUser("user" + contact.user_id, contact);
                if (header != null) {
                    itemFriend.setHeader(header);
                    header.addSubItem(itemFriend);
                } else {
                    mItems.add(itemFriend);
                }
            }
        }
        if (needPhonebook) {
            for (ContactsController.Contact contact : ContactsController.getInstance().phoneBookContacts) {
                ItemContact itemContact = new ItemContact("contact" + contact.id, contact, false);
                if (header != null) {
                    itemContact.setHeader(header);
                    header.addSubItem(itemContact);
                } else {
                    mItems.add(itemContact);
                }
            }
        }
        if (header != null)
            mItems.add(header);
        return mItems;
    }

The first fragment I use this function with showHeader = 1, then I replace it with second fragment with showHeader = 2 then enable sticky for both adapter use in two fragment

Item with header "EH" + 2 in second fragment no sticky.

but

When I swap two fragment, that mean the first show fragment with showHeader = 2, it work fine. header "EH"+2 sticky.

@davideas
Copy link
Owner

davideas commented Nov 24, 2016

@mdtuyen, I still don't get it, beside the complex logic of converting items (that remains your duty), maybe you want to say that the second fragment doesn't have sticky headers? are they both in memory or you complete replace the previous?

What I know is that you need to have a list for each Adapter, and that each Adapter reads the flags of the items, so, the items should be independent from the items of another list.
Regarding the sticky headers, the container should stay in the layout where the RecyclerView lays.

@mdtuyen
Copy link
Author

mdtuyen commented Nov 24, 2016

hi, as you say, "items should be independent from the items of another list". What is independent ? I make an list contact in device with difference header so that the contact is same.

Set up for adapter is the same but in the second fragment it not sticky.

@davideas
Copy link
Owner

@mdtuyen, the adapter items instances, holding the flags (enabled, expanded... etc), must be different if you use another adapter simultaneously. Not only, as I said, the layout container for sticky headers must be independent too! This has been tested, and it works, just need a good configuration.

Actually, I don't know in which situation you are, you might read the issue 179 (that is still open, but resolved!) to know what to do with multiple adapters having sticky headers. Also, you might need the IHolder interface where the model item is the same instance in both adapters, but the adapter item is different, and you do this when you convert your list for the 2 Adapters.
I hope we clarified now.

@mdtuyen
Copy link
Author

mdtuyen commented Nov 25, 2016

Hi, @davideas #179 is correct. It resolved my issue. But why do you don't inflater to another view from R.id.sticky_header_container directly when we enable sticky ?

@davideas davideas changed the title [Support] No expandable at startup [Support] No expandable at startup and No sticky header with multiple Adapters Nov 25, 2016
@davideas
Copy link
Owner

@mdtuyen, actually your idea to inflate the layout at run time is a very good idea and will avoid specifying the layout in the user xml (which means less configuration!). I already tried to create a layout dynamically long time ago by overriding the method to get the layout for sticky headers in my ExampleAdapter. That piece of code is now commented but works as expected and no layout in the xml.

Just a FrameLayout, that wraps the RV, would still be necessary if used in combination with SwipeRefreshLayout, but maybe I can try to add it at runtime in the parent view can hold only one child view like SwipeRefreshLayout.

So I was curious, I made a test, it works very well, I was also able to restore the elevation of the sticky layout, which would resolve also part of the issue #227.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants