Reference
实现效果
实现过程
获取数据
例如使用 Retrofit 访问接口获取数据:http://api.meituan.com/mmdb/movie/v2/list/rt/order/coming.json?ci=1&limit=12&token=&__vhost=api.maoyan.com&utm_campaign=AmovieBmovieCD-1&movieBundleVersion=6801&utm_source=xiaomi&utm_medium=android&utm_term=6.8.0&utm_content=868030022327462&net=255&dModel=MI 5&uuid=0894DE03C76F6045D55977B6D4E32B7F3C6AAB02F9CEA042987B380EC5687C43&lat=40.100673&lng=116.378619&__skck=6a375bce8c66a0dc293860dfa83833ef&__skts=1463704714271&__skua=7e01cf8dd30a179800a7a93979b430b2&__skno=1a0b4a9b-44ec-42fc-b110-ead68bcc2824&__skcy=sXcDKbGi20CGXQPPZvhCU3%2FkzdE%3D
同时可以将获取到的 JSON 格式的数据封装成 Bean 对象。
使用数据
将数据放入 RecyclerView 中
1、使用到的只是 Bean 对象中的 comingBeanList,所以先获取 comingBeanList:
1
| List<DataResponseBean.DataBean.ComingBean> comingBeanList = bean.getData().getComing();
|
2、因为将日前作为分组的依据,同时将日期作为 title,所以先获取所有 comingBeanList 中元素的日期:
1 2 3 4 5 6
| private void setPullAction(List<DataResponseBean.DataBean.ComingBean> comingslist) { mTitleList = new ArrayList<>(); for (int i = 0; i < comingslist.size(); i++) { mTitleList.add(comingslist.get(i).getComingTitle()); } }
|
3、给 RecyclerView 设置 ItemDecoration,用于显示每组第一个元素的 title:
1 2 3 4 5 6 7 8 9 10 11 12 13
| mRecyclerView.addItemDecoration(new SectionDecoration(this, new SectionDecoration.DecorationCallback() { @Override public String getGroupId(int position) { return mTitleList.get(position); }
@Override public String getGroupTitle(int position) { return mTitleList.get(position); } }));
|
4、给 RecyclerView 添加 adapter,显示列表数据:
1
| mRecyclerView.setAdapter(new MyRecyclerAdapter(this, comingBeanList));
|
将数据分组,每组第一个元素显示 title,其余元素不显示 title
1、重写 getItemOffsets 方法:
1 2 3 4 5 6 7 8 9 10 11
| @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int pos = parent.getChildAdapterPosition(view); if (isFirstInGroup(pos)) { outRect.top = barTopGap; } else { outRect.top = 0; } }
|
如果判断出当前位置的元素是该组的第一个元素,则将 outRect.top 置为 barTopGap,即在元素的上方预留出高度为 barTopGap 的区域;反之,则不预留区域。
2、判断当前位置的元素是否为该组的第一个元素的方法:
1 2 3 4 5 6 7 8 9 10
| private boolean isFirstInGroup(int pos) { if (pos == 0) { return true; } else { String groupId = mCallback.getGroupId(pos); String prevGroupId = mCallback.getGroupId(pos - 1); Log.d(TAG, "pos:" + pos + ", " + "groupId:" + groupId + ", " + "prevGroupId:" + prevGroupId); return !groupId.equals(prevGroupId); } }
|
如果当前位置是 0,则必然是第一个元素;反之,则比较当前位置的元素及其前一个元素的 id 是否相同,此处,比较的是它们的 title。
始终保持 RecyclerView 顶部有最上面显示的元素对应的组的 title
1、重写 onDrawOver 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(c, parent, state); int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view);
String title = mCallback.getGroupTitle(position).toUpperCase(); if (TextUtils.isEmpty(title)) { continue; }
int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight();
if (i == 0) { int viewBottom = view.getBottom(); if (isLastInGroup(position) && viewBottom < barTopGap) { c.drawRect(left, viewBottom - barTopGap, right, viewBottom, barPaint); c.drawText(title, left, viewBottom - alignBottom, textPaint); } else { c.drawRect(left, 0, right, barTopGap, barPaint); c.drawText(title, left, barTopGap - alignBottom, textPaint); } } else { if (isFirstInGroup(position)) { int viewTop = view.getTop(); c.drawRect(left, viewTop - barTopGap, right, viewTop, barPaint); c.drawText(title, left, viewTop - alignBottom, textPaint); } } } }
|
遍历 RecyclerView 上能显示出的所有元素:
(1)第一个元素
判断该元素是不是该组的最后一个元素,并且其剩余高度已经小于悬浮栏的高度,对应的状态是:
悬浮栏绘制的高度不变,依然是 barTopGap,但是绘制的上下界变了,上界从 RecyclerView 外的区域开始绘制。
否则,保持在 RecyclerView 的顶部有一高度为 barTopGap 的悬浮栏,对应的状态是:
(2)非第一个元素
判断该元素是不适该组的第一个元素,对应的状态是:
显示标题,标题对应的悬浮栏位置在该元素的上方,高度为 barTopGap。
否则,不需要处理,对应的状态是:
2、判断当前位置的元素是否为该组的最后一个元素的方法:
1 2 3 4 5 6
| private boolean isLastInGroup(int pos) { String groupId = mCallback.getGroupId(pos); String nextGroupId = mCallback.getGroupId(pos + 1); Log.d(TAG, "pos:" + pos + ", " + "groupId:" + groupId + ", " + "nextGroupId:" + nextGroupId); return !groupId.equals(nextGroupId); }
|
源码地址
StickyNavigationBar