![](https://img.haomeiwen.com/i9134822/7c3c6275832ae768.jpg)
效果图
![](https://img.haomeiwen.com/i9134822/ce42e1b454682cfc.png)
基类代码
适配器
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;
}
}
}
DoubleTimeLineDecoration.java
/**
* Created on 2021/7/19 9:04
*
* @author Gong Youqiang
*/
public abstract class DoubleTimeLineDecoration extends TimeLine {
public static final int LEFT = 0;
public static final int RIGHT = 1;
private int mStartSide;
public DoubleTimeLineDecoration(Config config) {
super(config);
mStartSide = LEFT;
}
/**
* 设置起始边
* @param startSide Left RIGHT
*/
public void setStartSide(int startSide) {
this.mStartSide = startSide;
}
@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);
int side = pos % 2;
if ((mFlag & FLAG_TITLE_TYPE_LEFT) != 0) {
if (side == mStartSide)
outRect.set(0, 0, (mLineOffset+mLineWidth)/2 + mLeftOffset, 0);
else
outRect.set((mLineOffset+mLineWidth)/2 + mLeftOffset, 0, 0, 0);
} else {
if (side == mStartSide)
outRect.set(0, 0, (mLineOffset+mLineWidth)/2, 0);
else
outRect.set((mLineOffset+mLineWidth)/2, 0, 0, 0);
}
}
@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);
}
/**
* 绘制标题
*/
@Override
protected void drawTitle(Canvas canvas, RecyclerView parent) {
int childCount = parent.getChildCount();
// 注意:隐藏相同标题对两侧布局不重要
int centerX = parent.getMeasuredWidth() / 2;
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
int top = child.getTop();
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int pos = params.getViewAdapterPosition();
ITimeItem timeItem = timeItems.get(pos);
int mLeft, mTop, mRight, mBottom;
if ((mFlag & FLAG_TITLE_TYPE_LEFT) != 0) {
if (child.getLeft() >= parent.getMeasuredWidth() / 2) {
mLeft = parent.getPaddingLeft();
mTop = child.getTop();
mRight = child.getLeft() - params.leftMargin;
mBottom = child.getBottom();
if ((mFlag & FLAG_TITLE_DRAW_BG) != 0)
canvas.drawRect(mLeft, mTop, mRight, mBottom, mBgPaint);
onDrawTitleItem(canvas, mLeft, mTop, mRight, mBottom, centerX, pos, false);
} else {
mLeft = child.getRight() + params.rightMargin;
mTop = child.getTop();
mRight = parent.getMeasuredWidth() - parent.getPaddingRight();
mBottom = child.getBottom();
if ((mFlag & FLAG_TITLE_DRAW_BG) != 0)
canvas.drawRect(mLeft, mTop, mRight, mBottom, mBgPaint);
onDrawTitleItem(canvas, mLeft, mTop, mRight, mBottom, centerX, pos, true);
}
}
}
}
/**
* 绘制标题
*
* @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 centerX, int pos, boolean isLeft);
@Override
protected void drawVerticalLine(Canvas c, RecyclerView parent) {
int top = parent.getPaddingTop();
final int left = parent.getPaddingLeft();
int bottom;
int childCount = parent.getChildCount();
if ((mFlag & FLAG_LINE_CONSISTENT) != 0) {
View lastChild = parent.getChildAt(childCount - 1);
bottom = lastChild.getBottom();
c.drawLine(parent.getMeasuredWidth() / 2, top, parent.getMeasuredWidth() / 2, bottom, mLinePaint);
} else {
View firstChild = parent.getChildAt(0);
top = (firstChild.getTop() + firstChild.getBottom()) / 2;
View lastChild = parent.getChildAt(childCount - 1);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) lastChild.getLayoutParams();
if(params.getViewAdapterPosition() == timeItems.size() - 1) {
bottom = (lastChild.getBottom() + lastChild.getTop()) / 2;
}else {
bottom = lastChild.getBottom();
}
c.drawLine(parent.getMeasuredWidth() / 2, top, parent.getMeasuredWidth() / 2, bottom, 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);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int pos = params.getViewAdapterPosition();
// 圆心坐标
int cx, cy;
int top = child.getTop();
int bottom = child.getBottom();
cx = parent.getMeasuredWidth() / 2;
cy = (bottom + top) / 2;
int r = (top - bottom) / 2;
r = Math.min((mLineOffset + mLineWidth) / 2, r);
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){
}
}
实例
第一步: 继承 DoubleTimeLineDecoration
/**
* Created on 2021/7/19 9:10
*
* @author Gong Youqiang
*/
public class DateInfoDTL extends DoubleTimeLineDecoration {
private static final String[] MONTHS = new String[]{
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
};
private int r;
private Paint monTextPaint;
private Paint dayTextPaint;
private int space;
public DateInfoDTL(Config config) {
super(config);
r = DisplayUtils.dip2px(24);
monTextPaint = new Paint();
monTextPaint.setTextSize(DisplayUtils.sp2px(mContext,12));
monTextPaint.setColor(Color.parseColor("#F5F5F5"));
dayTextPaint = new Paint();
dayTextPaint.setTextSize(DisplayUtils.sp2px(mContext,18));
dayTextPaint.setColor(Color.parseColor("#ffffff"));
space = DisplayUtils.dip2px(6);
mDotPaint.setMaskFilter(new BlurMaskFilter(6, BlurMaskFilter.Blur.SOLID));
}
@Override
protected void onDrawTitleItem(Canvas canvas, int left, int top, int right, int bottom, int centerX, int pos, boolean isLeft) {
// 不需要做什么
}
@Override
protected void onDrawDotItem(Canvas canvas, int cx, int cy, int radius, int pos) {
super.onDrawDotItem(canvas, cx, cy, radius, pos);
DateInfo timeItem = (DateInfo) timeItems.get(pos);
Date date = timeItem.getDate();
mDotPaint.setColor(timeItem.getColor());
canvas.drawCircle(cx,cy,r,mDotPaint);
if(date != null){
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
String mon = MONTHS[calendar.get(Calendar.MONTH)];
int n = calendar.get(Calendar.DAY_OF_MONTH);
String day = n<10?"0"+n:Integer.toString(n);
Rect monRect = new Rect();
monTextPaint.getTextBounds(mon,0,mon.length(),monRect);
Rect dayRect = new Rect();
dayTextPaint.getTextBounds(day,0,day.length(),dayRect);
int monWidth = monRect.width();
int monHeight = monRect.height();
int dayWidth = dayRect.width();
int dayHeight = dayRect.height();
int beginY = cy + r - (r * 2 - monHeight - dayHeight)/2;
canvas.drawText(day,cx-dayWidth/2,beginY,dayTextPaint);
beginY -= dayHeight + space;
canvas.drawText(mon,cx-monWidth/2,beginY,monTextPaint);
}
}
}
第二步: 创建数据,实现 ITimeItem 接口
/**
* Created on 2021/7/16 15:56
*
* @author Gong Youqiang
*/
public class DateInfo implements ITimeItem {
private String name;
private String detail;
private Date date;
private int color;
public DateInfo(String name, String detail, Date date, int color) {
this.name = name;
this.detail = detail;
this.date = date;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public void setColor(int color) {
this.color = color;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String getTitle() {
return null;
}
@Override
public int getColor() {
return color;
}
@Override
public int getResource() {
return 0;
}
public static List<DateInfo> initDateInfo() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
List<DateInfo> items = new ArrayList<>();
items.add(new DateInfo("喝茶", "第一天养养生吧~", calendar.getTime(), Color.parseColor("#f36c60")));
calendar.add(Calendar.DAY_OF_MONTH, 1);
items.add(new DateInfo("喝酒", "今天找老徐吃烧烤", calendar.getTime(), Color.parseColor("#ab47bc")));
calendar.add(Calendar.DAY_OF_MONTH, 1);
items.add(new DateInfo("画画", "去鼋头渚写生", calendar.getTime(), Color.parseColor("#aed581")));
calendar.add(Calendar.DAY_OF_MONTH, 1);
items.add(new DateInfo("高尔夫", "约个高尔夫", calendar.getTime(), Color.parseColor("#5FB29F")));
calendar.add(Calendar.DAY_OF_MONTH, 1);
items.add(new DateInfo("游泳", "今天来洗个澡", calendar.getTime(), Color.parseColor("#ec407a")));
calendar.add(Calendar.DAY_OF_MONTH, 1);
items.add(new DateInfo("温泉", "快上班了好好休息", calendar.getTime(), Color.parseColor("#ffd54f")));
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 DoubelActivity extends BaseActivity {
@BindView(R.id.rv_content)
RecyclerView mRecyclerView;
private RecyclerAdapter<DateInfo> mAdapter;
@Override
public int getLayoutId() {
return R.layout.activity_doubel;
}
@Override
public void initView() {
mRecyclerView.setLayoutManager(new DoubleSideLayoutManager(DoubleSideLayoutManager.START_LEFT, DisplayUtils.dip2px(40)));
mRecyclerView.setAdapter(mAdapter = new RecyclerAdapter<DateInfo>() {
@Override
public ViewHolder<DateInfo> onCreateViewHolder(View root, int viewType) {
return new DateInfoHolder(root);
}
@Override
public int getItemLayout(DateInfo s, int position) {
return R.layout.date_info_recycle_item;
}
});
List<DateInfo> timeItems = DateInfo.initDateInfo();
mAdapter.addAllData(timeItems);
TimeLine timeLine = provideTimeLine(timeItems);
mRecyclerView.addItemDecoration(timeLine);
}
private TimeLine provideTimeLine(List<DateInfo> timeItems) {
return new TimeLine.Builder(this, timeItems)
.setTitleStyle(TimeLine.FLAG_TITLE_POS_NONE, 0)
.setLine(TimeLine.FLAG_LINE_BEGIN_TO_END, 60, Color.parseColor("#757575"), 2)
.setDot(TimeLine.FLAG_DOT_DRAW)
.build(DateInfoDTL.class);
}
class DateInfoHolder extends RecyclerAdapter.ViewHolder<DateInfo> {
@BindView(R.id.tv_name)
TextView mNameTv;
@BindView(R.id.tv_detail)
TextView mDetailTv;
DateInfoHolder(View itemView) {
super(itemView);
}
@Override
protected void onBind(DateInfo timeItem) {
mNameTv.setText(timeItem.getName());
mDetailTv.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);
}
}
实例二
-
效果图
02.jpg
第一步: 继承 DoubleTimeLineDecoration
/**
* Created on 2021/7/19 9:48
*
* @author Gong Youqiang
*/
public class WeekPlanDTL extends DoubleTimeLineDecoration {
public WeekPlanDTL(Config config) {
super(config);
}
@Override
protected void onDrawTitleItem(Canvas canvas, int left, int top, int right, int bottom, int centerX, int pos, boolean isLeft) {
// Draw title part
ITimeItem item = timeItems.get(pos);
int height = bottom - top;
String title = item.getTitle();
if (TextUtils.isEmpty(title))
return;
Rect mRect = new Rect();
//mTextPaint.setColor(item.getColor());
mTextPaint.getTextBounds(title, 0, title.length(), mRect);
int x, y;
if (isLeft) {
x = centerX + DisplayUtils.dip2px(30);
} else {
x = centerX - DisplayUtils.dip2px(30) - mRect.width();
}
y = bottom - (height - mRect.height()) / 2;
canvas.drawText(title, x, y, mTextPaint);
}
@Override
protected void onDrawDotResItem(Canvas canvas, int cx, int cy, int radius, Drawable drawable, int pos) {
super.onDrawDotResItem(canvas, cx, cy, radius, drawable, pos);
// draw dot part
if (drawable != null) {
int height = drawable.getIntrinsicHeight();
int width = drawable.getIntrinsicWidth();
int left = cx - width / 2;
int top = cy - height / 2;
int right = cx + width / 2;
int bottom = cy + height / 2;
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
mDotPaint.setStyle(Paint.Style.STROKE);
mDotPaint.setColor(Color.parseColor("#ffffff"));
mDotPaint.setStrokeWidth(DisplayUtils.dip2px(2));
canvas.drawCircle(cx, cy, width / 2 - DisplayUtils.dip2px(3), 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;
}
public static List<TimeItem> initTimeInfo(){
List<TimeItem> items = new ArrayList<>();
items.add(new TimeItem("喝茶", "10-01,周二", "第一天养养生吧~", Color.parseColor("#f36c60"), R.drawable.timeline_ic_tea));
items.add(new TimeItem("喝酒", "06-12,周三", "今天找老徐吃烧烤", Color.parseColor("#ab47bc"), R.drawable.timeline_ic_drink));
items.add(new TimeItem("画画", "07-07,周四", "去鼋头渚写生", Color.parseColor("#aed581"), R.drawable.timeline_ic_draw));
items.add(new TimeItem("高尔夫", "08-20,周五", "约个高尔夫", Color.parseColor("#5FB29F"), R.drawable.timeline_ic_golf));
items.add(new TimeItem("游泳", "09-16,周六", "今天来洗个澡", Color.parseColor("#ec407a"), R.drawable.timeline_ic_bath));
items.add(new TimeItem("温泉", "10-01,周日", "快上班了好好休息", Color.parseColor("#ffd54f"), R.drawable.timeline_ic_footer));
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 DoubelActivity extends BaseActivity {
@BindView(R.id.rv_content)
RecyclerView mRecyclerView;
private RecyclerAdapter<TimeItem> mAdapter;
@Override
public int getLayoutId() {
return R.layout.activity_doubel;
}
@Override
public void initView() {
mRecyclerView.setLayoutManager(new DoubleSideLayoutManager(DoubleSideLayoutManager.START_LEFT, DisplayUtils.dip2px(40)));
mRecyclerView.setAdapter(mAdapter = new RecyclerAdapter<TimeItem>() {
@Override
public ViewHolder<TimeItem> onCreateViewHolder(View root, int viewType) {
return new WeekPlanViewHolder(root);
}
@Override
public int getItemLayout(TimeItem s, int position) {
return R.layout.two_side_left_recycle_item;
}
});
List<TimeItem> timeItems = TimeItem.initTimeInfo();
mAdapter.addAllData(timeItems);
TimeLine timeLine = provideTimeLine(timeItems);
mRecyclerView.addItemDecoration(timeLine);
}
private TimeLine provideTimeLine(List<TimeItem> timeItems) {
return new TimeLine.Builder(this, timeItems)
.setTitle(Color.parseColor("#8d9ca9"), 14)
.setTitleStyle(TimeLine.FLAG_TITLE_TYPE_LEFT, 0)
.setLine(TimeLine.FLAG_LINE_BEGIN_TO_END, 60, Color.parseColor("#757575"),3)
.setDot(TimeLine.FLAG_DOT_RES)
.build(WeekPlanDTL.class);
}
class WeekPlanViewHolder extends RecyclerAdapter.ViewHolder<TimeItem> {
@BindView(R.id.tv_name)
TextView mNameTv;
@BindView(R.id.tv_detail)
TextView mDetailTv;
@BindView(R.id.btn_go)
TextView mGoBtn;
@BindView(R.id.btn_write)
TextView mWriteBtn;
WeekPlanViewHolder(View itemView) {
super(itemView);
}
@Override
protected void onBind(TimeItem timeItem) {
mNameTv.setText(timeItem.getName());
mDetailTv.setText(timeItem.getDetail());
setColor(timeItem.getColor());
}
private void setColor(int color){
mGoBtn.setBackgroundColor(color);
mWriteBtn.setBackgroundColor(color);
}
}
}
网友评论