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