环形的ProgressBar

学习安卓笔记之自定义控件(二)

------环形的进度条

  上文讲了一个带指示器的进度条,实现的方式很简单,小伙伴们可以参考《学习安卓笔记之自定义控件(一)之带三角指示器的ProgressBar》。本章将会作一个如下图效果的圆形进度条(这个图没截好,左边差了一点)。
  
  目标效果


【准备动手之前】

  那吗如何做呢?我们先来分析一下,从图中可以看出该控件是由两部分组成的,一部分是圆环的部分,另一部分就是文本区域。该如何去绘制这些图形呢?我们来看下下图:
  
  这里写图片描述

  图中红色正方形区域控件,也相当于一块画布。之后所有要绘制的图形都会在这个正方形的区域中去绘制。红色圆形部分表示控件中的进度条。而绿色区域则表示文本的区域。接下来将准备将会用到的自定义属性。


【创建自定义属性】

  为了这个控件能使用的更加灵活,这里设置了一些将会用到的属性(模仿了一下之前用到的一些类似的控件):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleProgressBar">
        <!-- 文字的颜色 -->
        <attr name="circle_ring_text_color" format="color"/>
        <!-- 数值的颜色 -->
        <attr name="circle_ring_values_color" format="color"/>
        <!-- 显示的文本 -->
        <attr name="circle_ring_text" format="string"/>
        <!-- 单位 -->
        <attr name="circle_ring_unit" format="string"/>
        <!-- 圆环的宽度 -->
        <attr name="circle_ring_width" format="dimension"/>
        <!-- 默认圆环的颜色 -->
        <attr name="circle_ring_background_color" format="color"/>
        <!-- 显示进度的圆环的颜色 -->
        <attr name="circle_ring_foreground_color" format="color"/>
    </declare-styleable>
</resources>

【开始动手】

  同样这次代码量也不是很多,所以还是把整个类都贴出来了(包都不用导了直接复制就OK了):

package lyan.circleprogressbar.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ProgressBar;

import lyan.circleprogressbar.R;

/**
 * Author LYJ
 * Created on 2016/9/12.
 * Time 13:33
 */
public class MyCircleProgressBar extends ProgressBar {
    /**
     * 默认属性
     */
    private static final int CIRCLE_RING_TEXT_COLOR = 0xff000000;//文本默认颜色为黑色
    private static final String CIRCLE_RING_TEXT = "当前里程";//默认文本
    private static final String CIRCLE_RING_UNIT = "米";//默认单位
    private static final int CIRCLE_RING_WIDTH = 30;//圆环的默认宽度
    private static final int CIRCLE_RING_BACKGROUND_COLOR = 0xfff2f2f2;//默认背景圆环颜色为灰色
    private static final int CIRCLE_RING_FOREGROUND_COLOR = 0xff00ffff;//默认前景圆环颜色为青色
    private static final int CIRCLE_RING_VALUES_COLOR = 0xffff0000;//值的颜色默认为红色
    /**
     * 声明变量
     */
    private int circle_ring_Side;//包含圆环容器的边长
    private int circle_ring_radius;//圆环的半径
    private int draw_offset;//因为画笔的宽度产生的误差
    private int text_color;//文本的颜色
    private int values_color;//值得颜色
    private String text;//内容
    private String unit;//单位
    private int ring_width;//圆环的宽度
    private int ring_back_color;//默认的颜色
    private int ring_fore_color;//展示进度的颜色
    private Paint textPaint, backPaint, forePaint, testPaint;//画笔
    private RectF drawArcRect;//绘制弧形的区域

    /**
     * @param context
     */
    public MyCircleProgressBar(Context context) {
        this(context, null);
    }

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

