自定义view系列(2)--秒针旋转效果

本文介绍如何根据项目需求创建一个自定义View,展示从近到远查找可接单车辆的雷达扫描过程。内容包括自定义View的属性如颜色、半径级别和文本信息,以及适配和绘制文字的技巧。作者强调持之以恒学习自定义View的重要性,认为这是提升技能和竞争力的关键。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近项目需求变更,用户下单完成之后,需要以距离由近到远的方式逐步查找可接单的车辆,而对用户端的要求就是要让用户知道系统在为他派单,并且还得让用户知道此时此刻系统正在为他查询多少公里内的车辆.所以这个自定义view就诞生了.

其实网上已经有前人造好的轮子,但是有一点不满足需求就是需要让用户知道此时此刻正在什么范围内派单,所以还是得自己画一个满足需求的.

一,国际惯例,上效果图:


二,该自定义view具备的属性:

1,可以自定义外圈的颜色,内圆的颜色,扫描线的颜色

2,可以自定义圆圈半径级别(也可以理解为半径大小),有且只有1-10这10个数字选择,其他数字直接抛异常,原因下面再说.

3,可以自定义显示的文本信息

都是最基本的属性,满足项目需求即可,当然还可以抽取出扫描线的粗细,扫描速率,扫描方向,文本字体大小,圆圈的粗细等等.

三,直接上代码:

package com.lanma.customviewproject.views;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;

import com.lanma.customviewproject.R;

/**
 * 作者 qiang_xi on 2016/8/20 12:55.
 * 类似雷达扫描效果
 */
public class RadarSearchView extends View {
    private final int mSmallestRadius = getScreenWidth() / 10;
    private Paint mCirclePaint;//圆圈画笔
    private Paint mInsideCirclePaint;//内圆画笔
    private Paint mLinePaint;//线画笔
    private Paint mTextPaint;//文本画笔
    private int mCircleColor = Color.RED;
    private int mLineColor = Color.RED;
    private int mTextColor = Color.RED;
    private String mInsideText = "2km";//线中间的文本
    private int mRadius = mSmallestRadius;//圆半径==线的长度
    private float mRotateDegrees = 0;
    private boolean isShowing = false;

    public RadarSearchView(Context context) {
        this(context, null);
    }

