Android自訂控制項(狀態提示圖表)
1 背景
前面分析那麼多系統源碼了,也該暫停下來休息一下,趁昨晚閑著看見一個有意思的需求就操練一下分析源碼後的執行個體演練—-自訂控制項。
這個執行個體很適合新手入門自訂控制項。先看下:
橫屏模式如下:
豎屏模式如下:
看見沒有,這個控制項完全自訂的,連文字等都是自訂的,沒有任何圖片等資源,就僅僅是一個小的java檔案,這個介面只有一個控制項。如下咱們看下實現代碼。
!!!!!!! 下載Demo工程源碼點擊我<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjwvcD4NCjxwPqG+uaS9s8j0y64gaHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFuYm9iZXIg16rU2Lezx+vXosP3s/a0pqOs1/DW2LfWz+2zybn7ob88L3A+DQo8aDIgaWQ9"2-執行個體代碼">2 執行個體代碼
如下就是整個工程的源碼了。
自訂上面展示的控制項AreaChartsView源碼:
/** * 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 mXLevel = new ArrayList<>(); private ArrayList mYLevel = new ArrayList<>(); private ArrayList mGridLevelText = new ArrayList<>(); private ArrayList mGridColorLevel = new ArrayList<>(); private ArrayList 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= 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= 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 list) { mXLevel.clear(); mXLevel.addAll(list); } //初始化Y軸的座標區間點值,可以不均等分 public void initYLevelOffset(ArrayList list) { mYLevel.clear(); mYLevel.addAll(list); } //初始化每個區間的提示文字,如果不想顯示可以設定 public void initGridLevelText(ArrayList list) { mGridLevelText.clear(); mGridLevelText.addAll(list); } //初始化每個區間的顏色 public void initGridColorLevel(ArrayList list) { mGridColorLevel.clear(); mGridColorLevel.addAll(list); } //初始化每個區間的提示文字顏色 public void initGridTxtColorLevel(ArrayList list) { mGridTxtColorLevel.clear(); mGridTxtColorLevel.addAll(list); } //初始化XY軸title public void initTitleXY(String x, String y) { mTitleX = x; mTitleY = y; }}
再來看下布局檔案:
再看看主介面:
public class MainActivity extends AppCompatActivity { private AreaChartsView mAreaChartsView; private Timer timer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAreaChartsView = (AreaChartsView) this.findViewById(R.id.area_charts_view); //初始化自訂圖表的規格和屬性 ArrayList mXLevel = new ArrayList<>(); ArrayList mYLevel = new ArrayList<>(); ArrayList mGridLevelText = new ArrayList<>(); ArrayList mGridColorLevel = new ArrayList<>(); ArrayList mGridTxtColorLevel = new ArrayList<>(); //初始化x軸座標區間 mXLevel.add(0); mXLevel.add(60); mXLevel.add(90); mXLevel.add(100); mXLevel.add(110); mXLevel.add(120); //初始化y軸座標區間 mYLevel.add(0); mYLevel.add(90); mYLevel.add(140); mYLevel.add(160); mYLevel.add(180); mYLevel.add(200); //初始化區間顏色 mGridColorLevel.add(Color.parseColor(#1FB0E7)); mGridColorLevel.add(Color.parseColor(#4FC7F4)); mGridColorLevel.add(Color.parseColor(#4FDDF2)); mGridColorLevel.add(Color.parseColor(#90E9F4)); mGridColorLevel.add(Color.parseColor(#B2F6F1)); //初始化區間文字提示顏色 mGridTxtColorLevel.add(Color.parseColor(#EA8868)); mGridTxtColorLevel.add(Color.parseColor(#EA8868)); mGridTxtColorLevel.add(Color.parseColor(#EA8868)); mGridTxtColorLevel.add(Color.WHITE); mGridTxtColorLevel.add(Color.BLACK); //初始化區間文字 mGridLevelText.add(異常); mGridLevelText.add(過高); mGridLevelText.add(偏高); mGridLevelText.add(正常); mGridLevelText.add(偏低); mAreaChartsView.initGridColorLevel(mGridColorLevel); mAreaChartsView.initGridLevelText(mGridLevelText); mAreaChartsView.initGridTxtColorLevel(mGridTxtColorLevel); mAreaChartsView.initXLevelOffset(mXLevel); mAreaChartsView.initYLevelOffset(mYLevel); mAreaChartsView.initTitleXY(投入量(H), 產出量(H)); } @Override protected void onStart() { super.onStart(); timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Random random = new Random(); int x = random.nextInt(120) % (120 + 1) + 0; Random randomy = new Random(); int y = randomy.nextInt(200) % (200 + 1) + 0; //隨機類比賦值 mAreaChartsView.updateValues(x, y); } }, 0, 1000); } @Override protected void onPause() { super.onPause(); timer.cancel(); }}
3 總結上面代碼很簡單,核心的都已經注釋了,不需要過多解釋。核心思路就是一些座標點的計算。該控制項支援設定mergin及width與hight等屬性,支援自訂所有顏色及顯示及座標區分等,唯一缺陷就是沒來得及寫attr屬性xml設定這些值,有興趣的自己實現吧,我是沒時間了。
可以發現,自訂View無非就是重寫前面文章分析的那三個方法而已。