先看效果
导入依赖
implementation 'androidx.recyclerview:recyclerview:1.2.1'
acrivity_timeline.xml
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/my_rv"
android:layout_width="409dp"
android:layout_height="729dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
list_cell.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10sp"
android:text="New Text"
android:id="@+id/item_title" />
<TextView
android:id="@+id/item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/item_title"
android:text="New Text"
android:textSize="10sp" />
</RelativeLayout>
TimelineActivity.java
public class TimelineActivity extends AppCompatActivity {
private RecyclerView rv;
private ArrayList<HashMap<String,Object>> listItem;
private DiyAdapter myAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timeline);
// 初始化显示的数据
initData();
// 绑定数据到RecyclerView
initView();
}
/**
* 初始化显示的数据
*/
public void initData(){
/*在数组中存放数据*/
listItem = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> map1 = new HashMap<String, Object>(20);
HashMap<String, Object> map2 = new HashMap<String, Object>(20);
HashMap<String, Object> map3 = new HashMap<String, Object>(20);
HashMap<String, Object> map4 = new HashMap<String, Object>(20);
HashMap<String, Object> map5 = new HashMap<String, Object>(20);
HashMap<String, Object> map6 = new HashMap<String, Object>(20);
map1.put("item_title", "美国谷歌公司已发出");
map1.put("item_text", "发件人:谷歌 CEO");
listItem.add(map1);
map2.put("item_title", "国际顺丰已收入");
map2.put("item_text", "等待中转");
listItem.add(map2);
map3.put("item_title", "国际顺丰转件中");
map3.put("item_text", "下一站中国");
listItem.add(map3);
map4.put("item_title", "中国顺丰已收入");
map4.put("item_text", "下一站深圳龙岗区代理");
listItem.add(map4);
map5.put("item_title", "您的包裹由骑手派件中");
map5.put("item_text", "等待派件");
listItem.add(map5);
map6.put("item_title", "深圳公司已签收");
map6.put("item_text", "收件人:你的名字");
listItem.add(map6);
}
/**
* 绑定数据到RecyclerView
*/
public void initView(){
rv = (RecyclerView) findViewById(R.id.my_rv);
//使用线性布局
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
rv.setLayoutManager(layoutManager);
rv.setHasFixedSize(true);
//用自定义分割线类设置分割线
rv.addItemDecoration(new DividerItemDecoration(this));
//为ListView绑定适配器
myAdapter = new DiyAdapter(this,listItem);
rv.setAdapter(myAdapter);
}
}
car图片所在文件夹
DividerItemDecoration.java
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
// 写右边字的画笔(具体信息)
private final Paint mPaint;
// 写左边日期字的画笔( 时间 + 日期)
private final Paint mPaint1;
private final Paint mPaint2;
// 左 上偏移长度
private final int leftInterval;
private final int topInterval;
// 轴点半径
private final int circleRadius;
// 图标
private final Bitmap mIcon;
/**
* 在构造函数里进行绘制的初始化,如画笔属性设置等
*/
public DividerItemDecoration(Context context) {
// 轴点画笔(红色)
mPaint = new Paint();
mPaint.setColor(Color.RED);
// 左边时间文本画笔(蓝色)
// 此处设置了两只分别设置 时分 & 年月
mPaint1 = new Paint();
mPaint1.setColor(Color.BLUE);
mPaint1.setTextSize(50);
mPaint2 = new Paint();
mPaint2.setColor(Color.GREEN);
mPaint2.setTextSize(30);
// 赋值ItemView的左偏移长度为300
leftInterval = 300;
// 赋值ItemView的上偏移长度为80
topInterval = 80;
// 赋值轴点圆的半径为5
circleRadius = 5;
// 获取图标资源
mIcon = BitmapFactory.decodeResource(context.getResources(), R.mipmap.car);
}
/**
* 重写getItemOffsets()方法
* 作用:设置ItemView 左 & 上偏移长度
* @param outRect
* @param view
* @param parent
* @param state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
// 设置ItemView的左 & 上偏移长度分别为300 px & 80px,即此为onDraw()可绘制的区域
outRect.set(leftInterval, topInterval, 0, 0);
}
/**
* 重写onDraw()
* 作用:在间隔区域里绘制时光轴线 & 时间文本
* @param c
* @param parent
* @param state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// 获取RecyclerView的Child view(即可见的item view)的个数
int childCount = parent.getChildCount();
// 遍历每个Item,分别获取它们的位置信息,然后再绘制对应的分割线
for (int i = 0; i < childCount; i++) {
// 获取每个Item对象
View child = parent.getChildAt(i);
/**
* 绘制轴点
*/
// 轴点 = 圆 = 圆心(x,y)
float centerX = child.getLeft() - leftInterval / 3;
float centerY = child.getTop() - topInterval + (topInterval + child.getHeight()) / 2;
// 绘制轴点圆
//c.drawCircle(centerX, centerY, circleRadius, mPaint);
// 通过Canvas绘制角标
c.drawBitmap(mIcon,centerX - circleRadius ,centerY - circleRadius,mPaint);
/**
* 绘制上半轴线
*/
// 上端点坐标(x,y)
float upLineUpY = child.getTop() - topInterval;
// 下端点坐标(x,y)
float upLineBottomY = centerY - circleRadius;
//绘制上半部轴线
c.drawLine(centerX, upLineUpY, centerX, upLineBottomY, mPaint);
/**
* 绘制下半轴线
*/
// 上端点坐标(x,y)
float downLineUpY = centerY + circleRadius + 40;
// 下端点坐标(x,y)
float downLineBottomY = child.getBottom();
//绘制下半部轴线
c.drawLine(centerX, downLineUpY, centerX, downLineBottomY, mPaint);
/**
* 绘制左边时间文本
*/
// 获取每个Item的位置
int index = parent.getChildAdapterPosition(child);
// 设置文本起始坐标
float textX = child.getLeft() - leftInterval * 5 / 6;
// 根据Item位置设置时间文本
switch (index) {
case (0):
// 设置日期绘制位置
c.drawText("13:40", textX, upLineBottomY, mPaint1);
c.drawText("2022.8.11", textX - 5, upLineBottomY + 40, mPaint2);
break;
case (1):
// 设置日期绘制位置
c.drawText("17:33", textX, upLineBottomY, mPaint1);
c.drawText("2022.8.11", textX - 5, upLineBottomY + 40, mPaint2);
break;
case (2):
// 设置日期绘制位置
c.drawText("20:13", textX, upLineBottomY, mPaint1);
c.drawText("2022.8.11", textX - 5, upLineBottomY + 40, mPaint2);
break;
case (3):
// 设置日期绘制位置
c.drawText("10:40", textX, upLineBottomY, mPaint1);
c.drawText("2022.8.12", textX - 5, upLineBottomY + 40, mPaint2);
break;
case (4):
// 设置日期绘制位置
c.drawText("11:20", textX, upLineBottomY, mPaint1);
c.drawText("2022.8.12", textX - 5, upLineBottomY + 40, mPaint2);
break;
case (5):
// 设置日期绘制位置
c.drawText("11:40", textX, upLineBottomY, mPaint1);
c.drawText("2022.8.12", textX - 5, upLineBottomY + 40, mPaint2);
break;
default:c.drawText("已签收", textX, upLineBottomY, mPaint1);
}
}
}
}
DiyAdapter .java
public class DiyAdapter extends RecyclerView.Adapter {
private ArrayList<HashMap<String,Object>> listItem;
private LayoutInflater inflater;
public DiyAdapter(Context context, ArrayList<HashMap<String,Object>> listItem) {
inflater = LayoutInflater.from(context);
this.listItem = listItem;
}
class DiyViewHolder extends RecyclerView.ViewHolder{
private TextView title,text;
public DiyViewHolder(View root){
super(root);
title = (TextView) root.findViewById(R.id.item_title);
text = (TextView) root.findViewById(R.id.item_text);
}
public TextView getTitle(){
return title;
}
public TextView getText(){
return text;
}
}
/**
* 把View绑定Item布局
* @param parent
* @param viewType
* @return
*/
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new DiyViewHolder(inflater.inflate(R.layout.list_cell,null));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
DiyViewHolder dvh = (DiyViewHolder) holder;
//绑定数据到ViewHolder
dvh.title.setText((String)listItem.get(position).get("item_title"));
dvh.text.setText((String)listItem.get(position).get("item_text"));
}
/**
* 返回Item数目
* @return
*/
@Override
public int getItemCount() {
return listItem.size();
}
}
注:
学习自https://carsonho.blog.csdn.net/article/details/75005994