Android自定义View—仿雷达扫描效果

最近在翻以前写的代码,翻到几个月以前做的一个仿雷达扫描的效果,现在拿出来和大家分享一下,在进入分析和代码之前,我们先来看看效果吧,

…录屏质量较差,凑活着看吧。看到这样的效果,肯定是用自定义view的方式去实现的。通过观察效果,我们先来列一列要用的知识吧。

  1. 整个过程是一个不断绘制的过程,所以我们需要一个Handler。
  2. 核心的效果是一个扇形,所以我们还需要会绘制扇形。
  3. 绘制的扇形的颜色有一个梯度变化的效果,所以我们需要知道Shader的使用。
  4. 整个过程中扇形是不断变化位置的,我们还需要知道如何去旋转canvas。

ok,知道了这些,我们就一一来看一下上面的知识点。

一. 首先这个不断绘制的过程,我们说用handler去实现,其实方式很多,只不过我个人比较喜欢用handler的方式去实现,至于怎么使用,思路就是在Handler的handleMessage方法中继续send一个message。
例如下面代码:

private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            if(msg.what == MSG_RUN) {
                ...
                postInvalidate();
                sendEmptyMessage(MSG_RUN);
            }
        }
    };

二. 绘制扇形,如何利用canvas去绘制扇形,这都是最基本的知识了,这里仅仅提一下就ok啦,

canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

简单说一下参数吧,第一个参数是指的这个扇形所在圆的一个外接矩形。

第二个参数是扇形开始的位置,这里需要注意的是这个开始的位置是从坐标系的正东方向开始算起的。
如图,

Android自定义View—仿雷达扫描效果_第1张图片

第三个参数,指的是扇形的角度。

第四个参数如果设置false,绘制的是一段圆弧,true的话,绘制的才是一个实体的扇形。

三. Android提供了不少的Shader,我们这里需要一个梯度的变化,这就需要SweepGradient这个类了。
来看看这个类的构造方法,

public SweepGradient (float cx, float cy, int color0, int color1)

这里只看一个它的一个构造方法,前两个参数是指梯度的圆心位置,后两个参数指的是梯度开始的颜色和结束的颜色。

四. 旋转canvas很容易,只需要调用canvas.rotate方法就ok,但是要记住在旋转之前要保存现场,完毕之后要恢复现场。

除此之外,我们还需要绘制两个圆和两条线。

梳理完这些之后,下面我们正式开始编写代码实现这个效果了。
首先,我们先来初始化一些东西,

public class MyView extends View {

    private static final int MSG_RUN = 1;

    private Paint mCirclePaint; // 绘制圆形
    private Paint mArcPaint; // 绘制扇形
    private Paint mLinePaint; // 绘制线条

    private RectF mRectF;

    private int mSweep; // 扇形角度

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        mCirclePaint.setColor(Color.BLACK);
        mCirclePaint.setStyle(Style.STROKE);
        mCirclePaint.setStrokeWidth(1.f);

        mArcPaint.setColor(Color.GRAY);
        mArcPaint.setStyle(Style.FILL);

        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setColor(Color.BLACK);
        mLinePaint.setStrokeWidth(1.f);

        mRectF = new RectF();
    }
}

这里定义了3个Paint,分别用来绘制圆形、线条和扇形,并且在构造中初始化这些Paint。

因为我们需要的是一个正方形的区域,所以我们还需要重写onMeasure方法,并且在这里面设置Rect的值。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int size = getMeasuredWidth();
    setMeasuredDimension(size, size);
    mRectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight());

    mArcPaint.setShader(new SweepGradient(size/2, size/2, Color.GRAY, Color.BLACK));
}

这里我们设置我们的view大小是一个正方形,而且初始化了mRectF,这个我们在绘制的时候要用到。
最后,我们给绘制扇形的Paint设置了一个shader,这里正是使用了SweepGradient,关于它的构造方法的在上面已经说明了。
继续代码,就到了绘制的时候了,怎么绘制呢? 我们先来看代码,

@Override
protected void onDraw(Canvas canvas) {
    int centerX = getMeasuredWidth() / 2;
    int centerY = getMeasuredHeight() / 2;

    canvas.save();
    canvas.rotate(mSweep, centerX, centerY);
    canvas.drawArc(mRectF, 0, mSweep, true, mArcPaint);
    canvas.restore();

    canvas.drawLine(0, centerY, getMeasuredWidth(), centerY, mLinePaint);
    canvas.drawLine(centerX, 0, centerX, getMeasuredHeight(), mLinePaint);

    canvas.drawCircle(centerX, centerY, centerX / 2, mCirclePaint);
    canvas.drawCircle(centerX, centerY, centerX, mCirclePaint);
}

这里分为三个部分,后两个部分很容易,就是绘制了两条横竖的线条和两个大小不一样的圆。最重要的还是看第一部分,首先我们保存现场,因为我们在这里要去旋转画布。接下来果然是去旋转了画布,旋转的角度是mSweep,这个值在后面我们会不断的去改变它,最后使用drawArc方法去绘制出了我们需要的扇形,可以看到这里的绘制的开始位置一直是0,那我们在效果图中看到的开始位置不断变化是怎么回事?答案正式我们旋转了画布角度的结果!最后的最后,别忘了去恢复现场。

现在整个绘制的流程就走完了,也可见看到这样一个效果是如此的简单,接下来就是如何开始这个动画,并且不断的去绘制。

public void start() {
    mHandler.sendEmptyMessage(MSG_RUN);
}

private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        if(msg.what == MSG_RUN) {
            mSweep+=2;
            if(mSweep > 360) mSweep = 0;
            postInvalidate();
            sendEmptyMessage(MSG_RUN);
        }
    }
};

我们公开了一个start方法用来开启动画,在start中是调用了handler的sendEmptyMessage去发送一个效果,接下来我们来看看handler中的逻辑。在handler的handleMessage中,我们去改变mSweep的值,如果他的值大于360度,则重新置0,最后再次发送出一个效果,这样就达到了一个不断改变mSweep的值的目的,而且它的值是在0~360度之间。

ok,到现在这个简单的效果就完成了,我们只需要在xml布局中使用这个view,然后在activity中调用它的start方法,就可以显示出这个绚丽的效果了。

最后是demo的源码下载: 代码下载,戳这里

你可能感兴趣的:(android)