悬浮ItemDecoration
先来一个效果图:
如果你没有用过RecyclerView的分隔线,那么可以看看我的前篇文章:
绘制RecyclerView的分隔线
思路步骤
- 设置回调接口,用于判断每个item的类型,以及各个类型对应的名称
public interface DecorationListener{
int getType(int position);//获得分组的type
String getTypeText(int position);//获得该组的文字信息
}
- 继承
RecyclerView.ItemDecoration
重写getItemOffsets
将需要绘制分隔栏的地方设置偏移量,我们这里是采用分组的形式,即不同组之间才需要绘制分隔符。可以看看我的图片↑。 - 重写
onDraw
在设置了偏移量的地方绘制分隔栏 - 重写
onDrawOver
在头顶绘制悬浮分隔栏
调用顺序
首先会调用getItemOffsets,然后是onDraw,然后会绘制item本身,最后时onDrawOver,所以onDrawOver里面绘制的会覆盖掉你的view,所以这就是现实了悬浮在view之上!
代码实现
- 定义好要用的变量
private Context mContext;
private int mTitleHeight;//分隔栏高度
private Paint mPaint;//画背景的画笔
private Paint mTextPaint;//画文字的画笔
private Rect mBounds;//文字边界
private int mTextSize;
private int mTextColor;
private int mBackgroundColor;
//用于得到type和type对应的Text
private DecorationListener mListener;
- 初始化变量
public FloatingDecoration(Context context,DecorationListener listener){
mContext = context;
mTitleHeight = 100;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setColor(Color.GRAY);
mBounds = new Rect();
mListener = listener;
mTextPaint = new Paint();
mTextPaint.setColor(Color.WHITE);
mTextPaint.setAntiAlias(true);//抗锯齿
mTextPaint.setTextSize(sp2px(context,30));
}
- 设置偏移量
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildLayoutPosition(view);
if (position == 0) {//第0个肯定是新的一组
outRect.set(0, mTitleHeight, 0, 0);
} else {//其他的通过判断
if (mListener.getType(position)!= mListener.getType(position-1)) {//通过接口来获得类型
outRect.set(0, mTitleHeight, 0, 0);//跟前一个tag不一样了,说明是新的分类,也要title
} else {
outRect.set(0, 0, 0, 0);
}
}
}
- 绘制分隔栏
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
int position = params.getViewLayoutPosition();
if (position == 0) {//等于0肯定要有分组title的
drawTitleArea(c, left, right, child, params, position);
}else {//其他的通过判断
if (mListener.getType(position)!= mListener.getType(position-1)) {
//且跟前一个tag不一样了,说明是新的分组,也要title
drawTitleArea(c, left, right, child, params, position);
}
}
}
}
- 绘制文字区域
private void drawTitleArea(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) {//最先调用,绘制在最下层
c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint);
//绘制文字
mTextPaint.getTextBounds(mListener.getTypeText(position), 0,mListener.getTypeText(position).length(), mBounds);
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
//文字baseline计算方法: 矩形区域的top + bottom - fontMetrics.bottom - fontMetrics.top 除以 2
int baseline = ( child.getTop() - mTitleHeight + child.getTop() - fontMetrics.bottom - fontMetrics.top) / 2;
c.drawText(mListener.getTypeText(position), child.getPaddingLeft(), baseline, mTextPaint);
}
其实目前已经可以实现分组了!但是还没有悬浮的效果。
-
绘制顶部的悬浮分隔栏
来看一张原理图:
原理图
好,让我们来开始编码:
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {//最后调用 绘制在最上层
//获得可见的第一个view的position
int pos = ((LinearLayoutManager)(parent.getLayoutManager())).findFirstVisibleItemPosition();`
//要绘制的文字
String tag = mListener.getTypeText(pos);
//获得在屏幕上能看到的第一个view
View child = parent.getChildAt(0);
if(mListener.getType(pos)!= mListener.getType(pos+1)){
//这是最后一个
int bottom = child.getBottom();
if(bottom<mTitleHeight){
//底部小于分隔栏的高度了 说明已经上去了
//绘制背景 随着最后一个的bottom一起向上移动
//为了简洁我就没写各边的padding了
c.drawRect(0, 0, parent.getRight(), bottom, mPaint);
mPaint.getTextBounds(tag, 0, tag.length(), mBounds);
//文字
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
int baseline = (bottom + bottom -mTitleHeight - fontMetrics.bottom - fontMetrics.top) / 2;
c.drawText(tag,child.getPaddingLeft(),baseline,mTextPaint);
return;
}
}
//如果不是最后一个 那就直接绘制在RecyclerView的顶部!
//分隔线的背景
c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint);
//分组的文字
mTextPaint.getTextBounds(tag, 0, tag.length(), mBounds);
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
int baseline = (mTitleHeight - fontMetrics.bottom - fontMetrics.top) / 2;
c.drawText(tag, child.getPaddingLeft(),baseline, mTextPaint);
}
结语:其实不绘制文字,还是很容易就搞定这个的,但是要绘制文字就要做许多计算!主要就是baseline(基线)的计算,文字是绘制在基线上边的。大家可以多看看文字绘制的方法,最好动手画画,就能理解了。
网友评论