編輯:關於Android編程
今天寫一個餅圖自定義View的文章。由於公司的項目需要用到餅圖,UI給的設計圖和自己找的一個餅圖框架的標題位置不符,所以就自己畫了一個。
PieChart mChart mChart = (PieChart) findViewById(R.id.pieChar); mChart = (PieChart) findViewById(R.id.pieChar); String[] titles = new String[] {"錢包余額","金錢袋資產","金寶箱資產"}; mChart.setTitles(titles); int[] colors = new int[]{0xfff5a002,0xfffb5a2f,0xff36bc99}; mChart.setColors(colors); mChart.setValues(new double[]{999,999,999}); mChart.postInvalidate();需要傳入標題和值,每個餅的顏色也可以重新設置,不傳顏色值就用默認的。畫餅圖的難點就在數學計算和邏輯思維能力。
/** * 獲得每個值所占的角度 * @return */ private float[] getAngles(){ if(mValues == null || mValues.length == 0) return null; double sum = 0; int len = mTitles.length; float[] angles = new float[len]; int gapCount = 0;//餅圖間隙條數 for(int i=0;i根據傳過來的值計算每個值所占的角度,餅圖之間有1°的間隙, if(mValues[i]>0) gapCount++;只有值大於0時才畫餅,才增加一條間隙。 pieAngle = 360 - gapCount*ANGLE_DIS;這裡計算所有餅所占整個圓的角度。 if(mValues[len-1]>0) angles[len - 1] = pieAngle - angle;最後一個餅的角度為餅的總角度減去前面所有餅的角度之和,這樣做是為了讓所有餅的角度加起來等於pieAngle這個角度,減少除法運算的小數誤差效果。0) gapCount++; } float angle = 0; pieAngle = 360 - gapCount*ANGLE_DIS; for(int i=0;i 0) angles[len - 1] = pieAngle - angle; return angles; }
/** * 在餅圖的每個餅上寫上百分比,必須放在計算了每個值所占的角度angles之後 * @param canvas */ private void setPieContentText(Canvas canvas){ float pre = 0; float[] centerAngle = new float[mAngles.length]; for(int i=0;i這段代碼是計算百分比文字在圓內的角度,centerAngle[i] = ANGLE_DIS+mAngles[i]/2 + pre;百分比文字在每個餅的中心。 float cenR = pieR*1.0f/2*3/5;計算百分比文字中心到圓心的距離,可根據需要做調整,pieR為餅圖直徑。 如上圖所示,float xa = (float) (cenR * Math.cos(centerAngle[i] * (Math.PI / 180)));float ya = (float) (cenR * Math.sin(centerAngle[i] * (Math.PI / 180)));中xa和ya為文字到圓心的水平垂直距離。cenX = getWidth()*1.0f/2+xa; cenY = TOP_PADDING + pieR*1.0f/2 + ya;cenX 和cenY為文字在view上面的坐標點。
import java.text.DecimalFormat; import java.util.List; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import cn.golditfin.component.amount.AmountUtils; /** * 圓形餅圖控件,title為多少條就顯示多少條,無視value * @author HuangYuGuang * Create on 2015年12月18日 * File Name PieChart.java */ public class PieChart extends View{ private final float density = getResources().getDisplayMetrics().density; /**餅圖之間間隔的角度*/ private final float ANGLE_DIS = 1; /**左右兩邊空間距離*/ private final float LR_PADDING = 25 * density; /**頂部空間距離*/ private final float TOP_PADDING = 25 * density; /**餅圖和下邊文字的距離*/ private final float PIE_TEXT_DIS = 22 * density; /**上下行文字的距離*/ private final float TEXT_TEXT_DIS = 15 * density; /**底部和文字的距離,實際為BOTTOM_DIS+TEXT_TEXT_DIS*/ private final float BOTTOM_DIS = 10 * density; /**標題和值的最小距離*/ private final float TITLE_VALUE_DIS = 18 * density; /**每部分的顏色值,默認有10個顏色 */ private int[] mColors = new int[]{0xfff5a002,0xfffb5a2f,0xff36bc99,0xff43F90C,0xff181A18,0xffF802F6 ,0xff022DF8,0xffECF802,0xff02F8E8,0xffEA0F8E}; /**值*/ private double[] mValues; /**值轉換成角度*/ private float[] mAngles; /**餅圖直徑*/ private float pieR; /**餅圖所占總的角度*/ private float pieAngle; private String[] mTitles ; //每部分的內容 private String mEmptyMsg = "暫無數據"; //無數據提示的內容 private float mTitleSize; private float mValueSize; /**餅圖裡面文字的大小*/ private float mPieTextSize; private int mTitleColor = 0xFF595959; private int mValueColor = 0xFF595959; private int mDefaultPointColor = 0xfff5a002; //無數據時提示文字的顏色 private Rect mTextBound; private Paint mTextPaint; private Paint mPiePaint; public PieChart(Context context) { this(context,null); } public PieChart(Context context, AttributeSet attrs) { this(context, attrs,0); } public PieChart(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTitleSize = sp2px(14); mPieTextSize = sp2px(12); mValueSize = sp2px(16); mPiePaint = new Paint(); mTextPaint = new Paint(); mTextBound = new Rect(); mTextPaint.setColor(0xff595959); mTextPaint.setTextSize(mTitleSize); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPiePaint.setAntiAlias(true); mTextPaint.setAntiAlias(true); /**------------------------------無數據--------------------------------*/ if(mTitles == null || mTitles.length == 0 || isValueEmpty()){ mPiePaint.setColor(mDefaultPointColor); mTextPaint.setColor(0xffffffff); float cr = (getWidth()<getHeight()?getWidth()/2:getHeight()/2) - LR_PADDING; canvas.drawCircle(getWidth()/2, getHeight()/2, cr, mPiePaint); mTextPaint.getTextBounds(mEmptyMsg, 0, mEmptyMsg.length(), mTextBound); canvas.drawText(mEmptyMsg, (getWidth()-mTextBound.width())/2, (getHeight()+mTextBound.height())/2, mTextPaint); return; } /**------------------------------無數據--------------------------------*/ /**------------------------------畫餅圖--------------------------------*/ int textHeight = getTextHeight("00", Math.max(mTitleSize, mValueSize)); float r1 = getWidth() - LR_PADDING*2; float r2 = getHeight() - TOP_PADDING - PIE_TEXT_DIS - (TEXT_TEXT_DIS+textHeight)*(mTitles.length) - BOTTOM_DIS; pieR = Math.min(r1, r2);//為了防止餅圖越界,餅圖直徑選取最小值 RectF oval = new RectF((getWidth()-pieR)/2, TOP_PADDING, (getWidth()+pieR)/2, TOP_PADDING+pieR); mPiePaint.setStyle(Paint.Style.FILL); mAngles = getAngles(); float startAngle = 0; for(int i=0;i<mAngles.length;i++){ mPiePaint.setColor(mColors[i]); if(mAngles[i] == 0) continue; canvas.drawArc(oval, startAngle, mAngles[i], true, mPiePaint); startAngle += (mAngles[i]+ANGLE_DIS); } /**------------------------------畫餅圖--------------------------------*/ /**------------------------------最下面的文字,title和value--------------------------------*/ float cr = 5 * density; //圓點半徑 float ctd = 8 * density; //圓點和右邊文字距離 float titleLen = getMaxTextWidth(mTitles, mTitleSize) + TITLE_VALUE_DIS; float len = 2*cr + ctd + titleLen + getMaxTextWidth(mValues, mValueSize); float topDis = TOP_PADDING + PIE_TEXT_DIS +pieR + textHeight; //第一行文字底部和控件頂部距離 float cX = (getWidth()-len)/2+cr; float titleX = cX+cr+ctd; float valueX = titleX + titleLen; int valueLen = mValues.length-1; for(int i=0;i<mTitles.length;i++){ mPiePaint.setColor(mColors[i]); float yDis = topDis+(textHeight+TEXT_TEXT_DIS)*i; canvas.drawCircle(cX, yDis-textHeight/2, cr, mPiePaint); mTextPaint.setTextSize(mTitleSize); mTextPaint.setColor(mTitleColor); canvas.drawText(mTitles[i], titleX, yDis, mTextPaint); mTextPaint.setColor(mValueColor); mTextPaint.setTextSize(mValueSize); canvas.drawText(AmountUtils.moneyFormat(i>valueLen?0:mValues[i]), valueX, yDis, mTextPaint); } /**------------------------------最下面的文字,title和value--------------------------------*/ //餅圖上的文字 setPieContentText(canvas); } /** * 設置每個餅圖代表的名字 * @param titles */ public void setTitles(List<String> titles){ mTitles = (String[])titles.toArray(); } /** * 設置每個餅圖代表的名字 * @author HuangYuGuang * Create on 2015年12月30日 * @param titles */ public void setTitles(String[] titles){ mTitles = titles; } /** * 設置值 * @param values */ public void setValues(List<Double> values){ mValues = new double[values.size()]; for(int i=0;i<values.size();i++){ mValues[i] = values.get(i); } } /** * 設置值 * @param values */ public void setValues(double[] values){ mValues = values; } /** * 設置每塊餅圖的顏色 * @param colors */ public void setColors(List<Integer> colors){ mColors = new int[colors.size()]; for(int i=0;i<colors.size();i++){ mColors[i] = colors.get(i); } } /** * 設置每塊餅圖的顏色 * @param colors */ public void setColors(int[] colors){ mColors = colors; } /** * 設置名字的字體大小 * @param size */ public void setTitleSize(float size){ mTitleSize = sp2px(size); } /** * 設置數值的大小 * @param size */ public void setValueSize(float size){ mValueSize = sp2px(size); } /** * 設置餅圖上文字的大小 * @param size */ public void setPieTextSize(float size){ mPieTextSize = sp2px(size); } /** * 名字的顏色 * @param titleColor */ public void setTitleColor(int titleColor){ mTitleColor = titleColor; } /** * 數值的顏色 * @param valueColor */ public void setValueColor(int valueColor){ mValueColor = valueColor; } /** * 設置無數據時提示文字的顏色 * Create on 2015年12月31日 * @param color */ public void setDefaultPointColor(int color){ mDefaultPointColor = color; } /** * 無數據的提示內容 * @param msg */ public void setEmptyMsg(String msg){ mEmptyMsg = msg; } private boolean isValueEmpty(){ if(mValues == null || mValues.length == 0) return true; for(double va:mValues){ if(va > 0) return false; } mEmptyMsg = "暫無數據"; return true; } /** * 獲得每個值所占的角度 * @return */ private float[] getAngles(){ if(mValues == null || mValues.length == 0) return null; double sum = 0; int len = mTitles.length; float[] angles = new float[len]; int gapCount = 0;//餅圖間隙條數 for(int i=0;i<len;i++){ sum += mValues[i]; if(mValues[i]>0) gapCount++; } float angle = 0; pieAngle = 360 - gapCount*ANGLE_DIS; for(int i=0;i<len-1;i++){ angles[i] = (float)(pieAngle*mValues[i]/sum); angle += angles[i]; } if(mValues[len-1]>0) angles[len - 1] = pieAngle - angle; return angles; } /** * 在餅圖的每個餅上寫上百分比,必須放在計算了每個值所占的角度angles之後 * @param canvas */ private void setPieContentText(Canvas canvas){ float pre = 0; float[] centerAngle = new float[mAngles.length]; for(int i=0;i<mAngles.length;i++){ if(mAngles[i] == 0) continue; centerAngle[i] = ANGLE_DIS+mAngles[i]/2 + pre; pre += mAngles[i]; } float cenR = pieR*1.0f/2*3/5; float lastPir = 1.0f;//為了使所有的%比加起來等於1 mTextPaint.setColor(0xFFFFFFFF); float cenX = 0; float cenY = 0; for(int i=0;i<centerAngle.length;i++){ if(centerAngle[i]==0 ) continue; float xa = (float) (cenR * Math.cos(centerAngle[i] * (Math.PI / 180))); float ya = (float) (cenR * Math.sin(centerAngle[i] * (Math.PI / 180))); cenX = getWidth()*1.0f/2+xa; cenY = TOP_PADDING + pieR*1.0f/2 + ya; mTextPaint.setTextSize(mPieTextSize); double curPer = numDecimals(mAngles[i]/pieAngle); String perMsg = i==centerAngle.length-1?percentFormat(lastPir):percentFormat(curPer); canvas.drawText(perMsg, cenX-getTextWidth(perMsg, mPieTextSize)*1.0f/2, cenY+getTextHeight(perMsg, mPieTextSize)*1.0f/2/2, mTextPaint); lastPir -= curPer; } } private int getMaxTextWidth(double[] ds,float size){ String[] strs = new String[ds.length]; for(int i=0;i<ds.length;i++){ strs[i] = AmountUtils.moneyFormat(ds[i]); } return getMaxTextWidth(strs, size); } private int getMaxTextWidth(String[] strs,float size){ Rect textBound = new Rect(); Paint paint = new Paint(); paint.setTextSize(size); int len = 0; for(int i=0;i<strs.length;i++){ paint.getTextBounds(strs[i], 0, strs[i].length(), textBound); len = Math.max(len, textBound.width()); } return len; } private int getTextWidth(String str,float size){ return getTextRect(str, size).width(); } private int getTextHeight(String str,float size){ return getTextRect(str, size).height(); } private Rect getTextRect(String str,float size){ Rect textBound = new Rect(); Paint paint = new Paint(); paint.setTextSize(size); paint.getTextBounds(str, 0, str.length(), textBound); return textBound; } private float sp2px(float sp){ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } /** * 格式化為百分比格式 * @param num * @return */ private String percentFormat(double num){ DecimalFormat df = new DecimalFormat("#0.00%"); return df.format(num); } /** * 保留四位小數 * @param num * @return */ public static double numDecimals(double num){ return ((int)(num*10000))*1.0d/10000; } }
關鍵點 canvas.drawBitmap(bitmap, srcRect, dstRect, null); 將bitmap的srcRect區域繪制到canva
本文實例講述了Android中WebView用法。分享給大家供大家參考,具體如下:WebView相當於一個迷你浏覽器,采用WebKit內核,因此完美支持html,java
本文實例講述了Android利用ViewPager實現用戶引導界面效果。分享給大家供大家參考,具體如下:我相信有很多朋友在裝完軟件首次打開時,有很多軟件都有一個軟件功能介
在前面的兩篇文章中,我們講到了關於ArrayAdapter的使用。用ArrayAdapter來在ListView中展示數據是很不錯的,但是很多時候,我們的ListView