/** * Author : yanbo * Date : 2015-06-03 * Time : 09:22 * Description : 自訂地區描述圖表View */public class AreaChartsView extends View { private Paint mPaint; private int[] mZeroPos = new int[2]; private int[] mMaxYPos = new int[2]; private int[] mMaxXPos = new int[2]; private int mWidth, mHight; private int mRealWidth, mRealHight; private String mTitleY, mTitleX; private ArrayList<Integer> mXLevel = new ArrayList<>(); private ArrayList<Integer> mYLevel = new ArrayList<>(); private ArrayList<String> mGridLevelText = new ArrayList<>(); private ArrayList<Integer> mGridColorLevel = new ArrayList<>(); private ArrayList<Integer> mGridTxtColorLevel = new ArrayList<>(); private int mGridLevel = mXLevel.size() - 1; //title字元大小 private int mXYTitleTextSize = 40; private int mMeasureXpos, mMeasureYpos; public AreaChartsView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setAntiAlias(true); mPaint.setFilterBitmap(true); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mWidth = getWidth(); mHight = getHeight(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); initPosition(); drawXYTitle(canvas); drawXYLine(canvas); drawContent(canvas); } private void initPosition() { //初始化座標圖的xy交點原點座標 mZeroPos[0] = mXYTitleTextSize * 2; mZeroPos[1] = mHight - mXYTitleTextSize * 4; //初始化座標圖的X軸最大值座標 mMaxXPos[0] = mWidth; mMaxXPos[1] = mHight - mXYTitleTextSize * 4; //初始化座標圖的Y軸最大值座標 mMaxYPos[0] = mXYTitleTextSize * 2; mMaxYPos[1] = mXYTitleTextSize * 2; } private void drawXYTitle(Canvas canvas) { mPaint.setColor(Color.parseColor("#1FB0E7")); mPaint.setTextSize(mXYTitleTextSize); mPaint.setTextAlign(Paint.Align.LEFT); //畫Y軸頂的title canvas.drawText(mTitleY, mMaxYPos[0] - mXYTitleTextSize * 2, mMaxYPos[1] - mXYTitleTextSize, mPaint); mPaint.setTextAlign(Paint.Align.RIGHT); //畫X軸頂的title canvas.drawText(mTitleX, mMaxXPos[0], mMaxXPos[1] + mXYTitleTextSize * 2, mPaint); } private void drawXYLine(Canvas canvas) { mPaint.setColor(Color.DKGRAY); mPaint.setTextAlign(Paint.Align.RIGHT); //畫XY軸 canvas.drawLine(mMaxYPos[0], mMaxYPos[1], mZeroPos[0], mZeroPos[1], mPaint); canvas.drawLine(mZeroPos[0], mZeroPos[1], mMaxXPos[0], mMaxXPos[1], mPaint); } private void drawContent(Canvas canvas) { mGridLevel = mXLevel.size() - 1; //計算出位移title等顯示尺標後的真實XY軸長度,便於接下來等分 mRealWidth = (mWidth - mXYTitleTextSize * 2); mRealHight = (mHight - mXYTitleTextSize * 4); //算出等分間距 int offsetX = mRealWidth/(mGridLevel); int offsetY = mRealHight/(mGridLevel+1); //迴圈繪製content for (int index=0; index<mGridLevel+1; index++) { mPaint.setColor(Color.DKGRAY); mPaint.setTextAlign(Paint.Align.RIGHT); mPaint.setTextSize(mXYTitleTextSize-5); //繪製X軸的那些座標區間點,包含0點座標 canvas.drawText(String.valueOf(mXLevel.get(index)), mZeroPos[0]+(index*offsetX), mZeroPos[1] + mXYTitleTextSize, mPaint); if (index != 0) { //繪製Y軸座標區間點,不包含0點座標,X軸已經畫過了 canvas.drawText(String.valueOf(mYLevel.get(index)), mZeroPos[0], mZeroPos[1]-(index*offsetY), mPaint); } if (index == mGridLevel) { //座標區間 = 真實區間 + 1 break; } mPaint.setColor(mGridColorLevel.get(mGridLevel - 1 - index)); mPaint.setStyle(Paint.Style.FILL); //繪製區間疊加圖譜方塊,從遠到0座標,因為小的圖會覆蓋大的圖 canvas.drawRect(mMaxYPos[0], mMaxYPos[1] + index*offsetY, mMaxXPos[0]-index*offsetX, mMaxXPos[1], mPaint); mPaint.setColor(mGridTxtColorLevel.get(index)); mPaint.setTextAlign(Paint.Align.RIGHT); mPaint.setTextSize(mXYTitleTextSize); //繪製每個方塊狀態區間的提示文字 canvas.drawText(mGridLevelText.get(index), mMaxXPos[0] - index * offsetX - mXYTitleTextSize, mMaxYPos[1] + index * offsetY + mXYTitleTextSize, mPaint); } //繪製當前座標 drawNotice(canvas, offsetX, offsetY); } private void drawNotice(Canvas canvas, int offsetX, int offsetY) { int realPosX = 0; int realPosY = 0; //計算傳入的x值與真實螢幕座標的像素值的百分比差值轉換 for (int index=0; index<mGridLevel; index++) { if (mMeasureXpos >= mXLevel.get(index) && mMeasureXpos < mXLevel.get(index+1)) { int subValue = mMeasureXpos - mXLevel.get(index); int offset = mXLevel.get(index+1) - mXLevel.get(index); realPosX = mZeroPos[0] + index*offsetX + (subValue / offset); break; } } //計算傳入的y值與真實螢幕座標的像素值的百分比差值轉換 for (int index=0; index<mGridLevel; index++) { if (mMeasureYpos >= mYLevel.get(index) && mMeasureYpos < mYLevel.get(index+1)) { int subValue = mMeasureYpos - mYLevel.get(index); int offset = mYLevel.get(index+1) - mYLevel.get(index); realPosY = mZeroPos[1] - index*offsetY - (offsetY - (subValue / offset)); break; } } //畫我們傳入的座標點的標記小紅點 mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(realPosX, realPosY, 8, mPaint); int[] centerPos = {mZeroPos[0] + mRealWidth/2, mZeroPos[1] - mRealHight/2}; mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); RectF rectF = null; Path path = new Path(); //畫紅點旁邊的提示框和文字,有四個地區,然後提示框的小三角指標方位不同 if (realPosX <= centerPos[0] && realPosY >= centerPos[1]) { //left-bottom //畫三角形 path.moveTo(realPosX+5, realPosY+5); path.lineTo(realPosX+15, realPosY+15); path.lineTo(realPosX+15, realPosY-15); //畫矩形背景 rectF = new RectF(realPosX+15, realPosY-40, realPosX+200, realPosY + 30); canvas.drawRoundRect(rectF, 15, 15, mPaint); //畫提示框的文字 mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - 5); canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX+30, realPosY, mPaint); } else if (realPosX <= centerPos[0] && realPosY < centerPos[1]) { //left-top path.moveTo(realPosX+5, realPosY+5); path.lineTo(realPosX+15, realPosY+15); path.lineTo(realPosX + 15, realPosY - 15); rectF = new RectF(realPosX+15, realPosY - 20, realPosX+200, realPosY + 50); canvas.drawRoundRect(rectF, 15, 15, mPaint); mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - 5); canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX+30, realPosY+20, mPaint); } else if (realPosX > centerPos[0] && realPosY >= centerPos[1]) { //right-bottom path.moveTo(realPosX-5, realPosY+5); path.lineTo(realPosX-15, realPosY+15); path.lineTo(realPosX - 15, realPosY - 15); rectF = new RectF(realPosX-200, realPosY-40, realPosX-15, realPosY + 30); canvas.drawRoundRect(rectF, 15, 15, mPaint); mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - 5); canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX-180, realPosY, mPaint); } else if (realPosX > centerPos[0] && realPosY < centerPos[1]) { //right-top path.moveTo(realPosX-5, realPosY+5); path.lineTo(realPosX-15, realPosY+15); path.lineTo(realPosX - 15, realPosY - 15); rectF = new RectF(realPosX-200, realPosY - 20, realPosX-15, realPosY + 50); canvas.drawRoundRect(rectF, 15, 15, mPaint); mPaint.reset(); mPaint.setColor(Color.RED); mPaint.setTextSize(mXYTitleTextSize - 5); canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX-180, realPosY+30, mPaint); } path.close(); mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawPath(path, mPaint); } //設定當前比值 public void updateValues(int x, int y) { mMeasureXpos = x; mMeasureYpos = y; postInvalidate(); } //設定XY軸頂角的title字型大小 public void setTitleTextSize(int size) { mXYTitleTextSize = size; } //初始化X軸的座標區間點值,可以不均等分 public void initXLevelOffset(ArrayList<Integer> list) { mXLevel.clear(); mXLevel.addAll(list); } //初始化Y軸的座標區間點值,可以不均等分 public void initYLevelOffset(ArrayList<Integer> list) { mYLevel.clear(); mYLevel.addAll(list); } //初始化每個區間的提示文字,如果不想顯示可以設定"" public void initGridLevelText(ArrayList<String> list) { mGridLevelText.clear(); mGridLevelText.addAll(list); } //初始化每個區間的顏色 public void initGridColorLevel(ArrayList<Integer> list) { mGridColorLevel.clear(); mGridColorLevel.addAll(list); } //初始化每個區間的提示文字顏色 public void initGridTxtColorLevel(ArrayList<Integer> list) { mGridTxtColorLevel.clear(); mGridTxtColorLevel.addAll(list); } //初始化XY軸title public void initTitleXY(String x, String y) { mTitleX = x; mTitleY = y; }}
上面代碼很簡單,核心的都已經注釋了,不需要過多解釋。核心思路就是一些座標點的計算。該控制項支援設定mergin及width與hight等屬性,支援自訂所有顏色及顯示及座標區分等,唯一缺陷就是沒來得及寫attr屬性xml設定這些值,有興趣的自己實現吧,我是沒時間了。