安卓开发 - 动态仪表盘的使用

找了两个仪表盘的文章,我觉得比较好看,分别如下所示。
在这里插入图片描述
在这里插入图片描述

1

代码

由三部分组成

  • 创建CircleProgressView.java
  • 创建values/attr.xml
  • 调用方法
CircleProgressView.java
/**
 * Created by kang on 2020/12/3.
 */

public class CircleProgressView extends View {

    /**
     * 直径
     */
    private int mDiameter;

    /**
     * 绘制时控制绘制的范围
     */
    private Paint mPaint;
    private float progressValue = 0;
    RectF rect;

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

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

    /**
     * 获得自定义的样式属性
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public CircleProgressView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, defStyle, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.CircleProgressView_diameter:
                    // 默认设置为40dp
                    mDiameter = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 40, getResources().getDisplayMetrics()));
                    break;
            }
        }
        a.recycle();

        mPaint = new Paint();
        rect = new RectF();
        progressValue = 0;
    }

    public void setProgressValue(float progressValue) {
        this.progressValue = progressValue;
        postInvalidate();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = 0;
        int height = 0;

        //设置直径的最小值
        if (mDiameter <= 40) {
            mDiameter = 40;
        }
        height = mDiameter;
        width = mDiameter;

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int mWidth = getMeasuredWidth();//得到测量宽度
        int mHeight = getMeasuredHeight();//得到测量高度
        /************************************绘制圆弧***********************************/
        float section = progressValue / 100;//progressValue是传进来的值,由定时器不停地发送过来
        mPaint.setAntiAlias(true);//消除锯齿
        mPaint.setStrokeWidth((float) 11);//画笔宽度
        mPaint.setStyle(Paint.Style.STROKE);//描边
        mPaint.setStrokeCap(Paint.Cap.ROUND);//ROUND,表示是圆角的笔触
        rect.set(20, 20, mWidth - 20, mHeight - 20);//矩形区域,用来限制绘制的圆弧范围
        mPaint.setColor(Color.BLACK);
        /*
         颜色线性渐变
         LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,Shader.TileMode tile)
         float x0: 渐变起始点x坐标
         float y0:渐变起始点y坐标
         float x1:渐变结束点x坐标
         float y1:渐变结束点y坐标
         int color0: 起始渐变色
         int color1: 结束渐变色
         Shader.TileMode tile: CLAMP边缘拉伸
         */
        LinearGradient shader = new LinearGradient(3, 3, mWidth - 3, mHeight - 3, Color.parseColor("#4299ff"), Color.parseColor("#3cedff"), Shader.TileMode.CLAMP);
        mPaint.setShader(shader);
        //画弧,第一个参数是RectF,第二个参数是角度的开始,第三个参数是旋转多少度,第四个参数是true的时候画扇形,是false的时候画弧线
        canvas.drawArc(rect, 90, section * 360, false, mPaint);

        /************************************绘制小圆球***********************************/
        mPaint.setStyle(Paint.Style.FILL);//填充
        mPaint.setColor(Color.BLUE);
        float radius = rect.height() / 2;//半径
        //获取圆弧的圆心坐标
        PointF mCenterPoint = new PointF(rect.centerX(), rect.centerY());
        PointF mRunCirclePoint = new PointF();//小圆球的圆心坐标
        float a = section * 360 + 90;//小球圆心和圆弧圆心连线与x轴的夹角(顺时针旋转)
        if (a >= 90 && a < 180) {
            mRunCirclePoint.x = mCenterPoint.x - radius * (float) Math.sin((a - 90) * (2 * Math.PI / 360));
            mRunCirclePoint.y = mCenterPoint.y + radius * (float) Math.cos((a - 90) * (2 * Math.PI / 360));
            Log.i("CircleProgressView", "第三象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
        }
        if (a >= 180 && a < 270) {
            mRunCirclePoint.x = mCenterPoint.x - radius * (float) Math.cos((a - 180) * (2 * Math.PI / 360));
            mRunCirclePoint.y = mCenterPoint.y - radius * (float) Math.sin((a - 180) * (2 * Math.PI / 360));
            Log.i("CircleProgressView", "第四象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
        }
        if (a >= 270 && a < 360) {
            mRunCirclePoint.x = mCenterPoint.x + radius * (float) Math.sin((a - 270) * (2 * Math.PI / 360));
            mRunCirclePoint.y = mCenterPoint.y - radius * (float) Math.cos((a - 270) * (2 * Math.PI / 360));
            Log.i("CircleProgressView", "第一象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
        }
        if (a >= 360 && a <= 450) {
            mRunCirclePoint.x = mCenterPoint.x + radius * (float) Math.cos((a - 360) * (2 * Math.PI / 360));
            mRunCirclePoint.y = mCenterPoint.y + radius * (float) Math.sin((a - 360) * (2 * Math.PI / 360));
            Log.i("CircleProgressView", "第二象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
        }
        //绘制圆球,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象
        canvas.drawCircle(mRunCirclePoint.x, mRunCirclePoint.y, 10, mPaint);
    }

}
values/attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="diameter" format="dimension" />
    <declare-styleable name="CircleProgressView">
        <attr name="diameter" />
    </declare-styleable>
</resources>
调用

布局xml调用如下

<com.example.k.practice.CircleProgressView
        android:id="@+id/CircleProgressView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        custom:diameter="180px" />

如果直接调用setProgressValue()方法可以显示,但是不会动。
new一个TimeTask,负责传递消息。
发送消息给Handler处理,调用setProgressValue()方法。
然后使用Time设定一个时间。这个时间有点慢,也就是这个时间内到达,这个时间有点慢可以自己改。
好像还可以在里面显示字体,但是不知道怎么显示,外面也可以显示一圈刻度。

/**
 * Created by kang on 2020/11/18.
 */

public class MainHomeFragmentClass extends Fragment {
    CircleProgressView ProgressView;
    TimerTask task4;
    float progressValue4=0;
    private Handler handler;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.main_home_fragment,container,false);
        
        ProgressView=view.findViewById(R.id.CircleProgressView);
        task4 = new TimerTask() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 4;
                handler.sendMessage(message);
            }
        };
        handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if (msg.what == 4) {
                    //空气湿度
                    ProgressView.setProgressValue(progressValue4);//设置传入的值
                    progressValue4 += 0.1;
                    if (progressValue4 > 80) {// h 即为目标值(空气湿度)
                        task4.cancel();
                    }
                }
                return false;
            }
        });
        Timer timer = new Timer();
        timer.schedule(task4, 1000, 10);

        return view;
    }

}

