《Android 群英传》读书笔记:自定义 View -- 比例图

版权声明:本文为 like_program 原创文章,未经博主允许不得转载。 https://blog.csdn.net/like_program/article/details/53241308

转载请注明出处: http://blog.csdn.net/like_program/article/details/53241308

先看一下我们最终要实现的自定义 View。

最终效果图:

最终效果图

分析

首先,我们来分析下这个自定义 View 的组成:中间是一个圆形,圆形中显示的有文字,外圈是弧线。这样的话,就很简单了,我们只要分别绘制出圆,文字,弧线就行了。

新建项目

打开 Android Studio,新建 CircleProgressTest 项目。

新建 CircleProgressView.java,继承自 View,并实现 CircleProgressView.java 的三个构造方法,代码如下:

package com.example.circleprogresstest;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

/**
 * 自定义 View 比例图
 */
public class CircleProgressView extends View {

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

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

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

1. 绘制圆

我们先把圆绘制出来。圆的半径是屏幕宽度的 1/4,原点坐标的 x,y 值大小都是屏幕宽度的 1/2。

修改 CircleProgressView.java,代码如下:

package com.example.circleprogresstest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;

/**
 * 自定义 View 比例图
 */
public class CircleProgressView extends View {

    private Context mContext;

    /**
     * 圆的画笔
     */
    private Paint mCirclePaint;

    /**
     * 圆心的 x,y 坐标
     */
    private float mCircleXY;

    /**
     * 圆的半径
     */
    private float mRadius;

    /**
     * 屏幕宽度
     */
    private int mScreenWidth;

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

    public CircleProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
    }

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

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

        mScreenWidth = getScreenWidth((Activity) mContext);

        // 圆心的 x,y 坐标
        mCircleXY = mScreenWidth / 2f;
        // 圆的半径
        mRadius = mScreenWidth / 4f;

        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        // 圆的画笔
        mCirclePaint = new Paint();
        // 颜色
        mCirclePaint.setColor(getResources().getColor(
                android.R.color.holo_blue_bright));
    }

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

        // 绘制圆
        canvas.drawCircle(
                mCircleXY,
                mCircleXY,
                mRadius,
                mCirclePaint);
    }

    /**
     * 获取屏幕宽度
     *
     * @param activity
     * @return
     */
    private int getScreenWidth(Activity activity) {
        DisplayMetrics metrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
        return metrics.widthPixels;
    }
}

由于圆的半径,圆心坐标都和屏幕宽度有关,所以我们定义了 getScreenWidth() 方法来获取屏幕宽度。

获取了屏幕宽度之后,我们就可以给圆的半径,圆心坐标赋值了。

扫描二维码关注公众号,回复: 3800158 查看本文章

接着我们初始化了画笔,给画笔设置了颜色,设置的天蓝色,这样,等会绘制出来的圆就是天蓝色。

圆的属性,画笔,都已经准备就绪了,接下来,我们就在 onDraw() 方法中画圆。

画圆使用的是 canvas.drawCircle() 方法,这里解释下 drawCircle() 的几个参数:

/**
 * 绘制圆
 * 
 * @param cx 圆心的 x 坐标
 * @param cy 圆心的 y 坐标
 * @param radius 圆的半径
 * @param paint  画笔
 */
public void drawCircle (float cx, float cy, float radius, Paint paint)

这几个参数很好理解,就不多说了。

好了,圆已经画好了,我们把自定义的 View 放在布局文件中看看效果。在 CircleProgressView 上点击右键,选择 Copy Reference

复制全限定类名

复制 CircleProgressView 的全限定类名,拷贝到布局文件中,activity_main.xml 代码如下:

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

    <com.example.circleprogresstest.CircleProgressView
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

预览异常

我们预览一下自定义的 View。嗯,一片空白?还报异常?

异常

报的异常是:

java.lang.ClassCastException: com.android.layoutlib.bridge.android.BridgeContext cannot be cast to android.app.Activity

大意是 BridgeContext 这个 Context 无法转换为 Activity。

回忆一下刚才我们在什么地方使用了 Activity,嗯,是获取屏幕宽度的时候:

/**
 * 获取屏幕宽度
 *
 * @param activity
 * @return
 */
private int getScreenWidth(Activity activity) {
    DisplayMetrics metrics = new DisplayMetrics();
    activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    return metrics.widthPixels;
}

代码好像没什么问题。

异常说,Context 无法转换为 Activity,再看下我们在什么地方转换的 Activity,嗯,是调用 getScreenWidth() 方法获取屏幕宽度的时候:

mScreenWidth = getScreenWidth((Activity) mContext);

