Android自定義控件(實(shí)現(xiàn)狀態(tài)提示圖表)
前面分析那么多系統(tǒng)源碼了,也該暫停下來休息一下,趁昨晚閑著看見一個(gè)有意思的需求就操練一下分析源碼后的實(shí)例演練—-自定義控件。
這個(gè)實(shí)例很適合新手入門自定義控件。先看下效果圖:
橫屏模式如下:
豎屏模式如下:
看見沒有,這個(gè)控件完全自定義的,連文字等都是自定義的,沒有任何圖片等資源,就僅僅是一個(gè)小的Java文件,這個(gè)界面只有一個(gè)控件。如下咱們看下實(shí)現(xiàn)代碼。
實(shí)例代碼
如下就是整個(gè)工程的源碼了。
自定義上面展示的控件AreaChartsView源碼:
/** * Author : yanbo * Date : 2015-06-03 * Time : 09:22 * Description : 自定義區(qū)域描述圖表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() { //初始化坐標(biāo)圖的xy交點(diǎn)原點(diǎn)坐標(biāo) mZeroPos[0] = mXYTitleTextSize * 2; mZeroPos[1] = mHight - mXYTitleTextSize * 4; //初始化坐標(biāo)圖的X軸最大值坐標(biāo) mMaxXPos[0] = mWidth; mMaxXPos[1] = mHight - mXYTitleTextSize * 4; //初始化坐標(biāo)圖的Y軸最大值坐標(biāo) 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軸頂?shù)膖itle canvas.drawText(mTitleY, mMaxYPos[0] - mXYTitleTextSize * 2, mMaxYPos[1] - mXYTitleTextSize, mPaint); mPaint.setTextAlign(Paint.Align.RIGHT); //畫X軸頂?shù)膖itle 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; //計(jì)算出偏移title等顯示尺標(biāo)后的真實(shí)XY軸長(zhǎng)度,便于接下來等分 mRealWidth = (mWidth - mXYTitleTextSize * 2); mRealHight = (mHight - mXYTitleTextSize * 4); //算出等分間距 int offsetX = mRealWidth/(mGridLevel); int offsetY = mRealHight/(mGridLevel+1); //循環(huán)繪制content for (int index=0; index<mGridLevel+1; index++) { mPaint.setColor(Color.DKGRAY); mPaint.setTextAlign(Paint.Align.RIGHT); mPaint.setTextSize(mXYTitleTextSize-5); //繪制X軸的那些坐標(biāo)區(qū)間點(diǎn),包含0點(diǎn)坐標(biāo) canvas.drawText(String.valueOf(mXLevel.get(index)), mZeroPos[0]+(index*offsetX), mZeroPos[1] + mXYTitleTextSize, mPaint); if (index != 0) { //繪制Y軸坐標(biāo)區(qū)間點(diǎn),不包含0點(diǎn)坐標(biāo),X軸已經(jīng)畫過了 canvas.drawText(String.valueOf(mYLevel.get(index)), mZeroPos[0], mZeroPos[1]-(index*offsetY), mPaint); } if (index == mGridLevel) { //坐標(biāo)區(qū)間 = 真實(shí)區(qū)間 + 1 break; } mPaint.setColor(mGridColorLevel.get(mGridLevel - 1 - index)); mPaint.setStyle(Paint.Style.FILL); //繪制區(qū)間疊加圖譜方塊,從遠(yuǎn)到0坐標(biāo),因?yàn)樾〉膱D會(huì)覆蓋大的圖 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); //繪制每個(gè)方塊狀態(tài)區(qū)間的提示文字 canvas.drawText(mGridLevelText.get(index), mMaxXPos[0] - index * offsetX - mXYTitleTextSize, mMaxYPos[1] + index * offsetY + mXYTitleTextSize, mPaint); } //繪制當(dāng)前坐標(biāo) drawNotice(canvas, offsetX, offsetY); } private void drawNotice(Canvas canvas, int offsetX, int offsetY) { int realPosX = 0; int realPosY = 0; //計(jì)算傳入的x值與真實(shí)屏幕坐標(biāo)的像素值的百分比差值轉(zhuǎn)換 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; } } //計(jì)算傳入的y值與真實(shí)屏幕坐標(biāo)的像素值的百分比差值轉(zhuǎn)換 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; } } //畫我們傳入的坐標(biāo)點(diǎn)的標(biāo)記小紅點(diǎn) 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(); //畫紅點(diǎn)旁邊的提示框和文字,有四個(gè)區(qū)域,然后提示框的小三角指標(biāo)方位不同 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); } //設(shè)置當(dāng)前比值 public void updateValues(int x, int y) { mMeasureXpos = x; mMeasureYpos = y; postInvalidate(); } //設(shè)置XY軸頂角的title字體大小 public void setTitleTextSize(int size) { mXYTitleTextSize = size; } //初始化X軸的坐標(biāo)區(qū)間點(diǎn)值,可以不均等分 public void initXLevelOffset(ArrayList<Integer> list) { mXLevel.clear(); mXLevel.addAll(list); } //初始化Y軸的坐標(biāo)區(qū)間點(diǎn)值,可以不均等分 public void initYLevelOffset(ArrayList<Integer> list) { mYLevel.clear(); mYLevel.addAll(list); } //初始化每個(gè)區(qū)間的提示文字,如果不想顯示可以設(shè)置"" public void initGridLevelText(ArrayList<String> list) { mGridLevelText.clear(); mGridLevelText.addAll(list); } //初始化每個(gè)區(qū)間的顏色 public void initGridColorLevel(ArrayList<Integer> list) { mGridColorLevel.clear(); mGridColorLevel.addAll(list); } //初始化每個(gè)區(qū)間的提示文字顏色 public void initGridTxtColorLevel(ArrayList<Integer> list) { mGridTxtColorLevel.clear(); mGridTxtColorLevel.addAll(list); } //初始化XY軸title public void initTitleXY(String x, String y) { mTitleX = x; mTitleY = y; } }
再來看下布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <com.yanbober.customerviewdemo.areachartsview.AreaChartsView android:id="@+id/area_charts_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp"/> </RelativeLayout>
再看看主界面:
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); //初始化自定義圖表的規(guī)格和屬性 ArrayList<Integer> mXLevel = new ArrayList<>(); ArrayList<Integer> mYLevel = new ArrayList<>(); ArrayList<String> mGridLevelText = new ArrayList<>(); ArrayList<Integer> mGridColorLevel = new ArrayList<>(); ArrayList<Integer> mGridTxtColorLevel = new ArrayList<>(); //初始化x軸坐標(biāo)區(qū)間 mXLevel.add(0); mXLevel.add(60); mXLevel.add(90); mXLevel.add(100); mXLevel.add(110); mXLevel.add(120); //初始化y軸坐標(biāo)區(qū)間 mYLevel.add(0); mYLevel.add(90); mYLevel.add(140); mYLevel.add(160); mYLevel.add(180); mYLevel.add(200); //初始化區(qū)間顏色 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")); //初始化區(qū)間文字提示顏色 mGridTxtColorLevel.add(Color.parseColor("#EA8868")); mGridTxtColorLevel.add(Color.parseColor("#EA8868")); mGridTxtColorLevel.add(Color.parseColor("#EA8868")); mGridTxtColorLevel.add(Color.WHITE); mGridTxtColorLevel.add(Color.BLACK); //初始化區(qū)間文字 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)", "產(chǎn)出量(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; //隨機(jī)模擬賦值 mAreaChartsView.updateValues(x, y); } }, 0, 1000); } @Override protected void onPause() { super.onPause(); timer.cancel(); } }
總結(jié)
上面代碼很簡(jiǎn)單,核心的都已經(jīng)注釋了,不需要過多解釋。核心思路就是一些坐標(biāo)點(diǎn)的計(jì)算。該控件支持設(shè)置mergin及width與hight等屬性,支持自定義所有顏色及顯示及坐標(biāo)區(qū)分等,唯一缺陷就是沒來得及寫attr屬性xml設(shè)置這些值,有興趣的自己實(shí)現(xiàn)吧,我是沒時(shí)間了。
可以發(fā)現(xiàn),自定義View無非就是重寫前面文章分析的那三個(gè)方法而已。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android基礎(chǔ)開發(fā)之手勢(shì)識(shí)別
這篇文章主要為大家詳細(xì)介紹了Android基礎(chǔ)開發(fā)之手勢(shì)識(shí)別的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-06-06Android實(shí)現(xiàn)EditText添加下劃線
這篇文章主要為大家詳細(xì)介紹了Android如何實(shí)現(xiàn)給EditText添加下劃線,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08Android 邊播邊緩存的實(shí)現(xiàn)(MP4 未加密m3u8)
這篇文章主要介紹了Android 邊播邊緩存的實(shí)現(xiàn)(MP4 未加密m3u8),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Android音樂播放器制作 掃描本地音樂顯示在手機(jī)(一)
這篇文章主要介紹了Android音樂播放器的制作方法,掃描本地音樂顯示在手機(jī)上,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Android錄音功能的實(shí)現(xiàn)以及踩坑實(shí)戰(zhàn)記錄
在Android 開發(fā)過程中,有些功能是通用的,或者是多個(gè)業(yè)務(wù)方都需要使用的,下面這篇文章主要給大家介紹了關(guān)于Android錄音功能的實(shí)現(xiàn)以及踩坑的相關(guān)資料,需要的朋友可以參考下2022-06-06Android實(shí)現(xiàn)圖片自動(dòng)輪播并且支持手勢(shì)左右無限滑動(dòng)
這篇文章給大家介紹android實(shí)現(xiàn)圖片自動(dòng)輪播并且支持手勢(shì)左右無限滑動(dòng),代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-10-10android創(chuàng)建optionsmenu的方法
這篇文章主要介紹了android創(chuàng)建optionsmenu的方法,實(shí)例分析了Android菜單項(xiàng)的設(shè)置與創(chuàng)建技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07