2

这个挺好的。

代码

由两部分组成

  • 创建CircleProgressView.java
  • 调用方法
DashboardView.java

在这个文件里几个地方,就是我注释的地方,好像可以修改字体大小。

/**
 * Created by kang on 2020/12/3.
 */

public class DashboardView extends View {

    private int padding = 20;


    private int mWidth;
    private int mHeight;

    private float progress;


    private Paint mScalePaint;
    private Paint mCiclePaint;
    private Paint mTextPaint;
    private Paint mPointPaint;
    private Paint mCenterCiclePaint;
    private Paint mProgressTextPaint;


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

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

    public DashboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mScalePaint = new Paint();
        mScalePaint.setDither(true);
        mScalePaint.setAntiAlias(true);
        mScalePaint.setColor(Color.BLACK);
        mScalePaint.setStyle(Paint.Style.STROKE);
        mScalePaint.setStrokeWidth(2);
        mCiclePaint = new Paint();
        mCiclePaint.setDither(true);
        mCiclePaint.setAntiAlias(true);
        mCiclePaint.setStyle(Paint.Style.STROKE);
        mCiclePaint.setStrokeWidth(20);
        mTextPaint = new Paint();
        mTextPaint.setStyle(Paint.Style.STROKE);
       //mTextPaint.setTextSize(DisPlayUtils.dp2px(12));
        mTextPaint.setTextSize(12);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.BLACK);
        mPointPaint = new Paint();
        mPointPaint.setAntiAlias(true);
        mPointPaint.setDither(true);
        mPointPaint.setColor(Color.RED);
        mCenterCiclePaint = new Paint();
        mCenterCiclePaint.setColor(Color.WHITE);
        mCenterCiclePaint.setAntiAlias(true);
        mCenterCiclePaint.setDither(true);

        mProgressTextPaint = new Paint();
        mProgressTextPaint.setDither(true);
        mProgressTextPaint.setAntiAlias(true);
        mProgressTextPaint.setStyle(Paint.Style.STROKE);
        //mProgressTextPaint.setTextSize(DisPlayUtils.dp2px(20));
        mProgressTextPaint.setTextSize(20);


        SweepGradient gradient = new SweepGradient(0,0,new int[]{Color.parseColor("#3bc1ff"), Color.parseColor("#3fc6fa"),
                Color.parseColor("#5fc6d2"),Color.parseColor("#90c691"),Color.parseColor("#ccc643"),
                Color.parseColor("#fdc602"),Color.parseColor("#ff9300"),Color.parseColor("#ff5100"),
                Color.parseColor("#fa2300"),Color.parseColor("#ee0d07"),Color.parseColor("#d3162b"),},null);
        Matrix gradientMatrix = new Matrix();
        gradientMatrix.preRotate(135);
        gradient.setLocalMatrix(gradientMatrix);

        mCiclePaint.setShader(gradient);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = getWidth();
        mHeight = getHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(mWidth / 2, mHeight / 2);//移动坐标原点到中心
        RectF rectF = new RectF(-mWidth/2 + padding,-mHeight/2+padding,mWidth/2-padding,mHeight/2-padding);
        canvas.drawArc(rectF,135,270,false,mCiclePaint);

        drawNum(canvas);//画仪表盘刻度

        drawerPointer(canvas,progress);//画指针

        drawCenterCircle(canvas);//画指针圆心

        drawProgressText(canvas); //画仪表盘进度值
    }

    public void setProgress(float progress){
        if (progress<0)progress=0;
        if (progress>100)progress=100;
        this.progress = progress;
        invalidate();
    }


    private void drawNum(Canvas canvas){
        canvas.save();
        canvas.rotate(-135,0,0);
        int indexY = -mHeight/2+10;
        canvas.drawLine(0,indexY+20,0,indexY+40,mScalePaint);

        float rAngle = 270/(100*1.0f);
        for (int i = 0; i <=100 ; i++) {
            canvas.save(); //记录画布状态
            canvas.rotate(rAngle * i, 0, 0);
            if (i==0 || i%5==0){
                canvas.drawLine(0,indexY+20,0,indexY+40,mScalePaint);
                if (i%10==0){
                    //canvas.drawText(i+"",-DisPlayUtils.dp2px(6),indexY+70,mTextPaint);
                    canvas.drawText(i+"",-6,indexY+70,mTextPaint);
                }
            }else {
                canvas.drawLine(0,indexY+20,0,indexY+30,mScalePaint);
            }
            canvas.restore();
        }
        canvas.restore();
    }

    private void drawerPointer(Canvas canvas,float progress){
        canvas.save();
        canvas.rotate( -135+270/(100*1.0f)*progress);
        Path pointPath = new Path();
        RectF pointF = new RectF(-10,-10,10,10);
        pointPath.moveTo(0,-mWidth/4);
        pointPath.lineTo(-10,0);
        pointPath.addArc(pointF,0,180);
        pointPath.lineTo(0,-mWidth/4);
        canvas.drawPath(pointPath,mPointPaint);
        canvas.restore();
    }

    private void drawCenterCircle(Canvas canvas){
        canvas.drawCircle(0,0,5,mCenterCiclePaint);
    }

    private void drawProgressText(Canvas canvas){
        String text = progress+"%";
        if ( progress>=0 && progress<=60){
            mProgressTextPaint.setColor(Color.parseColor("#3bc1ff"));
        }else if (progress>60&&progress<=80){
            mProgressTextPaint.setColor(Color.parseColor("#ff5100"));
        }else if (progress>80){
            mProgressTextPaint.setColor(Color.parseColor("#ee0d07"));
        }
        //canvas.drawText(progress+"%",-getTextLength(mProgressTextPaint,text)/2,mHeight/2-DisPlayUtils.dp2px(10),mProgressTextPaint);
        canvas.drawText(progress+"%",-getTextLength(mProgressTextPaint,text)/2,mHeight/2-10,mProgressTextPaint);
    }

    private float getTextLength(Paint paint,String text){
        return paint.measureText(text);
    }
}
调用