    public RadarSearchView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RadarSearchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RadarSearchView);

        for (int i = 0; i < a.getIndexCount(); i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.RadarSearchView_radiusLevel:
                    setRadiusLevel(a.getInt(attr, 1));
                    break;
                case R.styleable.RadarSearchView_insideText:
                    mInsideText = a.getString(attr);
                    break;
                case R.styleable.RadarSearchView_insideTextColor:
                    mTextColor = a.getColor(attr, Color.RED);
                    break;
                case R.styleable.RadarSearchView_insideLineColor:
                    mLineColor = a.getColor(attr, Color.RED);
                    break;
                case R.styleable.RadarSearchView_circleColor:
                    mCircleColor = a.getColor(attr, Color.RED);
                    break;
            }
        }
        a.recycle();
    }

    private void init() {
        mCirclePaint = new Paint();
        mCirclePaint.setColor(mCircleColor);
        mCirclePaint.setStyle(Paint.Style.STROKE);
        mCirclePaint.setStrokeWidth(dpToPx(2));
        mCirclePaint.setAntiAlias(true);

        mInsideCirclePaint = new Paint();
        mInsideCirclePaint.setColor(mCircleColor);
        mInsideCirclePaint.setStyle(Paint.Style.FILL);
        mInsideCirclePaint.setAntiAlias(true);

        mLinePaint = new Paint();
        mLinePaint.setColor(mLineColor);
        mLinePaint.setStrokeWidth(dpToPx(1));
        mLinePaint.setAntiAlias(true);
        mLinePaint.setDither(true);

        mTextPaint = new Paint();
        mTextPaint.setColor(mTextColor);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(spToPx(14));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isShowing()) {
            //绘制外圆圈
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, mRadius, mCirclePaint);
            //绘制内圆点
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, dpToPx(3), mInsideCirclePaint);
            //绘制文本
            canvas.drawText(mInsideText, getWidth() / 2 - getTextWidth(mInsideText) / 2, getHeight() / 2 + mRadius / 2 + getTextHeight(mInsideText) / 2, mTextPaint);
            //绘制直线
            canvas.save();
            canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2 + mRadius * (float) Math.cos(mRotateDegrees * Math.PI / 180), getHeight() / 2 - mRadius * (float) Math.sin(mRotateDegrees * Math.PI / 180), mLinePaint);
            canvas.rotate(mRotateDegrees);
            canvas.restore();
            mRotateDegrees += 1;
            invalidate();
        }
    }

    private int dpToPx(int dp) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    private int spToPx(float spValue) {
        final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    private int getTextWidth(String text) {
        Rect textBound = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBound);
        return textBound.width();
    }

    private int getTextHeight(String text) {
        Rect textBound = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBound);
        return textBound.height();
    }

    private int getScreenWidth() {
        WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(metrics);
        return metrics.widthPixels;
    }

    /**
     * 只能设置1-10,10个级别(1的半径最小,10的半径最大)
     * 设置其他数值直接抛异常
     */
    public void setRadiusLevel(int radiusLevel) {
        if (radiusLevel < 1 || radiusLevel > 10) {
            throw new IllegalArgumentException("非法参数,参数只能在1-10之间选择");
        }
        switch (radiusLevel) {
            case 1:
                this.mRadius = mSmallestRadius;
                break;
            case 2:
                this.mRadius = mSmallestRadius + dpToPx(10);
                break;
            case 3:
                this.mRadius = mSmallestRadius + dpToPx(20);
                break;
            case 4:
                this.mRadius = mSmallestRadius + dpToPx(30);
                break;
            case 5:
                this.mRadius = mSmallestRadius + dpToPx(40);
                break;
            case 6:
                this.mRadius = mSmallestRadius + dpToPx(50);
                break;
            case 7:
                this.mRadius = mSmallestRadius + dpToPx(60);
                break;
            case 8:
                this.mRadius = mSmallestRadius + dpToPx(70);
                break;
            case 9:
                this.mRadius = mSmallestRadius + dpToPx(80);
                break;
            case 10:
                this.mRadius = mSmallestRadius + dpToPx(90);
                break;
        }
        invalidate();
    }

    public void setInsideText(String insideText) {
        this.mInsideText = insideText;
        invalidate();
    }

    public void setInsideTextColor(int insideTextColor) {
        this.mTextColor = insideTextColor;
        invalidate();
    }

    public void setLineColor(int lineColor) {
        this.mLineColor = lineColor;
        invalidate();
    }

    public void setCircleColor(int circleColor) {
        this.mCircleColor = circleColor;
        invalidate();
    }

    public boolean isShowing() {
        return isShowing;
    }

    public void dismiss() {
        this.isShowing = false;
        mRotateDegrees = 0;
        invalidate();
    }

    public void show() {
        this.isShowing = true;
        invalidate();
    }
}

四,释疑:

1,为什么不直接设置半径的大小,而自己规定一个半径级别呢?

答:主要是为了适配,因为不合适的半径大小会造成圆圈超出屏幕或圆圈包不住文字,这会很难看,而给出规定好的半径级别,可以极大程度减少以上情况的发生.

2,绘制文字需要注意的地方:

答:绘制文字时,一定要考虑到文字本身的大小,这个大家应该都知道,不过还是说下吧,可以用如下通用方法获取文字的宽高:

private int getTextWidth(String text) {
        Rect textBound = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBound);
        return textBound.width();
    }

    private int getTextHeight(String text) {
        Rect textBound = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBound);
        return textBound.height();
    }
比如在圆的半径的中点绘制一段文字,那就需要mRadius/2减去或加上getTextWidth("文本")/2,才能真正的把文字绘制到半径的中点.

五,总结:

真正学自定义view才不到几周的时间,所以发的都是很简单的效果,我相信只要持之以恒将来一定可以画出更炫的效果.虽然很久以前就研究过自定义view并且也尝试过自定义view,但是效果一直不理想,这就渐渐丧失了信心,但是这1个多月来一直逛"掘金",看到各种各样的自定义view,不管是菜鸟还是老鸟,他们都愿意分享,都对自定义view有所了解,我顿时感觉不学不行了,不然就要被淘汰了,更何况以后跳槽的话,会自定义view也是拿高薪的一大助力嘛.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值