内容简介:年底了,赶项目,于是忙了一个月业务,忙了一个月没有营养的东西。为啥说没营养,因为就是很简简单单的展示,没有啥东西可写。我差点要搬出11月份的腾讯面试经历了,就在这时我给自己挖了个坑。 我本人的自定义View的能力是很差的,之前也没有写过,一直都用android自带或者github上写好的东西。所以这个坑挖的还是值。之前我们有一个报警消息展示界面,是这样的;有个功能是这样的,红点显示未读,点击一下就能消灭红点。
年底了,赶项目,于是忙了一个月业务,忙了一个月没有营养的东西。为啥说没营养,因为就是很简简单单的展示,没有啥东西可写。我差点要搬出11月份的腾讯面试经历了,就在这时我给自己挖了个坑。 我本人的自定义View的能力是很差的,之前也没有写过,一直都用android自带或者github上写好的东西。所以这个坑挖的还是值。
坑的来源
之前我们有一个报警消息展示界面,是这样的;
有个功能是这样的,红点显示未读,点击一下就能消灭红点。
问题就来了:后台表示不能提供是否已读的状态,我表示我这边本地存储报警消息状态并不合理。然后我就骚了一波,说接口不用改,我自己这边处理。其实我想的就是 仿微信朋友圈里面的文字分割线“以下是已读内容” ,这样就不用处理每一条消息了,哈哈哈哈哈哈哈。
两种方案与思路
一开始我想到了两种方案:
A :类似于添加head,footer,写个新的viewholder进去。
优点:网文较多;布局复杂的情况下比较好管理修改;
缺点:修改的东西比较多。
B:自定义RecyclerView.ItemDecoration
优点:修改东西较少;自定义的优点;
缺点:自定义的缺点;\
思路:无论是A方案还是B方案,我都需要知道这个分割线的position,在这里我是将上一次请求到的数据中最新一条的createTime存入SP中,我将通过这个值去对比每一次请求下来的数据集的createTime,当他相等时,这个item的position,就应该是分割线的position。(这里选择对比条件是一定要选择一个唯一,不重复的)。
在A方案中,adapter得到list后,可以找到分割线的position,然后在此position返回TextDivider的Viewholder。麻烦在于position之后的数据,TextDivider之后的每一个数据的position都必须+1。每一次都得重新去算。每次滑动都会算,这里处理起来可能不是很方便,而且会增加许多属性帮助确定真正的position。弃之
所以我选择了B方案。也是对自己个机会去学习自定义view。
“懒惰是第一生产力” —— 沃·兹基朔德
RecyclerView.ItemDecoration
public class TextDivider extends RecyclerView.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){} } 复制代码
创建一个类去继承RecyclerView.ItemDecoration,有三个方法需要重写;
执行顺序是getItemOffsets(),onDraw(),onDrawOver();
看名字,ItemDecoration是一个装饰者,并且是给每一个item加一个装饰。我们常用场景是写个分割线,各种分割线,希望大家能通过我这篇文章,对ItemDecoration有更多新的骚操作。
我们先来说关于这三个方法的用法。
getItemOffsets
第一个参数Rect,看名字不是不太容易知道有啥用。其实它就是我们当前item的矩形。我们可以通过这个参数获取到他的top、bottom、left、right。也可以给这几个属性赋值。当我们不给这几个参数赋值时,默认为0;
当我们设置了rect的参数之后,就有了上图左边的效果,如果不赋值,默认就是右边这个样子。
onDraw与onDrawOver
这就是当灵魂画家的部分了,用canvas可以画你想画的东西。
parent帮助你获取当前item的属性。
state获取当前recycleView的状态。
这两个方法的区别在于先后顺序。
onDraw画的东西会被item布局挡住;
item布局里的东西会被onDrawOver挡住;
明白了吧?
左边的圆就是onDraw画的,右边的圆就是onDrawOver画的
tips!!!上一个的item可能会被下一个item的onDraw东西给挡住,所以在画的时候一定要控制好你的范围。
代码!安排!
private int bottomDevider;//分割线宽度 private int topDevider;//文字分割宽度 private String textString;//分割线的文字 Rect textBounds = new Rect(); private Paint dividerPaint; private Paint textPaint; private Long lastReadMsgDate;//上次获取数据集的最新数据的createtime 复制代码
除了textBounds ,其他都很容易理解是干嘛的。
public TextDivider(Context context) { dividerPaint = new Paint(); textPaint = new Paint(); //设置分割线颜色 dividerPaint.setColor(context.getResources().getColor(R.color.whitesmoke)); textPaint.setColor(context.getResources().getColor(R.color.vpi__bright_foreground_disabled_holo_dark)); textString = "--------------以-下-是-已-阅-读-内-容--------------"; textPaint.setTextSize(32); textPaint.setTextAlign(Paint.Align.CENTER); //设置分割线宽度 bottomDevider = context.getResources().getDimensionPixelSize(R.dimen.space_2); topDevider = 100; lastReadMsgDate = Long.parseLong(SPM.getStr(BaseApp.getContext(), LC.CONSTANT, LC.LAST_REMIND_MSG_DATA, "0")); } 复制代码
textPaint.setTextAlign(Paint.Align.CENTER); 这句代码是让所写的文字,居于原点水平居中。
private CreateTimeListener mListener; public void setCreateTimeListener(CreateTimeListener listener) { mListener = listener; } public interface CreateTimeListener { long getCreateTime(int position); } 复制代码
这是接口用来从外部获取当前item的createTime。
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.bottom = bottomDevider; if(lastReadMsgDate == mListener.getCreateTime(parent.getChildAdapterPosition(view))){ outRect.top = topDevider; } } 复制代码
给每个item下方增加一段距离,用于画普通的分割线。
在需要画文字分割线的上方增加一段距离,用于画文字分割线
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { final int childCount = parent.getChildCount(); final int left = parent.getLeft() + parent.getPaddingLeft(); final int right = parent.getRight() - parent.getPaddingRight(); for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); if(lastReadMsgDate == mListener.getCreateTime(position)){ float top = view.getBottom(); float bottom = view.getBottom() + bottomDevider; c.drawRect(left, top, right, bottom, dividerPaint); top = view.getTop() - bottomDevider; bottom = view.getTop(); c.drawRect(left, top, right, bottom, dividerPaint); //文字居中线 float x = (view.getRight() - view.getLeft())/2; //文字所占用的边框top,bottom位置 top = view.getTop() - topDevider; bottom = view.getTop() - bottomDevider; //获取文字的Bounds textPaint.getTextBounds(textString, 0, textString.length(), textBounds); //计算文字的基线 float y = ((bottom + top)/2) + (textBounds.height()/2); c.drawText(textString, x, y, textPaint); }else { float top = view.getBottom(); float bottom = view.getBottom() + bottomDevider; c.drawRect(left, top, right, bottom, dividerPaint); } } } 复制代码
在画文字分割线的时候我觉得比较烦的就是算距离。
通常我们用canvas画东西的时候的原点,在左上角。
而文字分割线的原点在第一个字的左下角偏左一点点的距离。
文字垂直居中
关于点先生有多帅就不多讲了。这里说一说文字居中的问题。
本帅了解也不是很深, 就只找到了一种方法让它居中。
水平居中很简单,上面已经说到过了。
item的原点在左上角蓝色圆的位置,文字要想垂直居中,原点应该在紫色圆的位置。 找到紫圆的Y轴坐标就可以了。
((bottom + top)/ 2) + (文字所占的高度 / 2)
文字所占高度,就是最后的难点了。 各种get方法都找不到文字高度,最后在画文字时候传的一个参数Rect给找到方法了。
textPaint.getTextBounds(textString, 0, textString.length(), textBounds); 复制代码
跟上文说的一样,就是矩形,这里传进去的textBounds就是Rect,穿进去之后可以获取到当前文字的一些属性,问题迎刃而解。
在recycleView使用处调用也很简单。
textDivider = new TextDivider(getContext()); textDivider.setCreateTimeListener(new TextDivider.CreateTimeListener() { @Override public long getCreateTime(int position) { if (cacherRmindMsgList.size()==0) return 0L; else return cacherRmindMsgList.get(position).getCreateTime(); } }); recyclerView.addItemDecoration(textDivider); 复制代码
嘻嘻!
后续
做完之后有个疑问。为啥获取文字属性的没有一个叫get***()的方法!
还要我亲自传一个参数进去接受这些东西。给个回调接口也好啊!
打脸也挺快,自己亲手写过的接口隔离原则都差点忘了。
Rect里面这么多属性,它又不知道我要什么东西,全都回调给我,也太傻逼了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Android 自定义 View (04自定义属性)
- Vue自定义组件(简单实现一个自定义组件)
- Android 自定义View:深入理解自定义属性(七)
- Qt编写自定义控件20-自定义饼图 原 荐
- SpringBoot2 | SpringBoot自定义AutoConfiguration | SpringBoot自定义starter(五)
- 『互联网架构』软件架构-springboot自定义视图和自定义Starter(90)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
迎接互联网的明天
邹静 / 电子工业 / 2011-6 / 55.00元
《迎接互联网的明天-玩转3D Web(附盘)》,全书共5章,第1章主要阐述了国内外空前繁荣的3D互联网技术领域,以及这些领域透射出来的潜在商机;第2章主要用当下比较流行的Flash编程语言ActionScript 3,来向大家介绍面向对象编程语言的思想概念,以及一些3D渲染技术的入门知识;第3章注重建模知识的运用,主要运用WireFusion和3ds Max来制作3D网页;第4章主要介绍3D游戏编......一起来看看 《迎接互联网的明天》 这本书的介绍吧!