布局xml文件内容如下,宽高最好差不多。

<com.example.k.practice.DashboardView
        android:id="@+id/DashboardView"
        android:layout_width="400dp"
        android:layout_height="400dp"
        android:layout_gravity="center"/>

如果直接调用setProgress()方法可以显示,但是不会动。
new一个TimeTask,负责传递消息。
发送消息给Handler处理,调用setProgress()方法。
然后使用Time设定一个时间。这个时间有点慢,也就是这个时间内到达,这个时间有点慢可以自己改。

/**
 * Created by kang on 2020/11/18.
 */

public class MainHomeFragmentClass extends Fragment {
    DashboardView Dashboard;
    TimerTask task4;
    float progressValue4=0;
    private Handler handler;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.main_home_fragment,container,false);
        Toast.makeText(getContext(),"这是主页碎片",Toast.LENGTH_SHORT).show();
        Dashboard=view.findViewById(R.id.DashboardView);
        //Dashboard.setProgress(5);
        task4=new TimerTask() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 4;
                handler.sendMessage(message);
            }
        };
        handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if (msg.what == 4) {
                    //空气湿度
                    Dashboard.setProgress(progressValue4);//设置传入的值
                    progressValue4 += 0.1;
                    if (progressValue4 > 30) {// h 即为目标值(空气湿度)
                        task4.cancel();
                    }
                }
                return false;
            }
        });
        Timer timer = new Timer();
        timer.schedule(task4, 1000, 10);
        return view;
    }
}

参考链接

Android仪表盘实现自动增长的动画 - 某农村小伙
Android仪表盘自定义 - qq_32792163

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值