自定义时间轴
可配合 recyclerView 进行使用
动态的指点具体的位置
高度自定义,根据需求修改 View 即可
可设置默认 圆点 的选中,以及修改其位置
效果图如下:
public class LineCircle extends View { private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private Path path = new Path(); /** * 线的颜色 */ private int mPathColor = Color.parseColor("#eeeeee"); /** * 外圆的颜色 */ private int outerCircleColor = Color.parseColor("#96ccff"); /** * 内圆的颜色 */ private int innerCircleColor = Color.parseColor("#2988e2"); /** * 外圆半径 */ private int outRadius = Utils.dp2px(5f).intValue(); /** * 内圆半径 */ private int inRadius = Utils.dp2px(2.5f).intValue(); /** * 通过 {@link #add(int)} 添加位置的圆的半径 */ private int listRadius = Utils.dp2px(2.5f).intValue(); /** * 是否为头部 */ private boolean mHead = false; /** * 是否发为尾部 */ private boolean mEnd = false; /** * 是否选中 */ private boolean isSelect = false; //偏移 private int offsetY; /** * 默认的圆在高度上的百分比 */ private float mScale = 0.3f; /** * 原点 y 轴位置 */ private ArrayList<Integer> mList = new ArrayList<>(); public LineCircle(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { int width = getWidth(); // 默认圆的高度 int scaleHeight; if (offsetY == 0) { scaleHeight = (int) (getHeight() * mScale); } else { scaleHeight = offsetY; } int center = width / 2; paint.setColor(mPathColor); paint.setStrokeWidth(Utils.dp2px(1f)); path.moveTo(width / 2, 50); path.lineTo(width / 2, getHeight()); //画线 if (mHead) { canvas.drawLine(center, scaleHeight, center, getHeight(), paint); } else if (mEnd) { if (mList.size() > 0 && mList.get(mList.size() - 1) > scaleHeight) { canvas.drawLine(center, 0, center, mList.get(mList.size() - 1), paint); } else { canvas.drawLine(center, 0, center, scaleHeight, paint); } } else { canvas.drawLine(center, 0, center, getHeight(), paint); } //画圆 if (isSelect) { paint.setColor(outerCircleColor); canvas.drawCircle(center, scaleHeight, outRadius, paint); paint.setColor(innerCircleColor); canvas.drawCircle(center, scaleHeight, inRadius, paint); } else { paint.setColor(mPathColor); canvas.drawCircle(center, scaleHeight, listRadius, paint); } if (mList.size() > 0) { for (int i = 0; i < mList.size(); i++) { paint.setColor(mPathColor); canvas.drawCircle(center, mList.get(i), listRadius, paint); } } } /** * 添加别的圆点 * 需传入具体的高度,以像素为单位 * <p> * 全部添加完成后需调用 {@link #addSave()} , * 注意,需按顺序添加,从小往大 * </p> * * @param offsetY 偏移 */ public void add(int offsetY) { mList.add(offsetY); } public void add(List<Integer> offsetY) { mList.clear(); mList.addAll(offsetY); } /** * 添加完成后执行 */ public void addSave() { invalidate(); } public void clear() { mList.clear(); } /** * 是否为第一个 */ public void setHead(boolean head) { this.mHead = head; invalidate(); } /** * 是否为最后一个 * * @param end */ public void setEnd(boolean end) { this.mEnd = end; invalidate(); } /** * 设置线的颜色 * * @param color */ public void pathColor(int color) { this.mPathColor = color; invalidate(); } /** * 设置圆高度的百分比 * * @param mScale */ public void setScale(float mScale) { this.mScale = mScale; invalidate(); } /** * 是否选中默认的圆。 * 高度使用 {@link #setSelect(boolean)} 进行设置 * * @param isSelect */ public void setSelect(boolean isSelect) { setSelect(isSelect, 0); } /** * 是否选中默认的圆,以及对应的位置 * * @param isSelect */ public void setSelect(boolean isSelect, int offsetY) { this.isSelect = isSelect; this.offsetY = offsetY; invalidate(); } }
布局如下:
<?xml version="1.0" encoding="utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <com.fengtong.core.ui.view.LineCircle android:id="@+id/record_line" android:layout_width="15dp" android:layout_height="match_parent" android:layout_marginStart="18dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="5dp"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/record_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/_666666" android:textSize="12sp" tools:ignore="HardcodedText" tools:text="2020-06-17" /> <androidx.appcompat.widget.LinearLayoutCompat android:id="@+id/record_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/record_date" android:orientation="vertical" android:paddingBottom="20dp"> </androidx.appcompat.widget.LinearLayoutCompat> </RelativeLayout> </androidx.appcompat.widget.LinearLayoutCompat>
使用:
1,需要配合 recyclerView 使用。也可单独使用
2,调用 setSelect 方法设置是否选中默认的,以及默认圆的位置,可通过百分比,或者 具体的Y轴值来设置。
3,通过调用 add 方法添加其他的 圆点 ,需要传入Y 轴位置。
下面是在 recyclerView 中使用的:
//当前条目的类型数据 String tag = entity.getField(MultipleFields.TAG); //当前条目的数据 String date = entity.getField(MultipleFields.DATE); List<String> list = entity.getField(MultipleFields.LIST); //拿到条目中对应的控件 AppCompatTextView dateTv = holder.getView(R.id.record_date); LineCircle line = holder.getView(R.id.record_line); LinearLayoutCompat layout = holder.getView(R.id.record_layout); //设置值 dateTv.setText(date); //添加dateTv 初始化完成的监听 dateTv.getViewTreeObserver().addOnGlobalLayoutListener(() -> { // 计算dateTv 的位置 int offsetY = dateTv.getTop() + (dateTv.getHeight() / 2); //设置默认圆的位置,以及选中。也可单独设置选中,通过 setScale 设置百分比,从 0 到 1 line.setSelect(true, offsetY); }); //判断条目是否为 第一个 或者最后一个 if (tag.equals("head")) { line.setHead(true); } else if (tag.equals("end")) { line.setEnd(true); } //遍历数据,动态的创建 View 并进行添加的指定的 layout 中。 //然后计算 view 的位置,通过 add 方法进行设置。 //最后需调用 addSave for (int i = 0; i < list.size(); i++) { AppCompatTextView textView = (AppCompatTextView) LayoutInflater.from(holder.itemView.getContext()) .inflate(R.layout.layout_record_text, layout, false); textView.setText(list.get(i)); layout.addView(textView); textView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { //拿到中心点 int top = textView.getTop() + layout.getTop() + (textView.getHeight() / 2); //绘制圆点 line.add(top); }); line.addSave(); }