在RecyclerView

时间:2016-01-18 06:23:19

标签: android material-design android-recyclerview

我需要在具有标题的组中对RecyclerView中的元素进行划分(如下图中的收件箱应用程序),请帮助我找出哪种方法对我的情况更好: 1)我可以使用异构布局但是在组中插入新元素并不是那么方便(因为我需要检查是否已经添加了同一组的元素或者我需要添加新的分隔符)。因此,在这种情况下,我将所有具有此类数据结构的操作包装到一个单独的类中。

2)从理论上讲,我可以用自己的RecyclerView包装每个组,这是一个好主意吗?

Inbox app

3 个答案:

答案 0 :(得分:91)

例如,你可以:

  1. 使用TreeMap<Date,List<Event>>按日期拆分元素。这将是一个用于保存业务对象的集合。当然,如果你已经有类似的结构,你可以保留它。有一些东西可以轻松构建项目列表,以便用正确的元素顺序填充UI。

  2. List项定义专用抽象类型(例如ListItem)以包装您的业务对象。它的实现可能是这样的:

    public abstract class ListItem {
    
        public static final int TYPE_HEADER = 0;
        public static final int TYPE_EVENT = 1;
    
        abstract public int getType();
    } 
    
  3. 为每个List元素类型定义一个类(这里我只添加了两种类型,但您可以根据需要使用多种类型):

    public class HeaderItem extends ListItem {
    
        private Date date;
    
        // here getters and setters 
        // for title and so on, built
        // using date
    
        @Override
        public int getType() {
            return TYPE_HEADER;
        }
    
    }
    
    public class EventItem extends ListItem {
    
        private Event event;
    
        // here getters and setters 
        // for title and so on, built 
        // using event
    
        @Override
        public int getType() {
            return TYPE_EVENT;
        }
    
    }
    
  4. 按如下方式创建一个List(其中mEventsMap是第1点的地图构建):

    List<ListItem> mItems;
    // ...
    mItems = new ArrayList<>();
    for (Date date : mEventsMap.keySet()) {
        HeaderItem header = new HeaderItem();
        header.setDate(date); 
        mItems.add(header);
        for (Event event : mEventsMap.get(date)) {
            EventItem item = new EventItem();
            item.setEvent(event);
            mItems.add(item);
        }
    }
    
  5. RecyclerView定义适配器,处理第4点定义的List。重要的是重写getItemViewType方法,如下所示:

    @Override
    public int getItemViewType(int position) {
        return mItems.get(position).getType();
    }
    

    然后你需要有两个布局和ViewHolder用于标题和事件项。适配器方法应该相应地处理这个问题:

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == ListItem.TYPE_HEADER) {
            View itemView = mLayoutInflater.inflate(R.layout.view_list_item_header, parent, false);
            return new HeaderViewHolder(itemView);
        } else {
            View itemView = mLayoutInflater.inflate(R.layout.view_list_item_event, parent, false);
            return new EventViewHolder(itemView);
        }
    }
    
    
    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
        int type = getItemViewType(position);
        if (type == ListItem.TYPE_HEADER) {
            HeaderItem header = (HeaderItem) mItems.get(position);
            HeaderViewHolder holder = (HeaderViewHolder) viewHolder;
            // your logic here
        } else {            
            EventItem event = (EventItem) mItems.get(position);
            EventViewHolder holder = (EventViewHolder) viewHolder;
            // your logic here
        }
    }
    
  6. Here它是GitHub上的一个存储库,提供了上述方法的实现。

答案 1 :(得分:9)

你可以尝试使用我写过的库来解决我的项目中的这个问题。 Gradle依赖(需要包含 jcenter repo):

dependencies {
    //your other dependencies
    compile 'su.j2e:rv-joiner:1.0.3'//latest version by now
}

然后,在你的情况下,你可以这样做:

//init your RecyclerView as usual
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));

//construct a joiner
RvJoiner rvJoiner = new RvJoiner();
rvJoiner.add(new JoinableLayout(R.layout.today));
YourAdapter todayAdapter = new YourAdapter();
rvJoiner.add(new JoinableAdapter(todayAdapter));
rvJoiner.add(new JoinableLayout(R.layout.yesterday));
YourAdapter yesterdayAdapter = new YourAdapter();
rvJoiner.add(new JoinableAdapter(yesterdayAdapter));

//set join adapter to your RecyclerView
rv.setAdapter(rvJoiner.getAdapter());

当您需要添加项目时,请将其添加到适当的适配器,例如:

if (timeIsToday) {
    todayAdapter.addItem(item);//or other func you've written
} else if (timeIsYesterday) {
    yesterdayAdapter.addItem(item);
}

如果需要动态地将新组添加到回收站视图,可以使用以下方法:

rvJoiner.add(new JoinableLayout(R.layout.tomorrow));
YourAdapter tomorrowAdapter = new YourAdapter();
rvJoiner.add(new JoinableAdapter(tomorrowAdapter));

您可以查看this link以获取更多图书馆说明。我不能说这肯定是实现目标的最佳方式,但它有时会帮助我。

<强> UPD:

我已经找到了这样做的方法而不使用外部库。使用RecyclerView.ItemDecoration课程。例如,要将项目按组分组,您可以执行以下操作:

recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {

        private int textSize = 50;
        private int groupSpacing = 100;
        private int itemsInGroup = 3;

        private Paint paint = new Paint();
        {
            paint.setTextSize(textSize);
        }

        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            for (int i = 0; i < parent.getChildCount(); i++) {
                View view = parent.getChildAt(i);
                int position = parent.getChildAdapterPosition(view);
                if (position % itemsInGroup == 0) {
                    c.drawText("Group " + (position / itemsInGroup + 1), view.getLeft(),
                            view.getTop() - groupSpacing / 2 + textSize / 3, paint);
                }
            }
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            if (parent.getChildAdapterPosition(view) % itemsInGroup == 0) {
                outRect.set(0, groupSpacing, 0, 0);
            }
        }
    });

希望它有所帮助。

答案 2 :(得分:1)

这个问题是从2016年开始的,与此同时(2020年),有不同的库可用于对回收者视图进行分组。最受欢迎的: