Android自定義控件實(shí)現(xiàn)折線圖
本文實(shí)例實(shí)現(xiàn)一個(gè)如下圖所示的Android折線圖,供大家參考,具體內(nèi)容如下
首先是控件繪圖區(qū)域的劃分,控件左邊取一小部分(控件總寬度的八分之一)繪制表頭,右邊剩余的部分繪制表格
確定表格的行列數(shù),首先繪制一個(gè)三行八列的網(wǎng)格,設(shè)置好行列的坐標(biāo)后開(kāi)始繪制
/*繪制三條橫線*/ for(int i=0;i<3;i++){ canvas.drawLine(textWide, mLineYs[i], totalWidth, mLineYs[i], mPaintLine); } /*繪制八條豎線*/ for(int i=0;i<8;i++){ canvas.drawLine(mLineXs[i], 0, mLineXs[i], totalHeight, mPaintLine); }
網(wǎng)格繪制完成后,開(kāi)始繪制折線圖
根據(jù)輸入的節(jié)點(diǎn)數(shù)據(jù),分別繪制兩條折線
通過(guò)canvas的drawLine方法依次連接兩點(diǎn)即可
在每個(gè)數(shù)據(jù)節(jié)點(diǎn)處繪制一個(gè)小圓,突出顯示
/*繪制第一條折線的路徑*/ for (int i = 0; i < mPerformance_1.length - 1; i++) { /*折線圖的折線的畫(huà)筆設(shè)置粗一點(diǎn)*/ mPaintLine.setStrokeWidth(5); /*計(jì)算當(dāng)前節(jié)點(diǎn)的坐標(biāo)值*/ float prePointX =mLineXs[i]; float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i].type]) * animCurrentValue; /*計(jì)算下一個(gè)節(jié)點(diǎn)的坐標(biāo)值*/ float nextPointX=mLineXs[i + 1]; float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i + 1].type]) * animCurrentValue; /*連接當(dāng)前坐標(biāo)和下一個(gè)坐標(biāo),繪制線段*/ canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine1); /*當(dāng)前節(jié)點(diǎn)坐標(biāo)處繪制小圓*/ canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint); }
兩條折線重合的地方,需要特殊考慮,比如希望兩條折線重合的地方折線變?yōu)榘咨?/p>
設(shè)置下兩條折線的畫(huà)筆即可
mPaintLine2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); mPaintLine1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
測(cè)試代碼及效果;
final Random random=new Random(); final LineChartView myView=(LineChartView)findViewById(R.id.custom_view); final LineChartView.Performance[] performances1=new LineChartView.Performance[8]; final LineChartView.Performance[] performances2=new LineChartView.Performance[8]; myView.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ for(int i=0;i<performances1.length;i++){ switch (random.nextInt(2016)%3){ case 0: performances1[i]= LineChartView.Performance.WIN; break; case 1: performances1[i]= LineChartView.Performance.DRAW; break; case 2: performances1[i]= LineChartView.Performance.LOSE; break; default: performances1[i]= LineChartView.Performance.LOSE; break; } switch (random.nextInt(2016)%3){ case 0: performances2[i]= LineChartView.Performance.WIN; break; case 1: performances2[i]= LineChartView.Performance.DRAW; break; case 2: performances2[i]= LineChartView.Performance.LOSE; break; default: performances1[i]= LineChartView.Performance.LOSE; break; } } myView.setPerformances(performances1,performances2); } });
完整代碼如下:
public class LineChartView extends View { private Context context; /*動(dòng)畫(huà)插值器*/ DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); /*動(dòng)畫(huà)刷新的次數(shù)*/ private int mDuration = 10; /*當(dāng)前動(dòng)畫(huà)進(jìn)度值*/ private int mCurrentTime = 0; private Performance[] mPerformance_1, mPerformance_2; /*兩條折線的顏色*/ private int mLineColor1, mLineColor2; /*繪制表頭文字畫(huà)筆*/ private Paint mPaintText = new Paint(); /*繪制表格的畫(huà)筆*/ private Paint mPaintLine = new Paint(); /*第一條折線的畫(huà)筆*/ private Paint mPaintLine1 =new Paint(); /*第二條折線的畫(huà)筆*/ private Paint mPaintLine2 =new Paint(); /*坐標(biāo)點(diǎn)的小圓點(diǎn)畫(huà)筆*/ private Paint mPointPaint = new Paint(); private float mSmallDotRadius = 4; private TypedValue typedValue; private int mPaintClolor; /*Handler刷新界面產(chǎn)生動(dòng)畫(huà)效果*/ private Handler mHandler = new Handler(); private Runnable mAnimation = new Runnable() { @Override public void run() { if (mCurrentTime < mDuration) { mCurrentTime++; LineChartView.this.invalidate(); } } }; public LineChartView(Context context) { super(context); this.context=context; init(); } public LineChartView(Context context, AttributeSet attrs) { super(context, attrs); this.context=context; init(); } public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context=context; init(); } public enum Performance { WIN(0), DRAW(1), LOSE(2); public int type; Performance(int type) { this.type = type; } } public void setPerformances(Performance[] performance1, Performance[] performance2) { if (performance1 == null) { performance1 = new Performance[0]; } if (performance2 == null) { performance2 = new Performance[0]; } mPerformance_1 = Arrays.copyOf(performance1, performance1.length > 8 ? 8 : performance1.length); mPerformance_2 = Arrays.copyOf(performance2, performance2.length > 8 ? 8 : performance2.length); if (isShown()) { mCurrentTime = 0; this.invalidate(); } } /** * 設(shè)置折線1的顏色 * * @param mLineColor1 */ public void setLineColor1(int mLineColor1) { this.mLineColor1 = mLineColor1; } /** * 設(shè)置折線2的顏色 * * @param mLineColor2 */ public void setLineColor2(int mLineColor2) { this.mLineColor2 = mLineColor2; } private void init() { mLineColor1=Color.BLUE; mLineColor2 = Color.GREEN; typedValue=new TypedValue(); context.getTheme().resolveAttribute(R.attr.title_bar,typedValue,true); mPaintClolor =getResources().getColor(typedValue.resourceId); final LineChartView.Performance[] performances1=new LineChartView.Performance[8]; final LineChartView.Performance[] performances2=new LineChartView.Performance[8]; final Random random=new Random(); for(int i=0;i<performances1.length;i++){ switch (random.nextInt(2016)%3){ case 0: performances1[i]= LineChartView.Performance.WIN; break; case 1: performances1[i]= LineChartView.Performance.DRAW; break; case 2: performances1[i]= LineChartView.Performance.LOSE; break; default: performances1[i]= LineChartView.Performance.LOSE; break; } switch (random.nextInt(2016)%3){ case 0: performances2[i]= LineChartView.Performance.WIN; break; case 1: performances2[i]= LineChartView.Performance.DRAW; break; case 2: performances2[i]= LineChartView.Performance.LOSE; break; default: performances1[i]= LineChartView.Performance.LOSE; break; } } setPerformances(performances1,performances2); } /** * @param canvas */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /*獲取控件總寬高*/ float totalWidth = getWidth(); float totalHeight = getHeight(); /*左邊取總寬度的八分之一繪制表格頭部*/ float textWide = totalWidth / 8; /*左邊留一點(diǎn)空白*/ float left_offset = 10; /*折線圖的總寬度等于控件的總寬度減去表頭和留白*/ float chartWide = totalWidth - textWide - left_offset; /*一共三行,設(shè)置每一行的垂直坐標(biāo)*/ float[] mLineYs = new float[]{totalHeight / 8, totalHeight / 2, totalHeight * 7 / 8}; /*一共八列,設(shè)置每一列的水平坐標(biāo)*/ float[] mLineXs = new float[]{ textWide + left_offset + chartWide * 0 / 8, textWide + left_offset + chartWide * 1 / 8, textWide + left_offset + chartWide * 2 / 8, textWide + left_offset + chartWide * 3 / 8, textWide + left_offset + chartWide * 4 / 8, textWide + left_offset + chartWide * 5 / 8, textWide + left_offset + chartWide * 6 / 8, textWide + left_offset + chartWide * 7 / 8, }; /*繪制表頭文字*/ mPaintText.setStyle(Paint.Style.FILL); mPaintText.setColor(mPaintClolor); mPaintText.setAlpha(226); mPaintText.setTextSize(28); /*從中間開(kāi)始繪制*/ mPaintText.setTextAlign(Paint.Align.CENTER); /*測(cè)量文字大小,并計(jì)算偏移量*/ Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics(); float textBaseLineOffset = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; canvas.drawText("勝場(chǎng)", textWide / 2, mLineYs[0] + textBaseLineOffset, mPaintText); canvas.drawText("平局", textWide / 2, mLineYs[1] + textBaseLineOffset, mPaintText); canvas.drawText("負(fù)場(chǎng)", textWide / 2, mLineYs[2] + textBaseLineOffset, mPaintText); /*繪制表格畫(huà)筆設(shè)置*/ mPaintLine.setStyle(Paint.Style.STROKE); mPaintLine.setAntiAlias(true); mPaintLine.setColor(mPaintClolor); mPaintLine.setAlpha(80); mPaintLine.setStrokeWidth(1); /*開(kāi)始繪制表格*/ /*繪制三條橫線*/ for(int i=0;i<3;i++){ canvas.drawLine(textWide, mLineYs[i], totalWidth, mLineYs[i], mPaintLine); } /*繪制八條豎線*/ for(int i=0;i<8;i++){ canvas.drawLine(mLineXs[i], 0, mLineXs[i], totalHeight, mPaintLine); } /*折線圖畫(huà)筆設(shè)置*/ mPaintLine1.setStyle(Paint.Style.STROKE); /*設(shè)置透明度,取值范圍為0~255,數(shù)值越小越透明,0表示完全透明*/ mPaintLine1.setAlpha(0); mPaintLine1.setAntiAlias(true); mPaintLine1.setColor(mLineColor1); mPaintLine1.setStrokeWidth(5); mPaintLine2.setStyle(Paint.Style.STROKE); /*設(shè)置透明度,取值范圍為0~255,數(shù)值越小越透明,0表示完全透明*/ mPaintLine2.setAlpha(0); mPaintLine2.setAntiAlias(true); mPaintLine2.setColor(mLineColor2); mPaintLine2.setStrokeWidth(5); if (typedValue.resourceId==R.color.white){ /*PorterDuff.Mode.SCREEN 上下層都顯示。*/ mPaintLine2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); mPaintLine1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); }else { /*PorterDuff.Mode.DARKEN 上下層都顯示。變暗*/ mPaintLine2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); mPaintLine1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); } /*畫(huà)節(jié)點(diǎn)處的小圓點(diǎn)的畫(huà)筆設(shè)置*/ mPointPaint.setStyle(Paint.Style.STROKE); mPointPaint.setAntiAlias(true); mPointPaint.setColor(mPaintClolor); /*計(jì)算當(dāng)前動(dòng)畫(huà)進(jìn)度對(duì)應(yīng)的數(shù)值*/ float animCurrentValue = mDecelerateInterpolator.getInterpolation(1.0f * mCurrentTime / mDuration); mPaintLine.setColor(mLineColor1); /*繪制第一條折線的路徑*/ for (int i = 0; i < mPerformance_1.length - 1; i++) { /*折線圖的折線的畫(huà)筆設(shè)置粗一點(diǎn)*/ mPaintLine.setStrokeWidth(5); /*計(jì)算當(dāng)前節(jié)點(diǎn)的坐標(biāo)值*/ float prePointX =mLineXs[i]; float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i].type]) * animCurrentValue; /*計(jì)算下一個(gè)節(jié)點(diǎn)的坐標(biāo)值*/ float nextPointX=mLineXs[i + 1]; float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i + 1].type]) * animCurrentValue; /*連接當(dāng)前坐標(biāo)和下一個(gè)坐標(biāo),繪制線段*/ canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine1); /*當(dāng)前節(jié)點(diǎn)坐標(biāo)處繪制小圓*/ canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint); } /*第一個(gè)折線圖的最后一個(gè)節(jié)點(diǎn)的坐標(biāo)*/ float lastPointX=mLineXs[mPerformance_1.length - 1]; float lastPointY= mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[mPerformance_1.length - 1].type]) * animCurrentValue; /*繪制最后一個(gè)節(jié)點(diǎn)的外圍小圓*/ canvas.drawCircle(lastPointX,lastPointY ,mSmallDotRadius, mPointPaint); /*繪制第二條折線*/ mPaintLine.setColor(mLineColor2); for (int i = 0; i < mPerformance_2.length - 1; i++) { /*折線圖的折線的畫(huà)筆設(shè)置粗一點(diǎn)*/ mPaintLine.setStrokeWidth(5); /*計(jì)算當(dāng)前節(jié)點(diǎn)的坐標(biāo)值*/ float prePointX =mLineXs[i]; float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[i].type]) * animCurrentValue; /*計(jì)算下一個(gè)節(jié)點(diǎn)的坐標(biāo)值*/ float nextPointX=mLineXs[i + 1]; float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[i + 1].type]) * animCurrentValue; /*連接當(dāng)前坐標(biāo)和下一個(gè)坐標(biāo),繪制線段*/ canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine2); /*當(dāng)前節(jié)點(diǎn)坐標(biāo)處繪制小圓*/ canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint); } /*第一個(gè)折線圖的最后一個(gè)節(jié)點(diǎn)的坐標(biāo)*/ lastPointX=mLineXs[mPerformance_2.length - 1]; lastPointY= mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[mPerformance_2.length - 1].type]) * animCurrentValue; /*繪制最后一個(gè)節(jié)點(diǎn)的外圍小圓*/ canvas.drawCircle(lastPointX,lastPointY ,mSmallDotRadius, mPointPaint); mHandler.postDelayed(mAnimation, 20); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解Android圖表 MPAndroidChart折線圖
- MPAndroidChart開(kāi)源圖表庫(kù)的使用介紹之餅狀圖、折線圖和柱狀圖
- Android MPAndroidChart開(kāi)源庫(kù)圖表之折線圖的實(shí)例代碼
- Android自定義View實(shí)現(xiàn)折線圖效果
- Android繪制動(dòng)態(tài)折線圖
- Android HelloChart開(kāi)源庫(kù)圖表之折線圖的實(shí)例代碼
- Android開(kāi)發(fā)之天氣趨勢(shì)折線圖
- Android自定義可左右滑動(dòng)和點(diǎn)擊的折線圖
- Android自定義View簡(jiǎn)易折線圖控件(二)
- Android開(kāi)發(fā)RecyclerView實(shí)現(xiàn)折線圖效果
相關(guān)文章
android實(shí)現(xiàn)按鈕獲取焦點(diǎn)延遲加載
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)按鈕獲取焦點(diǎn)延遲加載,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android中AlertDilog顯示簡(jiǎn)單和復(fù)雜列表的方法
這篇文章主要介紹了Android中AlertDialog顯示簡(jiǎn)單和復(fù)雜列表的方法,結(jié)合實(shí)例形式分析了Android的AlertDialog創(chuàng)建列表顯示對(duì)話框的相關(guān)方法與常見(jiàn)操作技巧,需要的朋友可以參考下2016-08-08Android重力傳感器實(shí)現(xiàn)滾動(dòng)的彈球
所謂傳感器能夠探測(cè)如光、熱、溫度、重力、方向 等等的功能,本文給大家介紹Android傳感器應(yīng)用之重力傳感器實(shí)現(xiàn)滾動(dòng)的彈球,對(duì)android重力傳感器相關(guān)知識(shí)感興趣的朋友一起看看吧2015-12-12Android IPC機(jī)制Messenger實(shí)例詳解
這篇文章主要介紹了 Android IPC機(jī)制Messenger實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07Android組件WebView編寫(xiě)有道詞典小案例分享
這篇文章主要為大家分享了Android組件WebView編寫(xiě)有道詞典小案例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05Android學(xué)習(xí)筆記之ActionBar Item用法分析
這篇文章主要介紹了Android學(xué)習(xí)筆記之ActionBar Item用法,結(jié)合實(shí)例形式分析了ActionBar Item的具體功能與相關(guān)使用技巧,需要的朋友可以參考下2017-05-05Android 設(shè)置主題實(shí)現(xiàn)點(diǎn)擊波紋效果的示例
本篇文章主要介紹了Android 設(shè)置主題實(shí)現(xiàn)點(diǎn)擊波紋效果的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11