问题应该就是出在这了。在这个地方,我们把 mContext 转换为 Activity。

既然 Android Studio 说这个地方有问题,那我们就仔细分析一下,我们是在 MainActivity 中实例化了布局文件,自定义 View 又写在了布局文件中,那么实例化自定义 View 时,传进来的 Context 应该就是 MainActivity。

MainActivity 继承自 AppCompatActivity,AppCompatActivity 最终还是继承自 Activity,那么我们把 MainActivity 转换成 Activity 。。。

Fuck!有问题吗?

反正我没发现。

不管了,先跑一下,万一能跑起来呢。
能跑起来

运行一下程序:

圆

看来我的代码写的没错。Fuck the Android Studio!

这里写图片描述

那刚才到底是什么问题呢,我们 StackOverFlow 一波~~

How to fix “java.lang.ClassCastException: com.android.layoutlib.bridge.android.BridgeContext cannot be cast to android.app.Activity” exception

这个回答,貌似也不能解决我们的问题,但是有一段对话引起了我们的注意:

stackoverflow

大意是:如果代码中有什么地方用到了 activity,就把那段代码写在 if (!View.isInEditMode()) {} 语句中。

我们看下 View.isInEditMode() 这个方法的 api:

Indicates whether this View is currently in edit mode. A View is usually in edit mode when displayed within a developer tool. For instance, if this View is being drawn by a visual user interface builder, this method should return true. Subclasses should check the return value of this method to provide different behaviors if their normal behavior might interfere with the host environment. For instance: the class spawns a thread in its constructor, the drawing code relies on device-specific features, etc. This method is usually checked in the drawing code of custom widgets.
Returns
True if this View is in edit mode, false otherwise.

大意是:如果当前处于编辑器环境中,比如 Android Studio,那么 isInEditMode() 返回值就是true。

那么,为什么要使用这个方法呢?根据我在网上查的资料,有一种说法是:

如果在自定义控件的构造函数或者其他绘制相关地方使用系统依赖的代码,会导致可视化编辑器无法报错并提示。

嗯,那我们就修改下 CircleProgressView.java 的 onMeasure() 方法,代码如下:

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

    // 获取屏幕宽度
    if (!isInEditMode()) {
        mScreenWidth = getScreenWidth((Activity) mContext);
    } else {
        // 这里填写你自己手机或模拟器的屏幕宽度
        mScreenWidth = 768;
    }
    // mScreenWidth = getScreenWidth((Activity) mContext);

    // 圆心的 x,y 坐标
    mCircleXY = mScreenWidth / 2f;
    // 圆的半径
    mRadius = mScreenWidth / 4f;

    initPaint();
}

由于在 Android Studio 中预览布局文件,不会执行 if (!isInEditMode()) 中的逻辑,那我们就要手动给 mScreenWidth(屏幕宽度)赋值了。这里我的模拟器的分辨率是 768 X 1280。所以我就直接给 mScreenWidth 赋值 768。

然后,我们 Rebuild 一下项目。

Rebuild

再预览一下布局文件,可以看到,圆已经成功的显示出来了。

预览图

绘制文字

圆已经成功的绘制出来,那么接下来我们就来绘制文字,在绘制文字之前,先要给大家普及一个概念:基线

什么是基线?

引用下 自定义控件之绘图篇( 五):drawText()详解 的内容:

小时候,我们在刚开始学习写字母时,用的本子是四线格的,我们必须把字母按照规则写在四线格内。
比如:

那么问题来了,在canvas在利用drawText绘制文字时,也是有规则的,这个规则就是基线!
我们先来看一下什么是基线:

基线

基线就是四线格中的第三条线!

canvas.drawText() 方法与基线

绘制文字,需要调用 canvas.drawText() 这个方法,那我们来看下 canvas.drawText() 这个方法:

/**
 * 绘制文字
 * 
 * @param text 要绘制的文字内容
 * @param x 要绘制文字的原点的 x 坐标
 * @param y 要绘制文字的原点的 y 坐标
 * @param paint  画笔
 */
public void drawText(String text, float x, float y, Paint paint)

上面这个构造函数是最常用的 drawText 方法,传进去一个 String 对象就能画出对应的文字。但这里有两个参数需要非常注意,文字原点的 x 坐标和 y 坐标 。很多同学可能会认为,这里传进去的原点参数 (x,y) 是绘制文字所在矩形的左上角的点。但实际上并不是!比如,我们上面如果要画 “like_program’s blog” 这几个字,这个原点坐标应当是下图中绿色小点的位置:

原点坐标

一般而言,(x,y) 所代表的位置是所画图形对应的矩形的左上角点。但在 drawText() 中是非常例外的,y 所代表的是基线的位置!

