Android自定义圆形进度条

自定义View是Android进阶的必备技能,只要掌握了自定义view的三个方法,自定义view其实很简单,下面间通过一个示例来说明

完成效果

  1. 首先需要在values文件夹下建一个attrs的文件

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="titletext" format="string" />
    <attr name="titletextcolor" format="color" />
    <attr name="titletextsize" format="dimension" />
    <attr name="drawcolor" format="color" />
    <attr name="drawwidth" format="dimension" />
    <attr name="textsize" format="dimension" />
    <attr name="textcolor" format="color" />

    <!--定义样式,声明属性-->
    <declare-styleable name="CustomTitleView">
        <attr name="titletext" />
        <attr name="titletextcolor" />
        <attr name="titletextsize" />
    </declare-styleable>

    <declare-styleable name="CustomRoundProgressBar">
        <attr name="drawcolor" />
        <attr name="drawwidth" />
        <attr name="textsize" />
        <attr name="textcolor" />
    </declare-styleable>

</resources>

其中attr为定义属性,name为属性名称。

<declare-styleable name="CustomRoundProgressBar">
意为是在CustomRoundProgressBar里面声明属性,此为声明属性的固定写法

xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.cdemo.customview.MainActivity">

    <!--
    <com.cdemo.customview.CustomTitleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        app:titletext="666666"
        app:titletextcolor="#ff0000"
        app:titletextsize="50sp" />
        -->

    <com.cdemo.customview.CustomRoundProgressBar
        android:id="@+id/customprogressbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        app:drawcolor="#4ACC60"
        app:drawwidth="39dp"
        app:textcolor="#4ACC60"
        app:textsize="50sp" />
</RelativeLayout>

根布局文件里写明命名空间,可以写成xmlns:app=”http://schemas.android.com/apk/res-auto”系统自动识别,也可以写成 xmlns:xxx=”http://schemas.android.com/apk/res/com.cdemo.customview”,其中”xxx”名字你自己定义,“com.cdemo.customview”对应你的包名,这里我使用app命名空间自动识别。

下面开始Java代码编写

package com.cdemo.customview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

/**
 * Created by yangdi on 2017/5/24.
 */

public class CustomRoundProgressBar extends View{

    // 进度条宽度
    private int drawWidth;
    // 进度条颜色
    private int drawColor;
    // 中间数字颜色
    private int textColor;
    // 中间数字字体大小
    private int textSize;

    // 字的高度
    private float mTxtHeight;

    // 进度条最大值
    private int maxProgress;
    // 进度条当前值
    private int currentProgress;

    private Paint mPaint;

    String TAG = "CustomRoundProgressBar";


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

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

    public CustomRoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        // 获取在xml文件中定义的属性和值
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomRoundProgressBar, defStyleAttr, 0);
        drawWidth = typedArray.getDimensionPixelSize(R.styleable.CustomRoundProgressBar_drawwidth, TypedValue.COMPLEX_UNIT_DIP);
        drawColor = typedArray.getInt(R.styleable.CustomRoundProgressBar_drawcolor, Color.LTGRAY);
        textColor = typedArray.getColor(R.styleable.CustomRoundProgressBar_textcolor,Color.BLACK);
        textSize = typedArray.getDimensionPixelSize(R.styleable.CustomRoundProgressBar_textsize, TypedValue.COMPLEX_UNIT_SP);

        typedArray.recycle();

        mPaint = new Paint();
        // 抗锯齿
        mPaint.setAntiAlias(true);

        Paint.FontMetrics fm = mPaint.getFontMetrics();
        mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
    }

通过TypedArray获取刚刚在xml文件中定义的属性和设定的值

onMeasure方法是自定义View中需要重写的方法,其中int widthMeasureSpec, int heightMeasureSpec是父布局传进来的两个参数,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定,指定View需要多少空间。
这个值由高32位和低16位组成,高32位保存的值叫specMode,可以通过如代码中所示的MeasureSpec.getMode()获取;低16位为specSize,同样可以由MeasureSpec.getSize()获取。

