环形的ProgressBar

来源:互联网 发布:暴雪什么游戏支持mac 编辑:程序博客网 时间:2024/05/17 01:32

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

——环形的进度条


  上文讲了一个带指示器的进度条,实现的方式很简单,小伙伴们可以参考《学习安卓笔记之自定义控件(一)之带三角指示器的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>

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

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


参考资料

  • http://blog.csdn.net/lmj623565791/article/details/43371299
1 0
原创粉丝点击