    public MyCircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        obtainAttributes(attrs);//获取并设置自定义属性
        init();//初始化
        initPaints();//初始化画笔
    }


    /**
     * 获取自定义属性
     *
     * @param attrs
     */
    private void obtainAttributes(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(
                attrs, R.styleable.CircleProgressBar);
        text = typedArray.getString(R.styleable.CircleProgressBar_circle_ring_text);
        text = text == null ? CIRCLE_RING_TEXT : text;
        unit = typedArray.getString(R.styleable.CircleProgressBar_circle_ring_unit);
        unit = unit == null ? CIRCLE_RING_UNIT : unit;
        values_color = typedArray.getColor(
                R.styleable.CircleProgressBar_circle_ring_values_color,CIRCLE_RING_VALUES_COLOR);
        text_color = typedArray.getColor(
                R.styleable.CircleProgressBar_circle_ring_text_color, CIRCLE_RING_TEXT_COLOR);
        ring_width = dip2px((int) typedArray.getDimension(
                R.styleable.CircleProgressBar_circle_ring_width, CIRCLE_RING_WIDTH));
        ring_back_color = typedArray.getColor(
                R.styleable.CircleProgressBar_circle_ring_background_color, CIRCLE_RING_BACKGROUND_COLOR);
        ring_fore_color = typedArray.getColor(
                R.styleable.CircleProgressBar_circle_ring_foreground_color, CIRCLE_RING_FOREGROUND_COLOR);
        typedArray.recycle();
    }

    /**
     * 初始化
     */
    private void init() {
        draw_offset = ring_width / 2 + dip2px(2);//偏移量
        drawArcRect = new RectF();//绘制弧形的区域
    }

    /**
     * 初始化画笔
     */
    private void initPaints() {
//        //测试画笔
//        testPaint = new Paint();
//        testPaint.setStrokeWidth(2);
//        testPaint.setColor(Color.BLACK);
//        testPaint.setStyle(Paint.Style.STROKE);
//        testPaint.setAntiAlias(true);
        //绘制默认圆环的画笔
        backPaint = new Paint();//用于绘制默认的圆环
        backPaint.setColor(ring_back_color);//画笔颜色
        backPaint.setAntiAlias(true);//抗锯齿
        backPaint.setDither(true);//防抖动
        backPaint.setStrokeWidth(ring_width);//设置笔刷宽度
        backPaint.setStyle(Paint.Style.STROKE);//不填充
        //绘制进度的画笔
        forePaint = new Paint();//用于绘制进度的画笔
        forePaint.setColor(ring_fore_color);
        forePaint.setStrokeWidth(ring_width);
        forePaint.setAntiAlias(true);
        forePaint.setDither(true);
        forePaint.setStyle(Paint.Style.STROKE);
        forePaint.setStrokeCap(Paint.Cap.ROUND);//笔刷边界为圆形
        //绘制文本的画笔
        textPaint = new Paint();//绘制文字
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);
        textPaint.setStyle(Paint.Style.FILL);//填充
        textPaint.setTypeface(Typeface.DEFAULT_BOLD);//文字粗体
        textPaint.setTextAlign(Paint.Align.CENTER);//文字居中
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//        canvas.drawColor(0xffffffff);
        circle_ring_radius = circle_ring_Side / 2 - draw_offset;//半径
        //绘制默认的圆环
        canvas.drawCircle(circle_ring_Side / 2, circle_ring_Side / 2, circle_ring_radius, backPaint);
        //绘制弧形
        drawArcRect.left = draw_offset;
        drawArcRect.top = draw_offset;
        drawArcRect.right = circle_ring_Side - draw_offset;
        drawArcRect.bottom = circle_ring_Side - draw_offset;
        canvas.drawArc(drawArcRect, 90, getProgress() * 360 / getMax(), false, forePaint);
        //绘制文本的区域
        int chord_length = (int) Math.sqrt(Math.pow(circle_ring_Side, 2) 
                + Math.pow(circle_ring_Side, 2));//获取矩形的对角线长度
        int draw_text_left = (int) ((chord_length / 2 - circle_ring_radius) * Math.sin(45));
        int draw_text_top = draw_text_left;
        int draw_text_right = circle_ring_Side - draw_text_left;
        int draw_text_bottom = circle_ring_Side - draw_text_top;
//        canvas.drawRect(draw_text_left, draw_text_top, draw_text_right, draw_text_bottom, testPaint);
        //绘制文本
        int text_4_side = Math.abs(draw_text_bottom - draw_text_top) / 4;//绘制区域的1/4份
        //固定提示文本
        textPaint.setColor(text_color);
        textPaint.setTextSize((float) (text_4_side * 0.6));
        canvas.drawText(text, circle_ring_Side / 2, draw_text_top + text_4_side, textPaint);
        //固定单位文本
        canvas.drawText(unit, circle_ring_Side / 2, draw_text_bottom - text_4_side/2, textPaint);
        //变换的数值
        textPaint.setColor(values_color);
        textPaint.setTextSize((float) (text_4_side)*1.5f);
        canvas.drawText(String.valueOf(getProgress()),circle_ring_Side/2,
                circle_ring_Side/2 + text_4_side/2,textPaint);
    }

    /**
     * 测量
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);//测量宽度
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);//测量高度
        circle_ring_Side = Math.min(widthSize, heightSize);//取最小值
        setMeasuredDimension(circle_ring_Side, circle_ring_Side);//设置包含圆环的容器为正方形
    }

    /**
     * dp-->px
     *
     * @param decisionType
     * @return
     */
    private int dip2px(int decisionType) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                decisionType, getResources().getDisplayMetrics());
    }

}

------让我们看一下运行效果
  光看着确实有点像那么回事的意思。

初步的效果


【测试一下功能】

  刚刚仅仅是看了一下效果图,并没有测试它的功能是否好使,接下来完整的测试一下这个自定义控件的功能是否符合要求吧:

  【向布局文件中添加自意义控件】,那个名为lyan的命名空间先不用管!

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:lyan="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.MainActivity">

    <lyan.circleprogressbar.view.MyCircleProgressBar
        android:id="@+id/test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        />
</RelativeLayout>

  【向Activity中添加测试代码】随便创建一个就OK。这里一样整类贴出。

package lyan.circleprogressbar.activity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import lyan.circleprogressbar.R;
import lyan.circleprogressbar.view.MyCircleProgressBar;

public class MainActivity extends AppCompatActivity {

    private MyCircleProgressBar circleProgressBar;
    private int count = 0;
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message message) {
            if (message.what == 1){
                ++count;
                circleProgressBar.setProgress(count);
                Log.e("handleMessage: ", count + "");
            }
            return false;
        }
    });
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        circleProgressBar = (MyCircleProgressBar) findViewById(R.id.test);
        circleProgressBar.setMax(100);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100){
                    try {
                        handler.sendEmptyMessage(1);
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

------我们再看一下运行效果
  看着还行,就差使用自定义的属性了。
效果二


【收工】

  更改一下布局文件,将自定义的属性添加上:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:lyan="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.MainActivity">

    <lyan.circleprogressbar.view.MyCircleProgressBar
        android:id="@+id/test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        lyan:circle_ring_background_color="#545454"
        lyan:circle_ring_foreground_color="#4592F3"
        lyan:circle_ring_text_color="#4592F3"
        lyan:circle_ring_values_color="#FBB900"
        lyan:circle_ring_width = "30dp"
        lyan:circle_ring_text = "消耗热量"
        lyan:circle_ring_unit = "卡路里"
        />
</RelativeLayout>

【最后运行一次】
  为了与一开始区分开,在布局里更改了几个字:
这里写图片描述

  至此我们的功能都实现完了*


参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值