How to categorize list items in a recyclerview?

元气小坏坏 提交于 2019-12-11 10:45:28

问题


I am building a notifications list for an application I'm working on and I'm having trouble finding a way to take my list of notifications from the server and displaying them in separate lists in a RecyclerView. The end product would display the list of notifications with headers for Recent notifications and Older notifications, a la:

<RECENT HEADER>
    <NOTIF-1>
    <NOTIF-2>
<OLDER HEADER>
    <NOTIF-3>
    <NOTIF-4>
    <NOTIF-5>
    <NOTIF-6>

except instead of angle-bracket text it's actual views representing those, complete with images, actual notification details and dividers.

I already have code that displays them in a RecyclerView:

XML:

<!-- Main layout -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/include_toolbar"/>

    <RelativeLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/notification_swipe_refresh"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <com.mapjungle.mymoose.ui.widget.EmptyRecyclerView
                android:id="@+id/notification_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

        </android.support.v4.widget.SwipeRefreshLayout>

    </RelativeLayout>

</LinearLayout>

Java:

@InjectView(R.id.notification_list) RecyclerView mRecyclerView;
@Inject Picasso mPicasso;
@Inject NotificationService mUserService;
private NotificationAdapter mAdatper;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_notifications);
    ButterKnife.inject(this);
    setTitle("Notifications");
    mAdatper = new NotificationAdapter(mPicasso);
    mRecyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(this)
            .color(getResources().getColor(R.color.secondary_color))
            .size(1)
            .build());
    final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mRecyclerView.setLayoutManager(layoutManager);
    mRecyclerView.setAdapter(mAdatper);
    updateList();
}

@Override
protected int getSelfNavDrawerItem() {
    return NAVDRAWER_ITEM_PHOTO_POST;
}

public void updateList() {
    mUserService.getNotifications(new Callback<List<Notification>>() {

        @Override
        public void success(List<Notification> notificationList, Response response) {
            mAdatper.replaceWith(notificationList);
        }

        @Override
        public void failure(RetrofitError error) {
            Timber.e(error, "Failed to load notifications...");
        }
    });
}

This all works fine enough to display all of the notifications and they're all sorted in the order from newest to oldest descending. But each has a boolean property "acknowledged" that is set to false if the user hasn't seen them before. I want to put split the list into the two groups I've explained above using this flag, but I don't know how to throw in the headers. I've thought about subclassing Notification to create NotificationHeader views and inserting them into the list where appropriate but that just feels sloppy to me. I've also thought about doing two recycler views, one for the new and another for the old, but visually that didn't work the way I intended (I haven't confirmed it but it looked like each recycler view scrolled independently of the others, something that I do not want). Any suggestions?

I know that the first idea of creating special Notification Headers will probably work, I've done something like that before, but it just feels like bad practice.


回答1:


RecyclerView.Adapter has a method called getItemViewType() that takes the position of an item in the adapter's list, and returns the view type it should use. In my case, the method looks like this:

@Override
public int getItemViewType(int position){
    Notification n = mNotifications.get(position);
    boolean useHeader = n.getType().equals(Notification.HEADER_OLDER) ||
            n.getType().equals(Notification.HEADER_RECENT);
    return useHeader ? this.USE_HEADER : this.DONT_USE_HEADER;
}

Which checks the items in the notification list and sees if they're a special static 'Header notification' object. This is used internally by the Adapter class and it passes the 'viewType' parameter to the onCreateViewHolder() method, which we also override:

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    int layout = viewType ==  USE_HEADER ?
            R.layout.view_item_notification_header :
            R.layout.view_item_notification;

    NotificationItemView view = (NotificationItemView) LayoutInflater.from(viewGroup.getContext())
            .inflate(layout, viewGroup, false);
    return new ViewHolder(view);
}

Overriding this method allows us to use the viewType parameter to choose the appropriate layout to inflate for the ViewHolder.

There are some better style/good practice decisions things I should have done here, such as making my Notification adapter hold a list of NotificationListItems instead of Notifications, which would allow me to put in a new kind of NotificationHeader object on it's own instead of making Notification objects that weren't really Notifications and using a bunch of constant values. But the underlying principle is still there:

  1. In your Model, have a method that returns the layout view to use for it
  2. In your adapter override getItemViewType() to use the aforementioned method and return an int that corresponds to the layout that should be inflated
  3. In your adapter also override onCreateViewHolder() to use the int from getItemViewType() and inflate the appropriate view accordingly


来源:https://stackoverflow.com/questions/32789529/how-to-categorize-list-items-in-a-recyclerview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!