嗯,相信文字原点的概念,大家已经清楚了。

清楚了原点的概念,后面也就好办了。修改 CircleProgressView.java,代码如下:

package com.example.circleprogresstest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;

/**
 * 自定义 View 比例图
 */
public class CircleProgressView extends View {

    ......

    /**
     * 文字的画笔
     */
    private Paint mTextPaint;

    /**
     * 绘制的文字
     */
    private String mShowText = "Android Skill";

    /**
     * 文字的大小
     */
    private int mShowTextSize = 50;

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

    public CircleProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
    }

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

    ......

    /**
     * 初始化画笔
     */
    private void initPaint() {
        ......

        // 文字的画笔
        mTextPaint = new Paint();
        // 颜色
        mTextPaint.setColor(Color.BLACK);
        // 文字大小
        mTextPaint.setTextSize(mShowTextSize);
        // 文本对齐
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }

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

        // 绘制圆
        canvas.drawCircle(
                mCircleXY,
                mCircleXY,
                mRadius,
                mCirclePaint);

        // 绘制文字
        canvas.drawText(
                mShowText,
                0,
                mShowText.length(),
                mCircleXY, mCircleXY + mShowTextSize / 4,
                mTextPaint);
    }

    ......
}

我们初始化了文字的画笔,并设置了一些属性,比如画笔颜色,文字大小,文字内容,文本对齐方式。然后调用了 canvas.drawText() 来绘制文字。这里,我们来看下这里我们调用的 canvas.drawText() 方法的各个参数是什么意思:

/**
 * 绘制文字
 * 
 * @param text 要绘制的文字内容
 * @param start 要绘制的文字中第一个字符的索引
 * @param end 要绘制的文字中最后一个字符的索引是 (end - 1)
 * @param x 要绘制文字的原点的 x 坐标
 * @param y 要绘制文字的原点的 y 坐标
 * @param paint 画笔
 */
public void drawText (String text, int start, int end, float x, float y, Paint paint)

参数意思很好懂,就不多说了。

好了,运行一下程序:

文字

嗯,文字也已经成功显示出来了。

关于 setTextAlign() 方法

不知道大家注意到没有,刚才设置文本对齐的时候,我们调用了 setTextAlign() 方法,并传入了 Paint.Align.CENTER ,让文字的显示效果是水平居中。

那么,我想问一下,如果我传入 Align.LEFT ,文字会怎样放置?有的同学可能会想,这还不简单,当然是和屏幕左边缘对齐了。

真的是这样吗?

我书读的少

我们修改下代码:

// mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTextAlign(Paint.Align.LEFT);

运行一下程序:

跑到右边

咦?什么情况?怎么跑到右边去了,也没有和屏幕右边缘对齐。

我们查看下 setTextAlign 的 api:

Set the paint’s text alignment. This controls how the text is positioned relative to its origin. LEFT align means that all of the text will be drawn to the right of its origin (i.e. the origin specifieds the LEFT edge of the text) and so on.

大意是:设置绘制文字的对齐方式,控制绘制文字如何相对于文字原点的位置定位,如果是 LEFT,表示文字的左边缘和原点对齐。

原来,这里 Align.LEFT 的意思并不是和屏幕左边缘对齐,而是文字的左边缘和原点对齐,也就是原点在文字的左边(原点即上面图片中的绿色小点),我们设置的原点的 x 坐标是在屏幕中间,原点又在文字的左边,那文字自然就跑到屏幕右边了。

那这样的话,如果设置成 Align.RIGHT,那么原点就会在文字的右边。文字就会跑到屏幕左边。为了验证我们的猜想,修改下代码:

// mTextPaint.setTextAlign(Paint.Align.CENTER);
// mTextPaint.setTextAlign(Paint.Align.LEFT);
mTextPaint.setTextAlign(Paint.Align.RIGHT);

运行一下程序:

跑到左边

果然,文字跑到屏幕左边了。

绘制弧形

好了,圆和文字我们都绘制出来了,只剩弧形了。

Android 给我们提供了 canvas.drawArc() 来绘制弧形。我们来看下 canvas.drawArc() 方法的各个参数意思:

/**
 * 绘制圆弧
 *
 * @param oval 椭圆的边界,用来定义弧形的形状和大小
 * @param startAngle 圆弧的起始角度
 * @param sweepAngle 圆弧扫过的角度
 * @param useCenter 这个参数如果为 true,就把椭圆的中心包括在圆弧中
 * @param paint 画笔
 */
public void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

这个方法参数有点不好理解,所以详细解释下:

第一个参数 oval

