天天看點

自定義 View | 時間軸

自定義時間軸

  • 可配合 recyclerView 進行使用
  • 動态的指點具體的位置
  • 高度自定義,根據需求修改 View 即可
  • 可設定預設 圓點 的選中,以及修改其位置

效果圖如下:

自定義 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();
}
           
自定義 View | 時間軸