Android自定义View初探(一)——饼图

接触Android已有两年有余,自从工作后都是为了工作而学习,没有时间去想、去做一些事情,久而久之,发现自己除了复制粘贴别人的代码和敲打一些简单的代码之外,无所长进。

当然,这里开始尝试做一些事情的时候,并不一定就是“长进”了,只是为了一点点突破,对自己思维的突破,对自己从无到有的突破。因为我意识到,有些事情,你不去尝试,你永远都无法进步。

对于从事Android开发工作的人来说,View这个东西既熟悉又陌生。熟悉的是他的基本功能,陌生的是他的原理。曾经面试过几个公司,面试官的问题中都提到了View相关的问题,不过大致都是“你只需要简单介绍View的基本绘制流程,不需要非常了解”,说到这个,我相信绝大部分Android开发者的大脑中都会迅速的呈现:onLayout、onMeasure,onDraw——如果这些你不知道,那你就需要去补补功课了。

当然,今天我们这里也只是入门操作,所以可以先不管那些复杂的自定义View的实现,我们今天只尝试在onDraw方法里做一些简单的自定义操作。

可能github或者别的地方早已有类似的开源控件,但是我没找到,所以只好自己做了。我不想只贴代码,我想让所有看过这篇文章的人都了解我的想法,或许很多地方是错的,这样不就为你们提供了反面教材吗?O(∩_∩)O

先看两张图片(只需要看图片上半部分即可):
Android自定义View初探(一)——饼图_第1张图片
Android自定义View初探(一)——饼图_第2张图片

看到这两张图片后,Android高手已经知道实现的原理了,不过这里我还是要唠叨一下,万一有人不知道呢?O(∩_∩)O

整个View分为两个大部分:左侧的圆和右侧的矩形(相当于图列),矩形的底色和圆的底色保持一致;左侧其实是六个扇形和一个圆外加三个text组成,右侧是六个圆角矩形加九个text(百分比、时长、运动类型*3)组成。

绘制的流程可以随意控制,我是先画的左边部分,这部分的代码如下(代码里有注释,其实难点在于计算图形和位子的位置):

    /**
     * 画运动饼图
     * 
     * @param canvas
     */
    private void drawSport_left(Canvas canvas)
    {
        //左边部分的中心点坐标(px)
        final float centerX = width / 3;
        final float centerY = height / 2;
        float baseDgree = 0;
        float total = 0;
        if (basePieAmounts != null && basePieAmounts.length > 0 && basePieColors != null
                && basePieColors.length > 0)
        {
            for (int i = 0; i < basePieAmounts.length; i++)
            {
                total += basePieAmounts[i];
            }
            // 画基础扇形
            for (int i = 0; i < basePieAmounts.length; i++)
            {
                float sweepAngle = (-DEFAULT_CIRCLE_ANGLE / total) * basePieAmounts[i];
                paint.setColor(SPORT_BASE_SWEEP_COLOR);
                paint.setAlpha(OPAQUE);
                paint.setStyle(Style.FILL);
                canvas.drawArc(pieRectf, baseDgree, sweepAngle + DEFAULT_OFFSET_ANGLE, true, paint);
                baseDgree -= -sweepAngle;
            }
        }
        // 画占比扇形
        if (childernAmounts != null && childernAmounts.length > 0)
        {
            baseDgree = 0;
            for (int i = 0; i < childernAmounts.length; i++)
            {
                float sweepAngle = (-DEFAULT_CIRCLE_ANGLE / total) * basePieAmounts[i];
                // 画占比扇形
                paint.setColor(childPieColors[i]);
                paint.setStyle(Style.FILL);
                paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OVER));
                paint.setAlpha(OPAQUE);
                canvas.drawArc(pieRectf, baseDgree, childernAmounts[i]
                        / (basePieAmounts[i] / (sweepAngle + DEFAULT_OFFSET_ANGLE)), true, paint);//这个DEFAULT_OFFSET_ANGLE就是为了实现扇形之间的间隙的

                baseDgree -= -sweepAngle;
            }
        }
        // 画能量消耗小圆
        paint.setColor(Color.WHITE);
        canvas.drawCircle(centerX, centerY, DEFAULT_CIRCLE_RADIUS / 1.6f, paint);
        // 画小圆能量消耗文本
        paint.setColor(Color.BLACK);
        paint.setTextSize(sportPowerCenterTextSize * density);
        paint.setTypeface(Typeface.create("System", Typeface.BOLD));
        paint.getTextBounds(sportPowerText, 0, sportPowerText.length(), rect);
        int tempH = rect.height();
        canvas.drawText(sportPowerText, centerX - rect.width() / 2, centerY + tempH / 2,
                paint);
        // 画小圆顶部文本
        resetPaint();
        paint.setColor(Color.GRAY);
        paint.setTextSize(sportPowerRoundTextSize * density);
        paint.getTextBounds(SPORT_POWER_TOP_TEXT, 0, SPORT_POWER_TOP_TEXT.length(), rect);
        canvas.drawText(SPORT_POWER_TOP_TEXT, centerX - rect.width() / 2,
                centerY - tempH - rect.height() / 2, paint);
        // 画小圆底部文本(可不重新设置paint)
        paint.setColor(Color.GRAY);
        paint.setTextSize(sportPowerRoundTextSize * density);
        paint.getTextBounds(SPORT_POWER_BOTTOM_TEXT, 0, SPORT_POWER_BOTTOM_TEXT.length(), rect);
        canvas.drawText(SPORT_POWER_BOTTOM_TEXT, centerX - rect.width() / 2,
                centerY + 2 * rect.height() + tempH / 2, paint);
    }