看上去我们要传入一个椭圆参数,但是看下 api:

RectF

我们发现,这个椭圆参数居然是矩形类型。换句话说,我们传进去一个矩形,然后系统会使用矩形的内切椭圆来定义弧形的形状和大小。

内切椭圆。。。

吐血

估计很多人的数学知识都早已还给数学老师了,没事,这里我准备了两张图,来帮助大家理解:

矩形1 矩形2

内切椭圆就是矩形里面的那个椭圆。我们可以看出,只要一个矩形的形状和大小确定下来了,那么它的内切椭圆也就确定下来了。

所以呢,我们创建一个矩形传入 canvas.drawArc() 方法,系统就会使用这个矩形的内切椭圆的边界来确定弧形的形状和大小。

第二个参数 startAngle 和第三个参数 sweepAngle

圆弧的起始角度 startAngle 和圆弧扫过的角度 sweepAngle

这尼玛又用到数学知识了:平面直角坐标系。这里我也准备了一张图,来解释坐标系的角度:

ps

第四个参数 useCenter

这个参数如果为 true,就把椭圆的中心包括在圆弧中。

听不懂是吧?听不懂就对了,我特么自己都听不懂,Fuck!所以我们直接看图:

useCenter 为 false useCenter 为 true

最后一个参数 paint

不解释。

好了,接下来我们就来绘制圆弧。修改 CircleProgressView.java,代码如下:

package com.example.circleprogresstest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;

/**
 * 自定义 View 比例图
 */
public class CircleProgressView extends View {

    ......

    /**
     * 圆弧的画笔
     */
    private Paint mArcPaint;

    /**
     * 圆弧扫过的角度
     */
    private float sweepAngle;

    /**
     * 矩形,用来辅助绘制圆弧
     */
    private RectF mRectF;

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

    public CircleProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
    }

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

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

        ......

        // 圆弧扫过的角度,360 度的 66%
        sweepAngle = 66 / 100f * 360f;

        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        ......

        // 弧形的画笔
        mArcPaint = new Paint();
        // 颜色
        mArcPaint.setColor(getResources().getColor(
                android.R.color.holo_blue_bright));
        // 描边
        mArcPaint.setStyle(Paint.Style.STROKE);
        // 描边宽度
        mArcPaint.setStrokeWidth(mScreenWidth * 0.1f);

        // 创建一个矩形,用来辅助绘制圆弧
        mRectF = new RectF(
                mScreenWidth * 0.1f, 
                mScreenWidth * 0.1f,
                mScreenWidth * 0.9f, 
                mScreenWidth * 0.9f);
    }

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

        // 绘制圆
        canvas.drawCircle(
                mCircleXY,
                mCircleXY,
                mRadius,
                mCirclePaint);

        // 绘制文字
        canvas.drawText(
                mShowText,
                0,
                mShowText.length(),
                mCircleXY, mCircleXY + mShowTextSize / 4,
                mTextPaint);

        // 绘制圆弧
        canvas.drawArc(
                mRectF,
                270,
                sweepAngle,
                false,
                mArcPaint);
    }

    ......
}

代码没什么好说的,嗯,再解释下关于创建矩形。矩形 RectF 的构造方法有四个参数,含义如下:

 /**
 * 创建矩形
 * 
 * @param left 矩形左边缘的 x 坐标
 * @param top 矩形上边缘的 y 坐标
 * @param right 矩形右边缘的 x 坐标
 * @param bottom 矩形下边缘的 y 坐标
 */
public RectF (float left, float top, float right, float bottom)

也很好懂,不多说。

运行一下程序:

最终效果图

好了,我们已经成功的把比例图绘制出来了。

绘制顺序

最后需要注意一下的是:在 onDraw() 方法中,canvas 绘制的图案是依次覆盖的。也就是说,如果你先画文字,再画圆,那么圆就会把文字覆盖掉,导致你在屏幕上看不到绘制的文字,我们来试验一下。

修改下 CircleProgressView.java 中的 onDraw() 方法,代码如下:

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

    // 先绘制文字
    canvas.drawText(
                mShowText,
                0,
                mShowText.length(),
                mCircleXY, mCircleXY + mShowTextSize / 4,
                mTextPaint);

    // 再绘制圆
    canvas.drawCircle(
                mCircleXY,
                mCircleXY,
                mRadius,
                mCirclePaint);

    // 最后绘制圆弧
    canvas.drawArc(
                mRectF,
                270,
                sweepAngle,
                false,
                mArcPaint);
}

运行一下程序:

覆盖文字

可以看到,屏幕上已经看不见文字了。

最后,贴上 CircleProgressView.java 的全部源码:

