自定义 View | 时间轴

自定义时间轴

  • 可配合 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();
}

在这里插入图片描述

Guess you like

Origin blog.csdn.net/baidu_40389775/article/details/107694125