里面有些参数看不懂的不用纠结,后面我会贴上完整的代码,这里只是展现左侧部分的绘制:先画三个基础的时间长度扇形,并不是等分的,比如从0度逆时针看,每个扇形代表的时长依次是15min、10min、5min,那么对应的扇形所占角度为360/30*15、360/30*10、360/30*5,知道这个原理后,对应时长的已完成时长(占比扇形的角度)也就知道了。

再看看右边部分,代码如下:

    /**
     * 画样例
     * 
     * @param canvas
     */
    private void drawSportLabels(Canvas canvas)
    {
        // 画底部样例
        if (bottomSamples != null && bottomSamples.length > 0 && basePieColors != null
                && basePieColors.length > 0)
        {
            resetPaint();
            float offect = 0;
            float textH = 0;
            final float offsetX = width / 3 + DEFAULT_CIRCLE_RADIUS;//与左侧圆的距离
            final float offsetY = height / 2 - 7.25f * DEFAULT_SPACING_HEIGHT * density;//7.25的由来:2.25的矩形高度+5
            for (int i = 0; i < bottomSamples.length; i++)
            {
                String text = bottomSamples[i];
                String sportTime = sportTimes[i];
                paint.setColor(SPORT_BASE_SWEEP_COLOR);
                paint.setAlpha(OPAQUE);
                paint.setTextSize(sampleTextSize * density);
                paint.getTextBounds(text, 0, text.length(), rect);
                textH = rect.height();
                // Log.e(TAG, "textLen : " + rect.width() + " textHeigh : " + rect.height());
                // 画图列方块
                RectF tempRectF = new RectF(offsetX + DEFAULT_SPACING_WIDTH * density, offsetY
                        + (DEFAULT_SPACING_HEIGHT + offect) * density, offsetX + 2.5f
                        * DEFAULT_SPACING_WIDTH * density, offsetY
                        + (2.5f * DEFAULT_SPACING_HEIGHT + offect) * density);
                canvas.drawRoundRect(tempRectF, 5.0f, 5.0f, paint);

                // 画图列文本(运动类型)
                paint.setColor(childPieColors[i]);
                canvas.drawText(text, offsetX + DEFAULT_SPACING_WIDTH * density, offsetY
                        + (2.75f * DEFAULT_SPACING_HEIGHT + offect) * density+textH, paint);

                // 画运动时长
                paint.setTextSize(sampleTextSize * density * 2);
                paint.setTypeface(Typeface.create("System", Typeface.BOLD));
                paint.getTextBounds(sportTime, 0, sportTime.length(), rect);
                canvas.drawText(sportTime, offsetX + (2.6f * DEFAULT_SPACING_WIDTH) * density, offsetY
                        + (1.5f * DEFAULT_SPACING_HEIGHT + offect) * density+rect.height()/2,
                        paint);
                // 画运动时长占比矩形
                float persent = childernAmounts[i] / basePieAmounts[i];
                float child = 1.5f * DEFAULT_SPACING_WIDTH * persent;
                String per = decimalFormat.format(persent * 100) + "%";
                paint.setColor(childPieColors[i]);
                tempRectF = new RectF(offsetX + DEFAULT_SPACING_WIDTH * density, offsetY
                        + (DEFAULT_SPACING_HEIGHT + offect) * density, offsetX
                        + (DEFAULT_SPACING_WIDTH + child) * density, offsetY
                        + (2.5f * DEFAULT_SPACING_HEIGHT + offect) * density);
                canvas.drawRoundRect(tempRectF, 5.0f, 5.0f, paint);
                //画运动时长占比百分数
                paint.setColor(Color.WHITE);
                paint.setTypeface(Typeface.create("System", Typeface.NORMAL));
                paint.setTextSize(sampleTextSize * density);
                paint.getTextBounds(per, 0, per.length(), rect);
                canvas.drawText(
                        per,
                        //1.75是1.5的矩形宽度+0.25的偏移,文字和矩形分开
                        offsetX + 1.75f * DEFAULT_SPACING_WIDTH * density - rect.width() / 2,
                        //2.25是要减去1.25的矩形高度,再减去1矩形的高度让文字底部与矩形底部平行
                        offsetY + (2.25f * DEFAULT_SPACING_HEIGHT + offect) * density
                                - rect.height() / 2, paint);
                //每画一次向下移动5个距离
                offect += 5 * DEFAULT_SPACING_HEIGHT;
            }
        }
    }