package com.example.circleprogresstest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;

/**
 * 自定义 View 比例图
 */
public class CircleProgressView extends View {

    private Context mContext;

    /**
     * 圆的画笔
     */
    private Paint mCirclePaint;

    /**
     * 圆心的 x,y 坐标
     */
    private float mCircleXY;

    /**
     * 圆的半径
     */
    private float mRadius;

    /**
     * 屏幕宽度
     */
    private int mScreenWidth;

    /**
     * 文字的画笔
     */
    private Paint mTextPaint;

    /**
     * 绘制的文字
     */
    private String mShowText = "Android Skill";

    /**
     * 文字的大小
     */
    private int mShowTextSize = 50;

    /**
     * 圆弧的画笔
     */
    private Paint mArcPaint;

    /**
     * 圆弧扫过的角度
     */
    private float sweepAngle;

    /**
     * 矩形,用来辅助绘制圆弧
     */
    private RectF mRectF;

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

    public CircleProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
    }

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

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

        // 获取屏幕宽度
        if (!isInEditMode()) {
            mScreenWidth = getScreenWidth((Activity) mContext);
        } else {
            // 这里填写你自己手机或模拟器的屏幕宽度
            mScreenWidth = 768;
        }
//        mScreenWidth = getScreenWidth((Activity) mContext);

        // 圆心的 x,y 坐标
        mCircleXY = mScreenWidth / 2f;
        // 圆的半径
        mRadius = mScreenWidth / 4f;

        // 圆弧扫过的角度,360 度的 66%
        sweepAngle = 66 / 100f * 360f;

        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        // 圆的画笔
        mCirclePaint = new Paint();
        // 颜色
        mCirclePaint.setColor(getResources().getColor(
                android.R.color.holo_blue_bright));

        // 文字的画笔
        mTextPaint = new Paint();
        // 颜色
        mTextPaint.setColor(Color.BLACK);
        // 文字大小
        mTextPaint.setTextSize(mShowTextSize);
        // 文本对齐
        // Align.CENTER 表示文字原点和文字中间对齐
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        // Align.LEFT 表示文字原点和文字左边缘对齐
//        mTextPaint.setTextAlign(Paint.Align.LEFT);
        // Align.RIGHT 表示文字原点和文字右边缘对齐
//        mTextPaint.setTextAlign(Paint.Align.RIGHT);

        // 弧形的画笔
        mArcPaint = new Paint();
        // 颜色
        mArcPaint.setColor(getResources().getColor(
                android.R.color.holo_blue_bright));
        // 描边
        mArcPaint.setStyle(Paint.Style.STROKE);
        // 描边宽度
        mArcPaint.setStrokeWidth(mScreenWidth * 0.1f);

        // 创建一个矩形,用来辅助绘制圆弧
        mRectF = new RectF(
                mScreenWidth * 0.1f, 
                mScreenWidth * 0.1f,
                mScreenWidth * 0.9f, 
                mScreenWidth * 0.9f);
    }

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

        // 绘制圆
        // 第一个参数是 圆心的 x 坐标
        // 第二个参数是 圆心的 y 坐标
        // 第三个参数是 圆的半径
        // 第四个参数是 画笔
        canvas.drawCircle(
                mCircleXY,
                mCircleXY,
                mRadius,
                mCirclePaint);

        // 绘制文字
        // 第一个参数是 要绘制的文字内容
        // 第二个参数是 要绘制的文字中第一个字符的索引
        // 第三个参数是 要绘制的文字中最后一个字符的索引减去一
        // 第四个参数是 要绘制文字的原点的 x 坐标
        // 第五个参数是 要绘制文字的原点的 y 坐标
        // 第六个参数是 画笔
        canvas.drawText(
                mShowText,
                0,
                mShowText.length(),
                mCircleXY, mCircleXY + mShowTextSize / 4,
                mTextPaint);

        // 绘制圆弧
        // 第一个参数是 椭圆的边界,用来定义弧形的形状和大小
        // 第二个参数是 圆弧的起始角度
        // 第三个参数是 圆弧扫过的角度
        // 第四个参数是 这个参数如果为 true,就把椭圆的中心包括在圆弧中
        // 第五个参数是 画笔
        canvas.drawArc(
                mRectF,
                270,
                sweepAngle,
                false,
                mArcPaint);
    }

    /**
     * 获取屏幕宽度
     *
     * @param activity
     * @return
     */
    private int getScreenWidth(Activity activity) {
        DisplayMetrics metrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
        return metrics.widthPixels;
    }
}

源码下载

猜你喜欢

转载自blog.csdn.net/like_program/article/details/53241308