当前位置:首页 > 行业动态 > 正文

如何在Android中通过自定义View实现横行时间轴效果?

在Android开发中,自定义View可以实现各种复杂的UI效果,包括横行时间轴。通过继承View类并重写onDraw方法,可以绘制出具有刻度和标签的时间轴。

Android使用自定义View实现横行时间轴效果

如何在Android中通过自定义View实现横行时间轴效果?  第1张

在Android开发中,时间轴的UI需求非常常见,虽然RecyclerView可以实现部分时间轴效果,但当需要更复杂的定制效果时,自定义View成为最佳选择,本文将详细介绍如何使用自定义View来实现一个横行时间轴效果,包括显示时间、阶段名、状态图标以及中间有虚线分隔的效果。

一、为什么选择自定义View?

1、每个阶段要显示时间、阶段名、状态图标、中间有虚线

2、文字上下交错显示

3、相邻阶段的文字在垂直方向上是可以相交的

4、时间轴的个数不确定,但是要铺满屏幕并且不可滑动

如果只实现上两点的效果,使用RecyclerView无疑是最好的选择,但是要同时实现以上整个效果目前想到的最好的办法就是使用自定义View。

二、如何开始?

相信也有人跟我一样,对自定义的绘制过程view、canvas、path、paint的使用有了解,但是真的要去写自定义View确不知道从何开始,不知道第一步如何下手,我个人的归纳就是:想要的太多,迟迟不动手,所以有想法一定要去动手试验!不要想着写完第一次运行就是最终想展示的完美效果,而是要抱着整体拆分成不重复的小块,然后去绘制重复块,然后去一点点实现一步步完美的心态才能做出来。

三、绘制步骤

1. 画中间的线

首先画虚线,如果虚线不知道怎么画,可以先画一条实线,然后再去找画虚线的方法,使用canvas中画线的方法drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint)根据参数得知需知道线的起点与终点坐标以及一个paint对象,因为是垂直居中且横穿整个控件的直线所以可以确定两个点的y坐标是一样的,也就是控件高的一半,起点的x坐标为0,终点的x坐标为控件的宽,也就是知道控件的宽和高之后就可以绘制出这条线,获取控件的宽高,可以在onMeasure方法中获取:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mViewWidth = MeasureSpec.getSize(widthMeasureSpec) dip2px(mContext, mSafeDistance * 2);
    mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
}

画线的代码(在onDraw方法中添加,下面其它的绘制方法同样是在onDraw方法中添加):

// 定义画笔,并设置相关属性
Paint mLinePaint = new Paint();
mLinePaint.setColor(Color.parseColor("#999999"));
mLinePaint.setStrokeWidth(1);
mLinePaint.setStyle(Paint.Style.STROKE);
// 画虚线
canvas.drawLine(0, mViewHeight / 2, mViewWidth, mViewHeight / 2, mLinePaint);

2. 画图标

canvas画图标的方法:drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint),根据方法的参数去分析如何准备值,这里需要一个bitmap对象,起点坐标以及paint,bitmap对象可以将资源文件drawable转为bitmap格式;坐标就是控件的中心点,画图标的代码:

// 图标 x,y 坐标
Bitmap statusBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.ic_no_pass);
float bX = mViewWidth / 2;
// 垂直的中心点在图标的顶部,所以要减去bitmap高的一半
float bY = mViewHeight / 2 statusBitmap.getHeight() / 2f;
Paint mBitmapPaint = new Paint();
mBitmapPaint.setFilterBitmap(true);
canvas.drawBitmap(statusBitmap, bX, bY, mBitmapPaint);

3. 画文本

canvas画文本的方法:drawText(@NonNull String text, float x, float y, @NonNull Paint paint),依然是根据方法得知需要知道绘制的内容,开始的坐标点以及paint,当文本在图标上方时,文本的y坐标需要使用图标的y坐标减去文本到图标的距离,x坐标同图片的x坐标一样;当文本在图标下方时,文本的y坐标需要使用图标的y坐标加上文本到图标的距离,画文本的代码:

// 定义画笔
Paint mDatePaint = new Paint();
mDatePaint.setColor(Color.parseColor("#666666"));
mDatePaint.setTextSize(dip2px(mContext, 12));
mDatePaint.setStyle(Paint.Style.FILL);
mDatePaint.setTextAlign(Paint.Align.CENTER);
mDatePaint.setAntiAlias(true);
// 画时间文本
canvas.drawText("12:00", bX, bY dip2px(mContext, 5), mDatePaint);
// 画阶段名文本
canvas.drawText("阶段名称", bX, bY + dip2px(mContext, 5), mDatePaint);

4. 完整代码示例

以下是一个完整的自定义View实现横行时间轴效果的示例代码:

public class TimeLineView extends View {
    private int mViewWidth;
    private int mViewHeight;
    private Paint mLinePaint;
    private Paint mDatePaint;
    private Bitmap statusBitmap;
    private Paint mBitmapPaint;
    private float mSafeDistance = 10; // 安全距离
    public TimeLineView(Context context) {
        super(context);
        init();
    }
    public TimeLineView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public TimeLineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init() {
        mLinePaint = new Paint();
        mLinePaint.setColor(Color.parseColor("#999999"));
        mLinePaint.setStrokeWidth(1);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setPathEffect(new DashPathEffect(new float[]{dpToPx(10), dpToPx(10)}, null)); // 虚线效果
        mDatePaint = new Paint();
        mDatePaint.setColor(Color.parseColor("#666666"));
        mDatePaint.setTextSize(dpToPx(12));
        mDatePaint.setStyle(Paint.Style.FILL);
        mDatePaint.setTextAlign(Paint.Align.CENTER);
        mDatePaint.setAntiAlias(true);
        statusBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_no_pass);
        mBitmapPaint = new Paint();
        mBitmapPaint.setFilterBitmap(true);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mViewWidth = MeasureSpec.getSize(widthMeasureSpec) dpToPx(mSafeDistance * 2);
        mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawLine(canvas);
        drawIcon(canvas);
        drawText(canvas);
    }
    private void drawLine(Canvas canvas) {
        canvas.drawLine(0, mViewHeight / 2, mViewWidth, mViewHeight / 2, mLinePaint);
    }
    private void drawIcon(Canvas canvas) {
        float iconX = mViewWidth / 2;
        float iconY = mViewHeight / 2 statusBitmap.getHeight() / 2f;
        canvas.drawBitmap(statusBitmap, iconX, iconY, mBitmapPaint);
    }
    private void drawText(Canvas canvas) {
        String timeText = "12:00";
        String stageName = "阶段名称";
        float textX = mViewWidth / 2;
        float textYTime = mViewHeight / 2 dpToPx(5); // 根据需要调整位置
        float textYStage = mViewHeight / 2 + dpToPx(5); // 根据需要调整位置
        canvas.drawText(timeText, textX, textYTime, mDatePaint);
        canvas.drawText(stageName, textX, textYStage, mDatePaint);
    }
    private int dpToPx(float dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }
}

5. 使用自定义View

在布局文件中使用自定义View:

<com.example.yourpackage.TimeLineView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

在Activity或Fragment中使用:

TimeLineView timeLineView = findViewById(R.id.timeLineView);

自定义View是一个强大的工具,通过合理拆解绘制元素并逐步实现,可以完成复杂的UI效果,本文介绍了如何使用自定义View实现横行时间轴效果,包括虚线、图标和文本的绘制,希望读者能够通过本文掌握自定义View的基本使用方法,并在实际开发中灵活运用。

0