FloatingDecoration:五行代码实现悬浮导航栏
来源:互联网 发布:虚拟照相软件 编辑:程序博客网 时间:2024/09/21 06:20
0.前言
FloatingDecoration本质上就是一个自定义ItemDecoration,FloatingDecoration的效果图如下:
1.使用方法
伸手党福利:
compile ‘com.chauncey.view:floatingdecoration:1.0.1’
FloatingDecoration decoration = new FloatingDecoration(this, new FloatingDecoration.DecorationCallback() { @Override public String getGroupLabel(int position) { return items.get(position).getTitle(); } });
//设置分隔线,参数为Drawabledecoration.setDividingLine(mDividingLine);
别忘了
recyclerView.addItemDecoration(decoration);
轻松实现悬浮导航栏效果
2.ItemDecoration
ItemDecoration类主要有三种方法:
public void onDraw(Canvas c, RecyclerView parent, State state)public void onDrawOver(Canvas c, RecyclerView parent, State state)public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
三种方法作用如下:
onDraw:绘制背景,会被Item的内容所覆盖。适合用来绘制每个分组的标识
onDrawOver:绘制前景,会覆盖Item的内容。适合用来绘制悬浮的顶部。
getItemOffsets:获取Item的距离,也可以给Item添加边距。绘制之前需要先空出一些空间。
3.FloatingDecoration源码解析
1.getItemOffsets
@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int pos = parent.getChildAdapterPosition(view); String groupLabel = mDecorationCallback.getGroupLabel(pos); if (groupLabel == null) return; //只有每个分组的第一个item需要label if (isFirstInGroup(pos)) outRect.top = mLabelHeight; } private boolean isFirstInGroup(int pos) { if (pos == 0) { return true; } else { String prevLabel = mDecorationCallback.getGroupLabel(pos - 1); String label = mDecorationCallback.getGroupLabel(pos); //与上一个item的label进行对比,若不一致则当前item是新一组的第一个元素 if (prevLabel.equals(label)) { return false; } else { return true; } } }
放两张设置前后的效果对比图
2.onDraw
//绘制分组的label @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); int childCount = parent.getChildCount(); if (mDividingLine != null) { //绘制分隔线。ps:可通过setDividingLine(Drawable drawable)设置分隔线 drawDividingLine(c, parent, left, right, childCount); } //绘制分组标识 drawLabelText(c, parent, left, right, childCount); } private void drawDividingLine(Canvas canvas, RecyclerView parent, int left, int right, int childCount) { for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); int top = view.getBottom() + lp.bottomMargin; int bottom = top + mDividingLine.getIntrinsicHeight(); mDividingLine.setBounds(left, top, right, bottom); mDividingLine.draw(canvas); } } private void drawLabelText(Canvas canvas, RecyclerView parent, int left, int right, int childCount) { for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); if (isFirstInGroup(position)) { String groupLabel = mDecorationCallback.getGroupLabel(position); if (groupLabel == null) return; int bottom = view.getTop(); int top = bottom - mLabelHeight; //绘制矩形 canvas.drawRect(left, top, right, bottom, mPaint); //将文本绘制在矩形的竖直中间位置 canvas.drawText(groupLabel , left + 30, bottom - mLabelHeight / 2 + (float) getLabelHeight() / 4, mTextPaint); } } }
//获取label文本的高度 private Paint.FontMetrics mMetrics = mTextPaint.getFontMetrics(); private double getLabelHeight() { return Math.ceil(mMetrics.bottom - mMetrics.top); }
设置之后整体效果如下
3.onDrawOver
@Override public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(canvas, parent, state); //获取当前可见的第一个item,意思就是在当前屏幕中的列表的第一个可见item。(已经划出屏幕外的item不算) int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition(); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); String label = mDecorationCallback.getGroupLabel(position); canvas.drawRect(left, 0, right, mLabelHeight, mPaint); canvas.drawText(label, 30, mLabelHeight / 2 + (float) getLabelHeight() / 4, mTextPaint); }
效果如图:
不过可以看得出来,顶部label的切换很突兀,于是想做一个慢慢把上一个label顶出屏幕的效果。
思路大致如下:
正常情况下悬浮顶部一直绘制在最上方
当分组的最后一个Item离开屏幕时,该分组的Label也随之跟着离开,而此时下面又接着下一个分组的label,就会产生下个label把上个label顶出的效果:
把代码改成:
//绘制悬浮顶部 @Override public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(canvas, parent, state); int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition(); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); String label = mDecorationCallback.getGroupLabel(position); //此处开始不同 for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); //判断item是不是该组的最后一个元素 if (isLastInGroup(position)) { int bottom = child.getBottom(); //滑动过程中,若分组中最后一个item的bottom小于label的高度,就把label的绘制位置往上提。 if (bottom <= mLabelHeight) { canvas.drawRect(left, 0, right, bottom, mPaint); canvas.drawText(label, 30, mLabelHeight / 2 + (float) getLabelHeight() / 4 - (mLabelHeight - bottom), mTextPaint); //以下代码不再执行,免得出现两次绘制。 return; } } } canvas.drawRect(left, 0, right, mLabelHeight, mPaint); canvas.drawText(label, 30, mLabelHeight / 2 + (float) getLabelHeight() / 4, mTextPaint); } private boolean isLastInGroup(int pos) { String label = mDecorationCallback.getGroupLabel(pos); String nextLabel; try { //若item是列表中所有item的最后一个,则pos+1会导致角标越界 nextLabel = mDecorationCallback.getGroupLabel(pos + 1); } catch (ArrayIndexOutOfBoundsException exception) { return true; } if (!label.equals(nextLabel)) return true; return false; }
大功告成,再来看看效果:
源码已经上传至Github,喜欢的话记得给我点个star。
0 0
- FloatingDecoration:五行代码实现悬浮导航栏
- js + css实现左侧悬浮导航栏
- 悬浮导航栏
- 五行代码实现ASP无组件上传
- 五行代码实现ASP无组件上传
- 五行代码实现ASP无组件上传
- 实现导航Tab栏悬浮功能之改进版
- jQuery简单实现导航栏根据滑动自动悬浮效果
- jQuery实现' 返回顶部 ' 和 ' 导航悬浮 '
- CoordinatorLayout协调布局,实现悬浮导航条
- 自定义ScrollView,实现导航条悬浮置顶
- jquery五行代码实现对浏览器版本检测
- 五行代码的疑惑
- 滚动程序五行代码
- 二分法五行核心代码
- CSS3--隐藏悬浮左侧导航栏
- 导航栏二级悬浮菜单样式
- Bootstrap多级导航栏(级联导航)的实现代码
- 观察者模式(结合代理模式)
- Navicat与PLSQL Developer安装时需要oracle client
- TProfiler工具的使用
- 【开源毕设】一款精美的家校互动APP分享——爱吖校推 [你关注的,我们才推](持续开源更新2)
- unity3d,跟着大佬做自己的第一个游戏(第一步,游戏模型制作)
- FloatingDecoration:五行代码实现悬浮导航栏
- SpringCloud微服务化(一)-服务注册与发现
- Debian系统开机启动过程以及如何自定义开机自启动脚本
- metasploit无法连接到数据库
- Redis连接池---jedis-2.9.0+commons-pool2-2.4.2
- OnCollision和OnTrigger无法响应的可能原因
- MyBatis批量更新报错org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.bindin
- MII_GMII_RGMII_RMII_SMII_SSMII_TBI_RTBI
- open系统调用实现原理