MeasureSpec的specMode,一共三种类型:

  • EXACTLY:一般是设置了明确的值,如:200dp或者是MATCH_PARENT
  • AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
  • UNSPECIFIED:表示子布局想要多大就多大,很少使用
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = 0;
        int height = 0;

        if(widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        }else{
            // 这里不考虑wrap_content的情况,如有需要可自己补上
         }

        if(heightMode == MeasureSpec.EXACTLY){
            height = heightSize;
        }else{
            // 这里不考虑wrap_content的情况,如有需要可自己补上
        }

        // 设置最终宽高
        setMeasuredDimension(width, height);
    }

以上写的代码全部服务于setMeasuredDimension函数指定最终需要的空间大小。

下面是 onDraw函数,也是自定义View需要重写的方法:

@Override
    protected void onDraw(Canvas canvas) {

        // 获取视图宽度/2
        int viewWidth = getWidth()/2;
        // 获取视图高度/2
        int viewHeight = getHeight()/2;
        // 圆形进度条半径为宽度的3倍
        int radius = drawWidth*3;

        // 画最外层圆圈
        mPaint.setColor(Color.LTGRAY);
        // 设置画笔宽度
        mPaint.setStrokeWidth(drawWidth);
        // 绘制空心效果
        mPaint.setStyle(Paint.Style.STROKE);
        // 画板底色
        canvas.drawColor(Color.WHITE);
        canvas.drawCircle(viewWidth, viewHeight, radius, mPaint);


        // 画中间进度数字
        mPaint.setStrokeWidth(0);/* 需要注意的是,上面设置过一次画笔宽度,现在需要设置为0,否则画出的字挤在一起*/
        // 画笔颜色
        mPaint.setColor(textColor);
        mPaint.setTextSize(textSize);
        // 字体
        mPaint.setTypeface(Typeface.DEFAULT_BOLD);
        // 通过画布测量字体所占长度
        float textWidth = mPaint.measureText(currentProgress+"%");
        canvas.drawText(currentProgress+"%", viewWidth - textWidth/2, viewHeight + textSize/3, mPaint);


        // 画进度
        mPaint.setColor(drawColor);
        mPaint.setStrokeWidth(drawWidth);
        // 绘制进度
        RectF oval = new RectF(viewWidth - radius, viewHeight - radius, viewWidth + radius, viewHeight + radius);
        canvas.drawArc(oval, 0, getProgress(), false, mPaint);
    }

我用的方法是,先画一个圆环作为底色,再到同样的位置画一个圆环,作为实时进度,中间画一个指示数字,根据任务执行的进度来更新圆环位置

public void setMaxProgress(int maxProgress) {
        this.maxProgress = maxProgress;
    }

    public void setCurrentProgress(int currentProgress) {
        this.currentProgress = currentProgress;
        postInvalidate();
    }

    private int getProgress(){
        if(currentProgress < maxProgress){
            return 360*currentProgress/maxProgress;
        }else{
            return 360;
        }
    }

MainAcyivity代码

package com.cdemo.customview;

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

public class MainActivity extends AppCompatActivity {

    private CustomRoundProgressBar customRoundProgressBar;
    private int maxProgress = 100;
    int currentProgress = 0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        customRoundProgressBar = (CustomRoundProgressBar) findViewById(R.id.customprogressbar);
        customRoundProgressBar.setMaxProgress(maxProgress);
        mHandler.sendEmptyMessage(0x11);
    }


    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x11:
                    currentProgress++;
                    customRoundProgressBar.setCurrentProgress(currentProgress);
                    sendEmptyMessageDelayed(0x11, 200);
                    if (currentProgress >= maxProgress) {
                        currentProgress = maxProgress;
                        removeMessages(0x11);
                    }
                    break;
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mHandler != null){
        mHandler.removeCallbacksAndMessages(null);
        }
    }
}

Activity里面的代码主要就是开一个子线程模拟任务进度,然后更新圆环的进度,直到完成

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值