接觸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);
}
}