美文网首页
自定义View进度球

自定义View进度球

作者: 免费的午餐 | 来源:发表于2019-06-26 12:06 被阅读0次

学习过了Android中贝塞尔曲线的画法贝塞尔曲线,我们就利用学到的东西自定义一下view,水波纹进度球也有叫能量球,进度球等。

废话不多说,先上图:


ScreenCaptureProject2.gif
/**
 *
 * 1.利用path 画圆
 * 2.利用path 画波浪,添加动画
 * 3.利用path的op()方法,取两个path的交集
 * 4.定义动画器,动态更新波浪的起点
 */
public class BezierView3 extends View {

    private float cx,cy;//圆心
    private Paint paint;//画圆的笔
    private float radius;//半径
    private Paint mPaint;//画贝塞尔曲线的笔


    private Path path;

    private ValueAnimator animator;
    private int offsetX;

    public BezierView3(Context context) {
        super(context);
    }

    public BezierView3(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        paint.setColor(Color.RED);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);

        path = new Path();

    }


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

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //屏幕中心为圆心
        cx = getWidth()/2f;
        cy = getHeight()/2f;
        radius = (getWidth()/3f)/2f;


        animator = ValueAnimator.ofInt(0, (int) radius*2);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                offsetX=(int)animation.getAnimatedValue();
                invalidate();
            }
        });

        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setDuration(1000);
        animator.start();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path.reset();

        Path cPath = new Path();
        cPath.addCircle(cx,cy,radius,Path.Direction.CW);

        //用切割也可以,不过就是锯齿很明显,所以还是用path.op()比较好
//        canvas.clipPath(cPath);


        path.moveTo((int)(cx-3*radius)+offsetX,(int) cy);

        for (int i = 0; i < 2*radius; i+=radius) {
            path.rQuadTo(radius/2, -((float)Math.sqrt(Math.pow(radius,2)-Math.pow(radius/2,2))),radius,0);
            path.rQuadTo(radius/2, ((float)Math.sqrt(Math.pow(radius,2)-Math.pow(radius/2,2))),radius,0);
        }



        path.lineTo(getWidth() ,getHeight());//右下角
        path.lineTo(0,getHeight());//左下角
        path.close();
        mPaint.setStyle(Paint.Style.FILL);

        //取两个path路径的交互区域 这种方式比canvas剪切方法要好,这样没有锯齿 ,剪接clip方法有锯齿严重
        path.op(cPath, Path.Op.INTERSECT);
        canvas.drawCircle(cx,cy,radius,paint);
        canvas.drawPath(path,mPaint);

    }
}

能够根据上面的代码画出,效果后,我们进一步理解了贝赛尔曲线。接下来我们画一个由进度效果的能量球。
先看效果:


ScreenCaptureProject4.gif

下面代码,注释已经很清楚了

package com.wgp.customview;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;

import androidx.annotation.Nullable;


/**
 * 利用Path的rQuadTo()、addCircle()画出进度球
 *
 * 1.先画出圆形利用 Path的addCircle()方法
 * 2.再用path的画贝赛尔曲线的方法把波浪画出来,并添加动画
 * 3.两个path路径进行取交集显示,path.op(cpath, Path.Op.INTERSECT);
 * 4.动态设置波浪起点的y坐标,跟随下载进度增长
 * 5.圆球中间显示文本,先定义一个矩形把圆球框起来,然后计算出文本的基线以及放入中心显示x坐标
 */

public class BezierProgressView extends View {

    //圆心坐标
    private float cx;
    private float cy;
    //圆半径
    private float raduis=200;

    //贝塞尔曲线的paint
    private Paint paint;
    //画圆的pain
    private Paint cpaint;

    //绘制贝塞尔曲线的path
    private Path path;
    //绘制圆的path
    private Path cpath;

    private ValueAnimator animator;
    //x轴方向波浪动画的位移
    private int offsetX;

    //下载的百分比 加载的百分比
    private int percent;

    //文本
    private Paint txtPaint;
    private Rect rect;
    private Paint rectPaint;
    private int baseLineY;


    public BezierProgressView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(5);
        paint.setAntiAlias(true);
        paint.setColor(Color.BLUE);

        path = new Path();
        cpath = new Path();

        cpaint = new Paint();
        cpaint.setStyle(Paint.Style.FILL);
        cpaint.setStrokeWidth(5);
        cpaint.setAntiAlias(true);
        cpaint.setColor(Color.CYAN);

        txtPaint = new Paint();
        txtPaint.setColor(Color.RED);
        txtPaint.setAntiAlias(true);
        txtPaint.setStrokeWidth(5);
        txtPaint.setStyle(Paint.Style.STROKE);
        txtPaint.setTextSize(50);
        //该方法即为设置基线上那个点究竟是left,center,还是right  这里我设置为center
        txtPaint.setTextAlign(Paint.Align.CENTER);


        rectPaint = new Paint();
        rectPaint.setColor(Color.TRANSPARENT);

    }

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

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        cx = getWidth()/2f;
        cy = getHeight()/2f;
        cpath.addCircle(cx,cy,raduis, Path.Direction.CW);


        animator = ValueAnimator.ofInt(0, (int) raduis);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(500);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //每改变一次数值,我们就刷新一下画面
                offsetX = (int) animation.getAnimatedValue();
                invalidate();
            }
        });

        animator.start();


        //根据画出的矩形 计算 canvas.drawText()方法中需要的x,y坐标,
        rect = new Rect((int)(cx-raduis),(int)(cy-raduis),(int)(cx+raduis),(int)(cy+raduis));
        Paint.FontMetrics fontMetrics = txtPaint.getFontMetrics();
        //为基线到字体上边框的距离
        float fontTop = fontMetrics.top;
        //为基线到字体下边框的距离
        float fontBottom = fontMetrics.bottom;
        //基线中间点的y轴计算公式
        baseLineY = (int) (rect.centerY() - fontTop/2 - fontBottom/2);


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //清空一下每一次画的线条,如果不清空,那么每画一次path,下次还会显示上一次的path画的路径
        path.reset();

        //模拟下载。真实的下载,这个是百分比数值,百分比数值乘以圆的直径 load = percent*2*raduis,这个数值放到 path.moveTo(cx-2*raduis +offsetX,cy+raduis-load);
        //然后当下载完成后,取消动画
        percent++;
        if (percent==(2*raduis+(raduis/8))){
            animator.cancel();
        }


        //贝塞尔曲线的起始点, 动态改变起点位置
        path.moveTo(cx-2*raduis +offsetX,cy+raduis-percent);

        //一个波峰一个波谷是一个波浪,
        for (int i = 0; i < 3; i++) {
            //一个波峰
            path.rQuadTo(raduis/4 ,-raduis/8,raduis/2,0);
            //一个波谷
            path.rQuadTo(raduis/4,raduis/8,raduis/2,0);
        }

        //画出圆形
        canvas.drawPath(cpath,cpaint);

        //封闭path路径,
        path.lineTo(getWidth(),getHeight());
        path.lineTo(0,getHeight());
        path.close();
        path.op(cpath, Path.Op.INTERSECT);
        canvas.drawPath(path,paint);


        //先画一个矩形,来确定中心位置,让文字可以放大最中间
        canvas.drawRect(rect, rectPaint);
        canvas.drawText(percent+"",rect.centerX(),baseLineY,txtPaint);
    }


}


相关文章

网友评论

      本文标题:自定义View进度球

      本文链接:https://www.haomeiwen.com/subject/oxoqcctx.html