不断学习,做更好的自己!💪
视频号 | CSDN | 简书 |
---|---|---|
欢迎打开微信,关注我的视频号:KevinDev | 点我 | 点我 |
效果图
基类代码
适配器
BaseAdapter.java
/**
* Created on 2021/7/16 14:50
*
* @author Gong Youqiang
*/
@SuppressWarnings("ALL")
public abstract class BaseAdapter<Data> extends RecyclerView.Adapter<BaseAdapter.ViewHolder<Data>>
implements View.OnClickListener,View.OnLongClickListener,IAdapterProxy<Data>{
// 数据集合
protected List<Data> mDataList;
// 监听器
private AdapterListener<Data> adapterListener;
public BaseAdapter() {
this(null);
}
public BaseAdapter(AdapterListener<Data> adapterListener) {
this(new ArrayList<Data>(),adapterListener);
}
public BaseAdapter(List<Data> mDataList, AdapterListener<Data> adapterListener) {
this.mDataList = mDataList;
this.adapterListener = adapterListener;
}
@Override
public ViewHolder<Data> onCreateViewHolder(ViewGroup parent, int viewType) {
// 创建ViewHolder
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View root = inflater.inflate(viewType,parent,false);
ViewHolder<Data> viewHolder = onCreateViewHolder(root,viewType);
// 基础的操作
root.setTag(R.id.recycler_view,viewHolder);
root.setOnClickListener(this);
root.setOnLongClickListener(this);
doWithRoot(viewHolder,root);
return viewHolder;
}
protected void doWithRoot(ViewHolder viewHolder,View root){
}
/**
* 实际的创建ViewHolder的方法
*/
public abstract ViewHolder<Data> onCreateViewHolder(View root, int viewType);
@Override
public void onBindViewHolder(ViewHolder<Data> holder, int position) {
// 设置不能进行重复绘制
//holder.setIsRecyclable(false);
// TODO 对多数据进行测试,查看哪里出了问题
// 绑定数据
Data data = mDataList.get(position);
holder.bind(data);
}
@Override
public int getItemViewType(int position) {
Data data = mDataList.get(position);
return getItemLayout(data,position);
}
/**
* 得到子布局的ID 适合多种子布局的情况下使用
*/
public abstract int getItemLayout(Data data,int position);
@Override
public int getItemCount() {
return mDataList.size();
}
@Override
public void onClick(View v) {
ViewHolder<Data> holder = (ViewHolder<Data>) v.getTag(R.id.recycler_view);
if(holder != null){
if(adapterListener == null)
return;
int pos = holder.getAdapterPosition();
adapterListener.onItemClick(holder,mDataList.get(pos));
}
}
@Override
public boolean onLongClick(View v) {
ViewHolder<Data> holder = (ViewHolder<Data>) v.getTag(R.id.recycler_view);
if(holder != null){
if(adapterListener != null){
int pos = holder.getAdapterPosition();
adapterListener.onItemLongClick(holder,mDataList.get(pos));
return true;
}
}
return false;
}
/**
* 得到数据
*/
public List<Data> getItems() {
return mDataList;
}
/**
* 新增一个数据
*/
public void add(Data data){
mDataList.add(data);
notifyItemInserted(mDataList.size() -1 );
}
/**
* 新增所有的数据
*/
public void addAllData(Collection<Data> datas){
int start = mDataList.size();
mDataList.addAll(datas);
notifyItemRangeChanged(start,datas.size());
}
/**
* 新增所有的数组数据
*/
public void addAllData(Data... datas){
int start = mDataList.size();
mDataList.addAll(Arrays.asList(datas));
notifyItemRangeChanged(start,datas.length);
}
/**
* 删除所有的数据
*/
public void remove(){
mDataList.clear();
notifyDataSetChanged();
}
/**
* 替换数据
*/
public void replace(Collection<Data> datas){
mDataList.clear();
mDataList.addAll(datas);
notifyDataSetChanged();
}
public void setAdapterListener(AdapterListener<Data> listener){
this.adapterListener = listener;
}
/*
* 适配器的监听器
*/
public interface AdapterListener<Data>{
// 单击的时候
void onItemClick(ViewHolder<Data> holder, Data data);
// 长按的时候
void onItemLongClick(ViewHolder<Data> holder, Data data);
}
/**
* 自定义的ViewHolder
*/
public static abstract class ViewHolder<Data> extends RecyclerView.ViewHolder{
protected Data mData;
public ViewHolder(View itemView) {
super(itemView);
}
public void bind(Data data){
mData = data;
onBind(data);
}
/**
* 实现数据的绑定
*/
protected abstract void onBind(Data data);
}
public abstract static class AdapterListenerImpl<Data> implements AdapterListener<Data>{
@Override
public void onItemClick(ViewHolder<Data> holder,Data data) {
}
@Override
public void onItemLongClick(ViewHolder<Data> holder,Data data) {
}
}
}
IAdapterProxy.java
/**
* Created on 2021/7/16 14:52
*
* @author Gong Youqiang
*/
public interface IAdapterProxy<Data>{
void addAllData(Collection<Data> dataList);
void setAdapterListener(BaseAdapter.AdapterListener<Data> listener);
}
2. 数据
ITimeItem.java
/**
* Created on 2021/7/16 14:56
* 时间轴数据需要实现的接口
* @author Gong Youqiang
*/
public interface ITimeItem {
/**
* 构建绘制的标题
* @return 标题
*/
String getTitle();
/**
* 用户绘制原点的颜色
* @return 颜色
*/
int getColor();
/**
* 图片的资源文件
* @return drawable的资源地址
*/
int getResource();
}
3. ItemDecoration
TimeLine.java
/**
* Created on 2021/7/16 14:54
*
* @author Gong Youqiang
*/
public abstract class TimeLine extends RecyclerView.ItemDecoration {
// 标题
public static final int FLAG_TITLE_POS_NONE = 0X0001;
public static final int FLAG_TITLE_TYPE_TOP = 0x0002;
public static final int FLAG_TITLE_TYPE_LEFT = 0x0004;
public static final int FLAG_TITLE_DRAW_BG = 0x0008;
public static final int FLAG_SAME_TITLE_HIDE = 0x0100;
// 时间线
public static final int FLAG_LINE_DIVIDE = 0x0010;
public static final int FLAG_LINE_CONSISTENT = 0x0020;
public static final int FLAG_LINE_BEGIN_TO_END= 0x0040;
// 时间点
public static final int FLAG_DOT_RES = 0x1000;
public static final int FLAG_DOT_DRAW = 0x2000;
protected Context mContext;
protected List<? extends ITimeItem> timeItems;
// 标题放置的类型
protected int mFlag;
// 上次的标题
// 标题分两种,
// 1. 上方
// 2. 左侧
protected int mTitleColor;
protected int mTopOffset;
protected int mLeftOffset;
protected Paint mTextPaint;
protected int mTitleFontSize;
protected int mBgColor;
protected Paint mBgPaint;
// 线
protected int mLineColor;
protected Paint mLinePaint;
protected int mLineOffset;
protected int mLineWidth;
// 点
protected Paint mDotPaint;
public TimeLine(Config config) {
mContext = config.context;
this.timeItems = config.timeItems;
this.mFlag = config.flag;
// 标题
this.mTitleColor = config.titleColor;
if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
mTopOffset = DisplayUtils.dip2px(config.titleOffset);
} else if ((mFlag & FLAG_TITLE_TYPE_LEFT) != 0) {
mLeftOffset = DisplayUtils.dip2px(config.titleOffset);
}
this.mTitleFontSize = DisplayUtils.sp2px(mContext, config.titleFontSize);
this.mBgColor = config.bgColor;
// 时间线
this.mLineColor = config.lineColor;
this.mLineOffset = DisplayUtils.dip2px(config.lineOffset);
this.mLineWidth = DisplayUtils.dip2px(config.lineWidth);
init();
}
private void init() {
mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mLinePaint.setColor(mLineColor);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mTextPaint.setTextSize(mTitleFontSize);
mTextPaint.setColor(mTitleColor);
mBgPaint = new Paint();
mBgPaint.setColor(mBgColor);
mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
}
/**
* 更新部分数据
*/
public void addItems(List items){
this.timeItems.addAll(items);
}
/**
* 更新全部数据
* @param items 数据
*/
public void replace(List<? extends ITimeItem> items){
this.timeItems = items;
}
/**
* 清除数据
*/
public void remove(){
this.timeItems.clear();
}
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
// 兼容4.0硬件加速无效
parent.setLayerType(View.LAYER_TYPE_SOFTWARE,mDotPaint);
int childCount = parent.getChildCount();
if (childCount == 0)
return;
// 绘制处理
// 1. 绘制标题
drawTitle(c, parent);
// 2. 绘制线
drawVerticalLine(c, parent);
// 3. 绘制点
drawPoint(c, parent);
}
/**
* 绘制标题
*/
protected abstract void drawTitle(Canvas canvas, RecyclerView parent);
/**
* 绘制直线
* @param c Canvas
* @param parent RecyclerView
*/
protected abstract void drawVerticalLine(Canvas c, RecyclerView parent);
/**
* 绘制点
* @param c Canvas
* @param parent RecyclerView
*/
protected abstract void drawPoint(Canvas c, RecyclerView parent);
public static class Config {
Context context;
List<? extends ITimeItem> timeItems = new ArrayList<>();
int flag = 0;
// 标题
int titleColor = Color.parseColor("#4e5864");
int titleFontSize = 20;
int bgColor;
int titleOffset = 40;
// 线
int lineColor = Color.parseColor("#8d9ca9");
int lineOffset = 30;
int lineWidth = 1;
}
public static class Builder {
private Config mConfig;
public Builder(Context context) {
this(context,new ArrayList());
}
public Builder(Context context,List<? extends ITimeItem> timeItems) {
this.mConfig = new Config();
this.mConfig.context = context;
this.mConfig.timeItems = timeItems;
}
/**
* 设置标题
*
* @param titleColor 标题文本的颜色
* @param fontSize 标题文本的大小 dp
* @param bgColor 背景颜色
*/
public Builder setTitle(int titleColor, int fontSize, int bgColor) {
this.mConfig.titleColor = titleColor;
this.mConfig.titleFontSize = fontSize;
this.mConfig.bgColor = bgColor;
this.mConfig.flag |= FLAG_TITLE_DRAW_BG;
return this;
}
/**
* 设置标题
*
* @param titleColor 标题文本的颜色
* @param fontSize 标题文本的大小 dp
*/
public Builder setTitle(int titleColor, int fontSize) {
this.mConfig.titleColor = titleColor;
this.mConfig.titleFontSize = fontSize;
return this;
}
/**
* 可以设置Title的位置,比如将标题设置在顶部或者将标题设置左边
*
* @param type 类型 FLAG_TITLE_POS_NONE/FLAG_TITLE_TYPE_TOP/FLAG_TITLE_TYPE_LEFT
* @param titleOffset 偏移量
*/
public Builder setTitleStyle(int type, int titleOffset) {
this.mConfig.flag |= type;
this.mConfig.titleOffset = titleOffset;
return this;
}
/**
* 启动隐藏相同标题
*/
public Builder setSameTitleHide() {
this.mConfig.flag |= FLAG_SAME_TITLE_HIDE;
return this;
}
/**
* @param type type 时间线的类型
* @param lineOffset 时间轴左边偏移的大小,右边也会偏移同样的大小
*/
public Builder setLine(int type, int lineOffset, int lineColor) {
return setLine(type, lineOffset, lineColor,1);
}
/**
* @param type type 时间线的类型
* @param lineOffset 时间轴左边偏移的大小,右边也会偏移同样的大小
*/
public Builder setLine(int type, int lineOffset, int lineColor,int lineWidth) {
this.mConfig.flag |= type;
this.mConfig.lineOffset = lineOffset;
this.mConfig.lineColor = lineColor;
this.mConfig.lineWidth = lineWidth;
return this;
}
/**
* 设置原点
*
* @param type 点的类型
*/
public Builder setDot(int type) {
this.mConfig.flag |= type;
return this;
}
/**
* 构建
*
* @param cls 构建的类
* @return T
*/
public TimeLine build(Class<? extends TimeLine> cls) {
TimeLine t = null;
try {
Constructor<? extends TimeLine> con = cls.getConstructor(Config.class);
t = con.newInstance(mConfig);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
return t;
}
}
}
SingleTimeLineDecoration.java
/**
* Created on 2021/7/16 14:54
*
* @author Gong Youqiang
*/
public abstract class SingleTimeLineDecoration extends TimeLine {
public SingleTimeLineDecoration(Config config) {
super(config);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
int pos = params.getViewAdapterPosition();
ITimeItem timeItem = timeItems.get(pos);
if ((mFlag & FLAG_SAME_TITLE_HIDE) != 0) {
if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
if (pos == 0 || !timeItem.getTitle().equals(timeItems.get(pos - 1).getTitle())) {
outRect.set(mLineOffset + mLineWidth, mTopOffset, 0, 0);
} else {
outRect.set(mLineOffset + mLineWidth, 0, 0, 0);
}
} else {
outRect.set(mLineOffset + mLineWidth + mLeftOffset, 0, 0, 0);
}
} else {
if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
outRect.set(mLineOffset + mLineWidth, mTopOffset, 0, 0);
} else {
outRect.set(mLineOffset + mLineWidth + mLeftOffset, 0, 0, 0);
}
}
}
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount = parent.getChildCount();
if (childCount == 0)
return;
// 绘制处理
// 1. 绘制标题
drawTitle(c, parent);
// 2. 绘制线
drawVerticalLine(c, parent);
// 3. 绘制点
drawPoint(c, parent);
}
/**
* 绘制标题
*/
@Override
protected void drawTitle(Canvas canvas, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
// 适配器中的实际位置
int pos = params.getViewAdapterPosition();
ITimeItem timeItem = timeItems.get(pos);
int mLeft, mTop, mRight, mBottom;
// 绘制上侧 FLAG_TITLE_TYPE_TOP 和左侧 FLAG_TITLE_TYPE_LEFT略有不同
// 1. 上侧
// 绘制区域:
// 上下:高度 = mTopOffset, 在子视图之上,间隔着子视图的topMargin的长度
// 左右:宽度 = 子视图宽度 + 左右的margin长度
// 2. 左侧
// 绘制区域
// 上下:高度 = 子视图高度
// 左右:宽度 = 时间线偏移量 + 时间线的宽度 + 标题偏移量, 间隔着左margin的长度
if ((mFlag & FLAG_SAME_TITLE_HIDE) != 0) {
if (i == 0 && pos != 0) {
if (timeItem.getTitle().equals(timeItems.get(pos - 1).getTitle())) {
continue;
}
}
if (pos != 0 && timeItem.getTitle().equals(timeItems.get(pos - 1).getTitle())) {
continue;
}
if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
mLeft = parent.getPaddingLeft();
mTop = child.getTop() - params.topMargin - mTopOffset;
mRight = child.getRight() + params.rightMargin;
mBottom = child.getTop() - params.topMargin;
// 绘制背景
if ((mFlag & FLAG_TITLE_DRAW_BG) != 0)
canvas.drawRect(mLeft, mTop, mRight, mBottom, mBgPaint);
onDrawTitleItem(canvas, mLeft, mTop, mRight, mBottom, pos);
} else if ((mFlag & FLAG_TITLE_TYPE_LEFT) != 0) {
mLeft = parent.getPaddingLeft();
mTop = child.getTop();
mRight = child.getLeft() - params.leftMargin - (mLineOffset + mLineWidth);
mBottom = child.getBottom();
if ((mFlag & FLAG_TITLE_DRAW_BG) != 0)
canvas.drawRect(mLeft, mTop, mRight, mBottom, mBgPaint);
onDrawTitleItem(canvas, mLeft, mTop, mRight, mBottom, pos);
}
} else {
if ((mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
// 绘制上面的标题
mLeft = parent.getPaddingLeft() + params.leftMargin;
mTop = child.getTop() - params.topMargin - mTopOffset;
mRight = child.getRight();
mBottom = child.getTop() - params.topMargin;
if ((mFlag & FLAG_TITLE_DRAW_BG) != 0)
canvas.drawRect(mLeft, mTop, mRight, mBottom, mBgPaint);
onDrawTitleItem(canvas, mLeft, mTop, mRight, mBottom, pos);
} else if ((mFlag & FLAG_TITLE_TYPE_LEFT) != 0) {
mLeft = parent.getPaddingLeft();
mTop = child.getTop();
mRight = child.getLeft() - params.leftMargin - (mLineOffset + mLineWidth);
mBottom = child.getBottom();
if ((mFlag & FLAG_TITLE_DRAW_BG) != 0)
canvas.drawRect(mLeft, mTop, mRight, mBottom, mBgPaint);
onDrawTitleItem(canvas, mLeft, mTop, mRight, mBottom, pos);
}
}
}
}
/**
* 绘制标题
*
* @param left 绘制文本区域范围
* @param top 绘制文本区域范围
* @param right 绘制文本区域范围
* @param bottom 绘制文本区域范围
* @param pos 使用数据的位置
*/
protected abstract void onDrawTitleItem(Canvas canvas, int left, int top, int right, int bottom, int pos);
@Override
protected void drawVerticalLine(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
int childCount = parent.getChildCount();
// 绘制时间线的x坐标
int beginX = left + mLineOffset / 2 + mLeftOffset;
int endX = beginX + mLineWidth;
int beginY = 0, endY = 0;
if ((mFlag & FLAG_LINE_DIVIDE) != 0) {
// 给相同的标题画 时间线
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int pos = params.getViewAdapterPosition();
ITimeItem item = timeItems.get(pos);
if (pos == 0 || !item.getTitle().equals(timeItems.get(pos - 1).getTitle()) || pos == timeItems.size() - 1
|| i== childCount -1) {
if (pos == 0) {
beginY = (child.getTop() + child.getBottom()) / 2;
} else if (pos == timeItems.size() - 1) {
endY = (child.getTop() + child.getBottom()) / 2;
c.drawRect(beginX, beginY, endX, endY, mLinePaint);
} else if(!item.getTitle().equals(timeItems.get(pos - 1).getTitle())) {
//Log.e(SingleTimeLineDecoration.class.getSimpleName(),"i:"+i+",pos:"+pos);
View lastChild = parent.getChildAt(i - 1);
if(lastChild != null) {
endY = (lastChild.getTop() + lastChild.getBottom()) / 2;
if (endY != beginY) {
c.drawRect(beginX, beginY, endX, endY, mLinePaint);
}
}
beginY = (child.getTop() + child.getBottom()) / 2;
}else {
if(childCount == 1)
continue;
endY = child.getBottom();
c.drawRect(beginX, beginY, endX, endY, mLinePaint);
}
}
}
} else if ((mFlag & FLAG_LINE_BEGIN_TO_END) != 0) {
View lastChild = parent.getChildAt(childCount - 1);
RecyclerView.LayoutParams lastParams = (RecyclerView.LayoutParams) lastChild.getLayoutParams();
View firstChild = parent.getChildAt(0);
RecyclerView.LayoutParams firstParams = (RecyclerView.LayoutParams) firstChild.getLayoutParams();
if (firstParams.getViewAdapterPosition() == 0) {
beginY = (firstChild.getTop() + firstChild.getBottom()) / 2;
} else {
beginY = firstChild.getTop();
}
if (lastParams.getViewAdapterPosition() == timeItems.size() - 1) {
endY = (lastChild.getBottom() + lastChild.getTop()) / 2;
} else {
endY = lastChild.getBottom();
}
c.drawRect(beginX, beginY, endX, endY, mLinePaint);
} else {
View lastChild = parent.getChildAt(childCount - 1);
beginY = parent.getTop();
endY = lastChild.getBottom();
c.drawRect(beginX, beginY, endX, endY, mLinePaint);
}
}
@Override
protected void drawPoint(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
// 圆心坐标
int cx, cy;
int top = child.getTop();
int bottom = child.getBottom();
cy = (bottom + top) / 2;
int r = (bottom - top) / 2;
r = Math.min((mLineOffset + mLineWidth) / 2, r);
if ((mFlag & FLAG_TITLE_POS_NONE) != 0 || (mFlag & FLAG_TITLE_TYPE_TOP) != 0) {
cx = (mLineOffset + (mLineWidth + 1)) / 2;
} else {
cx = mLeftOffset + (mLineOffset + (mLineWidth + 1)) / 2;
}
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int pos = params.getViewAdapterPosition();
if ((mFlag & FLAG_DOT_RES) != 0) {
ITimeItem timeItem = timeItems.get(pos);
if (timeItem != null) {
Drawable drawable = ContextCompat.getDrawable(mContext, timeItem.getResource());
onDrawDotResItem(c, cx, cy, r, drawable, pos);
}
} else
onDrawDotItem(c, cx, cy, r, pos);
}
}
/**
* 绘制原点
*
* @param cx 圆心x
* @param cy 圆心y
* @param radius 最大半径
* @param pos 位置
*/
protected void onDrawDotItem(Canvas canvas, int cx, int cy, int radius, int pos) {
}
/**
* @param cx 圆心X
* @param cy 圆心Y
* @param radius 最大半径
* @param drawable 绘制的Drawable
* @param pos 位置
*/
protected void onDrawDotResItem(Canvas canvas, int cx, int cy, int radius, Drawable drawable, int pos) {
}
}
实例
第一步: 继承 SingleTimeLineDecoration
/**
* Created on 2021/7/16 15:00
*
* @author Gong Youqiang
*/
public class StepSTL extends SingleTimeLineDecoration {
private Paint mRectPaint;
public StepSTL(SingleTimeLineDecoration.Config config) {
super(config);
mRectPaint = new Paint();
mRectPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));
mDotPaint.setMaskFilter(new BlurMaskFilter(6, BlurMaskFilter.Blur.SOLID));
}
@Override
protected void onDrawTitleItem(Canvas canvas, int left, int top, int right, int bottom, int pos) {
ITimeItem item = timeItems.get(pos);
int rectWidth = DisplayUtils.dip2px(120);
int height = bottom - top;
int paddingLeft = DisplayUtils.dip2px(10);
mRectPaint.setColor(item.getColor());
canvas.drawRoundRect(left+paddingLeft,top,left+rectWidth,bottom,DisplayUtils.dip2px(6),DisplayUtils.dip2px(6),mRectPaint);
String title = item.getTitle();
if(TextUtils.isEmpty(title))
return;
Rect mRect = new Rect();
mTextPaint.getTextBounds(title,0,title.length(),mRect);
int x = left + (rectWidth - mRect.width())/2;
//int x = left + UIUtils.dip2px(20);
int y = bottom - (height - mRect.height())/2;
canvas.drawText(title,x,y,mTextPaint);
}
@Override
protected void onDrawDotItem(Canvas canvas, int cx, int cy, int radius, int pos) {
ITimeItem item = timeItems.get(pos);
mDotPaint.setColor(item.getColor());
canvas.drawCircle(cx,cy,DisplayUtils.dip2px(6),mDotPaint);
}
}
第二步: 创建数据,实现 ITimeItem 接口
/**
* Created on 2021/7/16 15:07
*
* @author Gong Youqiang
*/
public class TimeItem implements ITimeItem {
private String name;
private String title;
private String detail;
private int color;
private int res;
public TimeItem(String name, String title, String detail, int color, int res) {
this.name = name;
this.title = title;
this.detail = detail;
this.color = color;
this.res = res;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setTitle(String title) {
this.title = title;
}
public void setColor(int color) {
this.color = color;
}
public int getRes() {
return res;
}
public void setRes(int res) {
this.res = res;
}
@Override
public String getTitle() {
return title;
}
@Override
public int getColor() {
return color;
}
@Override
public int getResource() {
return res;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public static List<TimeItem> initStepInfo(){
List<TimeItem> items = new ArrayList<>();
items.add(new TimeItem("完善信息", "实践探究", "+30积分", Color.parseColor("#F57F17"), 0));
items.add(new TimeItem("了解基地", "实践探究", "+30积分", Color.parseColor("#F57F17"), 0));
items.add(new TimeItem("知识储备", "实践探究", "+30积分", Color.parseColor("#F57F17"), 0));
items.add(new TimeItem("安全教育主题馆", "实践探究", "+30积分", Color.parseColor("#F57F17"), 0));
items.add(new TimeItem("评价教师", "总结拓展", "+30积分", Color.parseColor("#0D47A1"), 0));
items.add(new TimeItem("评价路线", "总结拓展", "+30积分", Color.parseColor("#0D47A1"), 0));
return items;
}
}
第三步:创建适配器
/**
* Created on 2021/7/16 15:25
*
* @author Gong Youqiang
*/
public abstract class RecyclerAdapter<Data> extends BaseAdapter<Data> {
public RecyclerAdapter() {
}
public RecyclerAdapter(AdapterListener<Data> adapterListener) {
super(adapterListener);
}
public RecyclerAdapter(List<Data> mDataList, AdapterListener<Data> adapterListener) {
super(mDataList, adapterListener);
}
@Override
protected void doWithRoot(BaseAdapter.ViewHolder viewHolder, View root) {
super.doWithRoot(viewHolder, root);
((RecyclerAdapter.ViewHolder)viewHolder).unbinder = ButterKnife.bind(viewHolder,root);
}
/**
* 自定义的ViewHolder
*/
public static abstract class ViewHolder<Data> extends BaseAdapter.ViewHolder<Data>{
public Unbinder unbinder;
public ViewHolder(View itemView) {
super(itemView);
}
}
}
第四步:创建时间轴
public class SingleTimeLineActivity extends BaseActivity {
@BindView(R.id.rv_content)
RecyclerView mRecyclerView;
private RecyclerAdapter<TimeItem> mAdapter;
@Override
public int getLayoutId() {
return R.layout.activity_single_time_line;
}
@Override
public void initView() {
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter = new RecyclerAdapter<TimeItem>() {
@Override
public ViewHolder<TimeItem> onCreateViewHolder(View root, int viewType) {
return new TimeLineViewHolder(root);
}
@Override
public int getItemLayout(TimeItem timeItem, int position) {
return R.layout.step_recycle_item;
}
});
List<TimeItem> timeItems = TimeItem.initStepInfo();
mAdapter.addAllData(timeItems);
TimeLine decoration = new SingleTimeLineDecoration.Builder(this, timeItems)
.setTitle(Color.parseColor("#ffffff"), 20)
.setTitleStyle(SingleTimeLineDecoration.FLAG_TITLE_TYPE_TOP, 40)
.setLine(SingleTimeLineDecoration.FLAG_LINE_DIVIDE, 50, Color.parseColor("#8d9ca9"))
.setDot(SingleTimeLineDecoration.FLAG_DOT_DRAW)
.setSameTitleHide()
.build(StepSTL.class);
mRecyclerView.addItemDecoration(decoration);
}
class TimeLineViewHolder extends RecyclerAdapter.ViewHolder<TimeItem> {
@BindView(R.id.tv_title)
TextView mTitleTv;
@BindView(R.id.tv_content)
TextView mContentTv;
public TimeLineViewHolder(View itemView) {
super(itemView);
}
@Override
protected void onBind(TimeItem timeItem) {
mTitleTv.setText(timeItem.getName());
mContentTv.setText(timeItem.getDetail());
}
}
}
第五步 工具类
package com.hk.launcherdemo.utils;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.GradientDrawable;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.LayoutRes;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* Created on 2021/6/30 16:35
*
* @author Gong Youqiang
*/
public class DisplayUtils {
public static int dpToPx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}
public static int pxToDp(float px) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, px, Resources.getSystem().getDisplayMetrics());
}
public static int getViewMeasuredHeight(TextView tv) {
tv.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
return tv.getMeasuredHeight();
}
/**
* dp转px
*/
public static int dip2px(Context context, float dpValue) {
float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* dp转px
*/
public static int dip2px(float dpValue) {
float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 将px值转换为sp值,保证文字大小不变
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将sp值转换为px值,保证文字大小不变
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
public static boolean hasEmpty(List<TextView> edits) {
for (TextView editText : edits) {
if (TextUtils.isEmpty(editText.getText().toString().trim())) {
return true;
}
}
return false;
}
public static boolean hasEmpty(TextView... edits) {
for (TextView editText : edits) {
if (TextUtils.isEmpty(editText.getText().toString().trim())) {
return true;
}
}
return false;
}
public static boolean hasEmpty(ImageView[] edits) {
for (ImageView imageView : edits) {
if (TextUtils.isEmpty(imageView.getTag().toString().trim())) {
return true;
}
}
return false;
}
public static View inflaterLayout(Context context, @LayoutRes int layoutRes) {
LayoutInflater inflater = LayoutInflater.from(context);
return inflater.inflate(layoutRes, null);
}
/**
* 圆角Drawable
*
* @param radius 圆角
* @param color 填充颜色
*/
public static GradientDrawable getShapeDrawable(int radius, @ColorInt int color) {
GradientDrawable gd = new GradientDrawable();
gd.setColor(color);
gd.setCornerRadius(radius);
return gd;
}
private static SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd");
/**
* 日期转yyyy-MM-dd格式
* @param date 日期
* @return String
*/
public static String date2SDayFormat(Date date){
return dayFormat.format(date);
}
}