这里的原理也很简单:计算出一个矩形的位置(并不一定要像我一样从上往下画),再根据这个位置画出与其相邻的图形或文字,就是那些坐标要注意适配屏幕,注意density的使用。

画图形和文字的核心代码就是这两个方法里的内容,当然,这两个方法都是在onDraw里执行的。我知道一些开发者只看这两部分代码,也不理解我文章的意图,毕竟我也是第一次尝试做这样的事情,难免有想不到的地方,下面我就贴出整个自定义View的代码,与大家一起共同探讨和交流:

/**
 * PieView
 * @author MR.yan 
* create at 2015年3月10日 下午4:19:47 */
public class PieView extends View { static final String TAG = PieView.class.getSimpleName(); /** * 数据显示类型-能量 */ public static final int CHART_DRAWING_DRAW_TYPE_POWER = 1; /** * 数据显示类型-睡眠 */ public static final int CHART_DRAWING_DRAW_TYPE_SLEEP = CHART_DRAWING_DRAW_TYPE_POWER + 1; /** * 数据显示类型-健康 */ public static final int CHART_DRAWING_DRAW_TYPE_HEALTH = CHART_DRAWING_DRAW_TYPE_POWER + 2; /** * 数据显示类型-运动 */ public static final int CHART_DRAWING_DRAW_TYPE_SPORT = CHART_DRAWING_DRAW_TYPE_POWER + 3; private static final int SPORT_BASE_SWEEP_COLOR = Color.parseColor("#d3d3d3"); /** * 能量消耗顶部固定文本 */ private static final String SPORT_POWER_TOP_TEXT = "能量消耗"; /** * 能量消耗底部固定文本 */ private static final String SPORT_POWER_BOTTOM_TEXT = "大卡"; /** * 圆周角度 */ public static final float DEFAULT_CIRCLE_ANGLE = 360.0f; /** * 默认扇形角度差值 */ public static final float DEFAULT_OFFSET_ANGLE = 1.5f; /** * 透明度 */ private static final int OPAQUE = 0xFF; /** * 饼图半径 */ private float DEFAULT_CIRCLE_RADIUS = 300; /** * 默认水平间距 */ private float DEFAULT_SPACING_WIDTH = 20; /** * 默认垂直间距 */ private float DEFAULT_SPACING_HEIGHT = 10; /** * 总数量 */ private float totalAmounts; /** * 屏幕密度 */ private float density; /** * 画笔 */ private Paint paint; /** * 是否初始化过 */ private boolean isInit; /** * 测量文本宽高的矩形 */ private Rect rect; /** * 图表视图的宽度(px) */ private float width; /** * 图表视图的高度(px) */ private float height; /** * 底部图表示例文本数组 */ private String bottomSamples[] = new String[] { "轻度运动", "中度运动", "剧烈运动" }; // 运动时长数组 private String sportTimes[] = new String[] { "0分钟", "0分钟", "0分钟" }; /** * 底部图表示例文本的大小 sp */ private float sampleTextSize = 10; /** * 运动能量消耗值字体大小 */ private float sportPowerCenterTextSize = 30; /** * 运动能量消耗其他字体大小 */ private float sportPowerRoundTextSize = 18; /** * 是否在扇形上显示文本 */ private boolean showPieText; "sport_lsport_base_color">#167aa4 "sport_lsport_over_color">#00b4ff "sport_msport_base_color">#1c924b "sport_msport_over_color">#0fce5b "sport_hsport_base_color">#b82649 "sport_hsport_over_color">#ff2f06 /** * 起始饼状图的颜色数组(主要是运动起始扇形) */ private int basePieColors[] = new int[] { getResources().getColor(R.color.sport_lsport_base_color), getResources().getColor(R.color.sport_msport_base_color), getResources().getColor(R.color.sport_hsport_base_color) }; /** * 占比饼状图的颜色数组(主要是运动占比扇形) */ private int childPieColors[] = new int[] { getResources().getColor(R.color.sport_lsport_over_color), getResources().getColor(R.color.sport_msport_over_color), getResources().getColor(R.color.sport_hsport_over_color) }; /** * 起始饼状图的值数组(对应扇形) */ private float basePieAmounts[] = new float[] { 1, 1, 1 }; /** * 占比扇形的值数组 */ private float[] childernAmounts = new float[] { 0, 0, 0 }; /** * 当前的图表类型 */ private int mCurrentDrawType; /** * 默认图表背景颜色 */ private int defaultBgColor = color.whitesmoke; /** * 占比扇形的颜色 */ private int smallPieColor = Color.YELLOW; /** * 外圆外切矩形 */ private RectF pieRectf; /** * 格式化小数 */ private DecimalFormat decimalFormat; /** * 内圆内的文本 */ private String scoreText = "0分"; /** * 得分的字体 */ private float scoreTextSize = 10; /** * 图表标题 */ private String pieTitle = ""; /** * 运动图表能量消耗文本 */ private String sportPowerText = "0"; public String[] getSportTimes() { return sportTimes; } public void setSportTimes(String[] sportTimes) { this.sportTimes = sportTimes; } public float getSportPowerCenterTextSize() { return sportPowerCenterTextSize; } public void setSportPowerCenterTextSize(float sportPowerCenterTextSize) { this.sportPowerCenterTextSize = sportPowerCenterTextSize; } public float getSportPowerRoundTextSize() { return sportPowerRoundTextSize; } public void setSportPowerRoundTextSize(float sportPowerRoundTextSize) { this.sportPowerRoundTextSize = sportPowerRoundTextSize; } public String getSportPowerText() { return sportPowerText; } public void setSportPowerText(String sportPowerText) { this.sportPowerText = sportPowerText; } public String getPieTitle() { return pieTitle; } public void setPieTitle(String pieTitle) { this.pieTitle = pieTitle; } public float getTotalAmounts() { return totalAmounts; } public void setTotalAmounts(float totalAmounts) { this.totalAmounts = totalAmounts; } public float getScoreTextSize() { return scoreTextSize; } public void setScoreTextSize(float scoreTextSize) { this.scoreTextSize = scoreTextSize; } public String getScoreText() { return scoreText; } public void setScoreText(String scoreText) { this.scoreText = scoreText; } /** * 获取占比扇形颜色 * * @return */ public int getSmallPieColor() { return smallPieColor; } /** * 设置占比扇形颜色 * * @param smallPieColor */ public void setSmallPieColor(int smallPieColor) { this.smallPieColor = smallPieColor; } /** * 获取当前的图表类型 * * @return */ public int getmCurrentDrawType() { return mCurrentDrawType; } /** * 设置当前图表类型 * * @param mCurrentDrawType */ public void setmCurrentDrawType(int mCurrentDrawType) { this.mCurrentDrawType = mCurrentDrawType; } /** * 获取图表默认背景颜色 * * @return */ public int getDefaultBgColor() { return defaultBgColor; } /** * 设置图表默认背景颜色 * * @param defaultBgColor */ public void setDefaultBgColor(int defaultBgColor) { this.defaultBgColor = defaultBgColor; } /** * 获取起始饼状颜色数组 * * @return */ public int[] getBasePieColors() { return basePieColors; } /** * 设置起始饼状颜色数组 * * @param basePieColors */ public void setBasePieColors(int[] basePieColors) { this.basePieColors = basePieColors; } /** * 获取基础扇形的对应值 * * @return */ public float[] getBasePieAmounts() { return basePieAmounts; } /** * 设置基础扇形的值 * * @param basePieAmounts */ public void setBasePieAmounts(float[] basePieAmounts) { this.basePieAmounts = basePieAmounts; } /** * 获取底部示例文本数组 * * @return */ public String[] getBottomSamples() { return bottomSamples; } /** * 设置底部示例文本数组 * * @param bottomSamples */ public void setBottomSamples(String[] bottomSamples) { this.bottomSamples = bottomSamples; } /** * 获取底部文本示例文字大小 * * @return */ public float getSampleTextSize() { return sampleTextSize; } /** * 设置底部文本示例文字大小 * * @param sampleTextSize */ public void setSampleTextSize(float sampleTextSize) { this.sampleTextSize = sampleTextSize; } /** * 是否在饼状图上显示文本 * * @return */ public boolean isShowPieText() { return showPieText; } /** * 设置是否在饼状图上显示文本 * * @param showBarText */ public void setShowPieText(boolean showBarText) { this.showPieText = showBarText; } /** * 增加占比扇形图(包含的值的顺序必须和构造时传入的初始值的顺序一样) * * @param amounts 新增的量集合 * @return */ public synchronized int onDrawPieChar(float amounts[]) { if (amounts == null || amounts.length < 1) return -1; if (basePieAmounts == null || basePieAmounts.length < 1 || basePieColors == null || basePieColors.length < 0) return -2; for (int i = 0; i < amounts.length; i++) { if (amounts[i] < 0) amounts[i] = 0; if (mCurrentDrawType == CHART_DRAWING_DRAW_TYPE_SPORT) if (amounts[i] > basePieAmounts[i]) amounts[i] = basePieAmounts[i]; } childernAmounts = Arrays.copyOf(amounts, amounts.length); postInvalidate(); return 0; } public PieView(Context c) { this(c, null); } public PieView(Context c, AttributeSet attributeSet) { this(c, attributeSet, 0); } public PieView(Context c, AttributeSet attributeSet, int defaultStyle) { super(c, attributeSet, defaultStyle); init(c); } /** * 初始化 * * @param c */ private void init(Context c) { density = c.getResources().getDisplayMetrics().density; paint = new Paint(); pieRectf = new RectF(); rect = new Rect(); decimalFormat = new DecimalFormat(); decimalFormat.setMaximumFractionDigits(0); } private void resetPaint() { paint.reset(); paint.setAntiAlias(true); } @Override public void onDraw(Canvas canvas) { drawInit(canvas); DEFAULT_SPACING_WIDTH = 30; DEFAULT_SPACING_HEIGHT = 12; drawSport_left(canvas); drawSportLabels(canvas); } /** * 画初始部分 * * @param canvas */ private void drawInit(Canvas canvas) { resetPaint(); if (!isInit) { width = getWidth(); height = getHeight(); float temp = width > height ? height : width; if (DEFAULT_CIRCLE_RADIUS > temp / 2 - 15 * density) DEFAULT_CIRCLE_RADIUS = temp / 2 - 15 * density; switch (mCurrentDrawType) { case CHART_DRAWING_DRAW_TYPE_HEALTH: pieRectf = new RectF(width / 2 - DEFAULT_CIRCLE_RADIUS, height / 2 - DEFAULT_CIRCLE_RADIUS, width / 2 + DEFAULT_CIRCLE_RADIUS, height / 2 + DEFAULT_CIRCLE_RADIUS);// 设置个新的长方形,扫描测量 break; case CHART_DRAWING_DRAW_TYPE_SPORT: pieRectf = new RectF(width / 3 - DEFAULT_CIRCLE_RADIUS, height / 2 - DEFAULT_CIRCLE_RADIUS, width / 3 + DEFAULT_CIRCLE_RADIUS, height / 2 + DEFAULT_CIRCLE_RADIUS);// 设置个新的长方形,扫描测量 break; } // Log.e(TAG, "widtdh : " + width + " height : " + height); isInit = true; } // 画标题 paint.setColor(Color.GRAY); paint.setTypeface(Typeface.create("System", Typeface.BOLD)); paint.setTextSize(sampleTextSize * density); paint.getTextBounds(pieTitle, 0, pieTitle.length(), rect); canvas.drawText(pieTitle, DEFAULT_SPACING_WIDTH * density, DEFAULT_SPACING_HEIGHT * density + rect.height(), paint); resetPaint(); // // 图表底色 // paint.setColor(defaultBgColor); // paint.setAlpha(OPAQUE); // canvas.drawRect(0, 0, width, height, paint); } /** * 画样例 * * @param canvas */ private void drawSportLabels(Canvas canvas) { // 画底部样例 if (bottomSamples != null && bottomSamples.length > 0 && basePieColors != null && basePieColors.length > 0) { resetPaint(); float offect = 0; float textH = 0; final float offsetX = width / 3 + DEFAULT_CIRCLE_RADIUS; final float offsetY = height / 2 - 7.25f * DEFAULT_SPACING_HEIGHT * density; for (int i = 0; i < bottomSamples.length; i++) { String text = bottomSamples[i]; String sportTime = sportTimes[i]; paint.setColor(SPORT_BASE_SWEEP_COLOR); paint.setAlpha(OPAQUE); paint.setTextSize(sampleTextSize * density); paint.getTextBounds(text, 0, text.length(), rect); textH = rect.height(); // Log.e(TAG, "textLen : " + rect.width() + " textHeigh : " + rect.height()); // 画图列方块 RectF tempRectF = new RectF(offsetX + DEFAULT_SPACING_WIDTH * density, offsetY + (DEFAULT_SPACING_HEIGHT + offect) * density, offsetX + 2.5f * DEFAULT_SPACING_WIDTH * density, offsetY + (2.5f * DEFAULT_SPACING_HEIGHT + offect) * density); canvas.drawRoundRect(tempRectF, 5.0f, 5.0f, paint); // 画图列文本 paint.setColor(childPieColors[i]); canvas.drawText(text, offsetX + DEFAULT_SPACING_WIDTH * density, offsetY + (2.75f * DEFAULT_SPACING_HEIGHT + offect) * density+textH, paint); // 画运动时长 paint.setTextSize(sampleTextSize * density * 2); paint.setTypeface(Typeface.create("System", Typeface.BOLD)); paint.getTextBounds(sportTime, 0, sportTime.length(), rect); canvas.drawText(sportTime, offsetX + (2.6f * DEFAULT_SPACING_WIDTH) * density, offsetY + (1.5f * DEFAULT_SPACING_HEIGHT + offect) * density+rect.height()/2, paint); // 画图列百分比 float persent = childernAmounts[i] / basePieAmounts[i]; float child = 1.5f * DEFAULT_SPACING_WIDTH * persent; String per = decimalFormat.format(persent * 100) + "%"; paint.setColor(childPieColors[i]); tempRectF = new RectF(offsetX + DEFAULT_SPACING_WIDTH * density, offsetY + (DEFAULT_SPACING_HEIGHT + offect) * density, offsetX + (DEFAULT_SPACING_WIDTH + child) * density, offsetY + (2.5f * DEFAULT_SPACING_HEIGHT + offect) * density); canvas.drawRoundRect(tempRectF, 5.0f, 5.0f, paint); //画运动时长占比百分数 paint.setColor(Color.WHITE); paint.setTypeface(Typeface.create("System", Typeface.NORMAL)); paint.setTextSize(sampleTextSize * density); paint.getTextBounds(per, 0, per.length(), rect); canvas.drawText( per, offsetX + 1.75f * DEFAULT_SPACING_WIDTH * density - rect.width() / 2, offsetY + (2.25f * DEFAULT_SPACING_HEIGHT + offect) * density - rect.height() / 2, paint); //每画一次向下移动5个距离 offect += 5 * DEFAULT_SPACING_HEIGHT; } } } /** * 画运动饼图 * * @param canvas */ private void drawSport_left(Canvas canvas) { //左边部分的中心点坐标(px) final float centerX = width / 3; final float centerY = height / 2; float baseDgree = 0; float total = 0; if (basePieAmounts != null && basePieAmounts.length > 0 && basePieColors != null && basePieColors.length > 0) { for (int i = 0; i < basePieAmounts.length; i++) { total += basePieAmounts[i]; } // 画基础扇形 for (int i = 0; i < basePieAmounts.length; i++) { float sweepAngle = (-DEFAULT_CIRCLE_ANGLE / total) * basePieAmounts[i]; paint.setColor(SPORT_BASE_SWEEP_COLOR); paint.setAlpha(OPAQUE); paint.setStyle(Style.FILL); canvas.drawArc(pieRectf, baseDgree, sweepAngle + DEFAULT_OFFSET_ANGLE, true, paint); baseDgree -= -sweepAngle; } } // 画占比扇形 if (childernAmounts != null && childernAmounts.length > 0) { baseDgree = 0; for (int i = 0; i < childernAmounts.length; i++) { float sweepAngle = (-DEFAULT_CIRCLE_ANGLE / total) * basePieAmounts[i]; // 画占比扇形 paint.setColor(childPieColors[i]); paint.setStyle(Style.FILL); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OVER)); paint.setAlpha(OPAQUE); canvas.drawArc(pieRectf, baseDgree, childernAmounts[i] / (basePieAmounts[i] / (sweepAngle + DEFAULT_OFFSET_ANGLE)), true, paint); baseDgree -= -sweepAngle; } } // 画能量消耗小圆 paint.setColor(Color.WHITE); canvas.drawCircle(centerX, centerY, DEFAULT_CIRCLE_RADIUS / 1.6f, paint); // 画小圆能量消耗文本 paint.setColor(Color.BLACK); paint.setTextSize(sportPowerCenterTextSize * density); paint.setTypeface(Typeface.create("System", Typeface.BOLD)); paint.getTextBounds(sportPowerText, 0, sportPowerText.length(), rect); int tempH = rect.height(); canvas.drawText(sportPowerText, centerX - rect.width() / 2, centerY + tempH / 2, paint); // 画小圆顶部文本 resetPaint(); paint.setColor(Color.GRAY); paint.setTextSize(sportPowerRoundTextSize * density); paint.getTextBounds(SPORT_POWER_TOP_TEXT, 0, SPORT_POWER_TOP_TEXT.length(), rect); canvas.drawText(SPORT_POWER_TOP_TEXT, centerX - rect.width() / 2, centerY - tempH - rect.height() / 2, paint); // 画小圆底部文本(可不重新设置paint) paint.setColor(Color.GRAY); paint.setTextSize(sportPowerRoundTextSize * density); paint.getTextBounds(SPORT_POWER_BOTTOM_TEXT, 0, SPORT_POWER_BOTTOM_TEXT.length(), rect); canvas.drawText(SPORT_POWER_BOTTOM_TEXT, centerX - rect.width() / 2, centerY + 2 * rect.height() + tempH / 2, paint); } }

你可能感兴趣的:(android,View)