您的位置:首页 > 移动开发 > Android开发

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

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

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

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

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

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

先看两张图片(只需要看图片上半部分即可):





看到这两张图片后,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 <br/>
*         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;

<color name="sport_lsport_base_color">#167aa4</color> <!-- 轻度运动底色 -->
<color name="sport_lsport_over_color">#00b4ff</color> <!-- 轻度运动覆盖色 -->
<color name="sport_msport_base_color">#1c924b</color> <!-- 中度运动底色 -->
<color name="sport_msport_over_color">#0fce5b</color> <!-- 中度运动覆盖色 -->
<color name="sport_hsport_base_color">#b82649</color> <!-- 高度运动底色 -->
<color name="sport_hsport_over_color">#ff2f06</color> <!-- 高度运动覆盖色 -->
/**
* 起始饼状图的颜色数组(主要是运动起始扇形)
*/
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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息