package mcxtzhang.itemdecorationdemo.decoration;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import java.util.List;
import mcxtzhang.itemdecorationdemo.CityBean;
import mcxtzhang.itemdecorationdemo.bean.BaseIndexTagBean;
/**
* 有分类title的 ItemDecoration
* Created by zhangxutong .
* Date: 16/08/28
*/
public class TitleItemDecoration extends RecyclerView.ItemDecoration {
private List<? extends BaseIndexTagBean> mDatas;
private Paint mPaint;
private Rect mBounds;//用于存放测量文字Rect
private LayoutInflater mInflater;
private int mTitleHeight;//title的高
private static int COLOR_TITLE_BG = Color.parseColor("#FFDFDFDF");
private static int COLOR_TITLE_FONT = Color.parseColor("#FF000000");
private static int mTitleFontSize;//title字体大小
public TitleItemDecoration(Context context, List<CityBean> datas) {
super();
mDatas = datas;
mPaint = new Paint();
mBounds = new Rect();
mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, context.getResources().getDisplayMetrics());
mTitleFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, context.getResources().getDisplayMetrics());
mPaint.setTextSize(mTitleFontSize);
mPaint.setAntiAlias(true);
mInflater = LayoutInflater.from(context);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, 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();
//我记得Rv的item position在重置时可能为-1.保险点判断一下吧
if (position > -1) {
if (position == 0) {//等于0肯定要有title的
drawTitleArea(c, left, right, child, params, position);
} else {//其他的通过判断
if (null != mDatas.get(position).getTag() && !mDatas.get(position).getTag().equals(mDatas.get(position - 1).getTag())) {
//不为空 且跟前一个tag不一样了,说明是新的分类,也要title
drawTitleArea(c, left, right, child, params, position);
} else {
//none
}
}
}
}
}
/**
* 绘制Title区域背景和文字的方法
*
* @param c
* @param left
* @param right
* @param child
* @param params
* @param position
*/
private void drawTitleArea(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) {//最先调用,绘制在最下层
mPaint.setColor(COLOR_TITLE_BG);
c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint);
mPaint.setColor(COLOR_TITLE_FONT);
/*
Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;*/
mPaint.getTextBounds(mDatas.get(position).getTag(), 0, mDatas.get(position).getTag().length(), mBounds);
c.drawText(mDatas.get(position).getTag(), child.getPaddingLeft(), child.getTop() - params.topMargin - (mTitleHeight / 2 - mBounds.height() / 2), mPaint);
}
@Override
public void onDrawOver(Canvas c, final RecyclerView parent, RecyclerView.State state) {//最后调用 绘制在最上层
int pos = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
String tag = mDatas.get(pos).getTag();
//View child = parent.getChildAt(pos);
View child = parent.findViewHolderForLayoutPosition(pos).itemView;//出现一个奇怪的bug,有时候child为空,所以将 child = parent.getChildAt(i)。-》 parent.findViewHolderForLayoutPosition(pos).itemView
boolean flag = false;//定义一个flag,Canvas是否位移过的标志
if ((pos + 1) < mDatas.size()) {//防止数组越界(一般情况不会出现)
if (null != tag && !tag.equals(mDatas.get(pos + 1).getTag())) {//当前第一个可见的Item的tag,不等于其后一个item的tag,说明悬浮的View要切换了
Log.d("zxt", "onDrawOver() called with: c = [" + child.getTop());//当getTop开始变负,它的绝对值,是第一个可见的Item移出屏幕的距离,
if (child.getHeight() + child.getTop() < mTitleHeight) {//当第一个可见的item在屏幕中还剩的高度小于title区域的高度时,我们也该开始做悬浮Title的“交换动画”
c.save();//每次绘制前 保存当前Canvas状态,
flag = true;
//一种头部折叠起来的视效,个人觉得也还不错~
//可与123行 c.drawRect 比较,只有bottom参数不一样,由于 child.getHeight() + child.getTop() < mTitleHeight,所以绘制区域是在不断的减小,有种折叠起来的感觉
//c.clipRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + child.getHeight() + child.getTop());
//类似饿了么点餐时,商品列表的悬停头部切换“动画效果”
//上滑时,将canvas上移 (y为负数) ,所以后面canvas 画出来的Rect和Text都上移了,有种切换的“动画”感觉
c.translate(0, child.getHeight() + child.getTop() - mTitleHeight);
}
}
}
mPaint.setColor(COLOR_TITLE_BG);
c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint);
mPaint.setColor(COLOR_TITLE_FONT);
mPaint.getTextBounds(tag, 0, tag.length(), mBounds);
c.drawText(tag, child.getPaddingLeft(),
parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2),
mPaint);
if (flag)
c.restore();//恢复画布到之前保存的状态
/* Button button = new Button(parent.getContext());
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(parent.getContext(), "啊哈", Toast.LENGTH_SHORT).show();//即使给View设置了点击事件,也是无效的,它仅仅draw了
}
});
ViewGroup.LayoutParams params = button.getLayoutParams();
if (params == null){
params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
button.setLayoutParams(params);
button.setBackgroundColor(Color.RED);
button.setText("无哈");
*//*button.measure(View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.EXACTLY),View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.EXACTLY));
*//*
//必须经过 测量 和 布局,View才能被正常的显示出来
button.measure(View.MeasureSpec.makeMeasureSpec(9999,View.MeasureSpec.UNSPECIFIED),View.MeasureSpec.makeMeasureSpec(9999,View.MeasureSpec.UNSPECIFIED));
button.layout(parent.getPaddingLeft(),parent.getPaddingTop(),
parent.getPaddingLeft()+button.getMeasuredWidth(),parent.getPaddingTop()+button.getMeasuredHeight());
button.draw(c);*/
//inflate一个复杂布局 并draw出来
/* View toDrawView = mInflater.inflate(R.layout.header_complex, parent, false);
int toDrawWidthSpec;//用于测量的widthMeasureSpec
int toDrawHeightSpec;//用于测量的heightMeasureSpec
//拿到复杂布局的LayoutParams,如果为空,就new一个。
// 后面需要根据这个lp 构建toDrawWidthSpec,toDrawHeightSpec
ViewGroup.LayoutParams lp = toDrawView.getLayoutParams();
if (lp == null) {
lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);//这里是根据复杂布局layout的width height,new一个Lp
toDrawView.setLayoutParams(lp);
}
if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
//如果是MATCH_PARENT,则用父控件能分配的最大宽度和EXACTLY构建MeasureSpec。
toDrawWidthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.EXACTLY);
} else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
//如果是WRAP_CONTENT,则用父控件能分配的最大宽度和AT_MOST构建MeasureSpec。
toDrawWidthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST);
} else {
//否则则是具体的宽度数值,则用这个宽度和EXACTLY构建MeasureSpec。
toDrawWidthSpec = View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY);
}
//高度同理
if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
toDrawHeightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(), View.MeasureSpec.EXACTLY);
} else if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
toDrawHeightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(), View.MeasureSpec.AT_MOST);
} else {
toDrawHeightSpec = View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY);
}
//依次调用 measure,layout,draw方法,将复杂头部显示在屏幕上。
toDrawView.measure(toDrawWidthSpec, toDrawHeightSpec);
toDrawView.layout(parent.getPaddingLeft(), parent.getPaddingTop(),
parent.getPaddingLeft() + toDrawView.getMeasuredWidth(), parent.getPaddingTop() + toDrawView.getMeasuredHeight());
toDrawView.draw(c);*/
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
//我记得Rv的item position在重置时可能为-1.保险点判断一下吧
if (position > -1) {
if (position == 0) {//等于0肯定要有title的
outRect.set(0, mTitleHeight, 0, 0);
} else {//其他的通过判断
if (null != mDatas.get(position).getTag() && !mDatas.get(position).getTag().equals(mDatas.get(position - 1).getTag())) {
outRect.set(0, mTitleHeight, 0, 0);//不为空 且跟前一个tag不一样了,说明是新的分类,也要title
} else {
outRect.set(0, 0, 0, 0);
}
}
}
}
}