Android?RecyclerLineChart實(shí)現(xiàn)圖表繪制教程
正文
本篇介紹線性圖標(biāo)RecyclerLineChart 的繪制,對(duì)于圖表的公共部分X、Y軸,背景Board等的繪制先前章節(jié)已經(jīng)有過(guò)介紹,這里不再重復(fù);以及高亮選中頂部的poupWindow基本的繪制邏輯跟BarChart類似,可參照之前章節(jié)。所以針對(duì)LineChart,這里只介紹主體圖表的繪制邏輯,以及線性表底部的drawFillColor填充。
首先介紹主體圖表的邏輯,與BarChart不同之處在于,BartChart的每個(gè)Item的繪制比較獨(dú)立,而LineChart對(duì)于當(dāng)前的Item,需要找到PreItem或者NextItem中的Y的點(diǎn)進(jìn)行drawLine, 相比而言稍顯復(fù)雜一些。圖表的中間位置的Line還比較容易繪制,圖表左右邊界是LineChart繪制最難的地方。
整個(gè)的繪制邏輯第一章節(jié)有介紹在Render類中,這里的話是LineChartRender的drawLineChartWithoutPoint 方法里,這個(gè)方法代碼比較長(zhǎng),分段介紹:
private <T extends BarEntry> void drawLineChartWithoutPoint(Canvas canvas, RecyclerView parent, YAxis mYAxis) { final float parentRightBoard = parent.getWidth() - parent.getPaddingRight(); final float parentLeft = parent.getPaddingLeft(); BaseBarChartAdapter adapter = (BaseBarChartAdapter) parent.getAdapter(); List<T> entryList = adapter.getEntries(); final int childCount = parent.getChildCount(); int adapterPosition; for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); T barEntry = (T) child.getTag(); if (barEntry.getY() == 0) { continue; } adapterPosition = parent.getChildAdapterPosition(child); RectF rectF = ChartComputeUtil.getBarChartRectF(child, parent, mYAxis, mLineChartAttrs, barEntry); PointF pointF2 = new PointF((rectF.left + rectF.right) / 2, rectF.top); // 這里還有好多繪制邏輯代碼 }//end for }// end drawLineChartWithoutPoint
整個(gè)繪制繪制依次遍歷 Adapter中當(dāng)前展現(xiàn)的點(diǎn),總共childcount 個(gè),遍歷的當(dāng)前點(diǎn)位pointF2, 以pointF2 為基準(zhǔn)接下來(lái)會(huì)涉及找 pointF0,pointF1, 這兩在pointF2左邊(假設(shè)存在的情況下);pointF3, pointF4, 這兩個(gè)點(diǎn)在PointFd2的右邊,之所以要找左右各兩個(gè)點(diǎn)是處理邊界情況。
正常情況下繪制邏輯
連接pointF1、pointF2。
if (i < childCount - 1) {//這里的LayoutManager設(shè)置的reverse倒敘,所以i+1在i的左邊i對(duì)應(yīng)的是pointF2 View pointF1Child = parent.getChildAt(i + 1); T barEntryLeft = (T) pointF1Child.getTag(); //這里的RectF跟之前的Barchart類似,為ItemView中除去space后所占的RectF區(qū)域,其中pointF1的X為RectF的X軸方向的中心。 RectF rectFLeft = ChartComputeUtil.getBarChartRectF(pointF1Child, parent, mYAxis, mLineChartAttrs, barEntryLeft); //找到PointF1 PointF pointF1 = new PointF((rectFLeft.left + rectFLeft.right) / 2, rectFLeft.top); //parentLeft為左邊界, parentRightBoard 為Chart的右邊界 if (pointF1.x >= parentLeft && pointF2.x <= parentRightBoard) { float[] pointsOut = new float[]{pointF1.x, pointF1.y, pointF2.x, pointF2.y}; drawChartLine(canvas, pointsOut);//繪制正常情況下的Line。 drawFill(parent, mLineChartAttrs, canvas, pointF1, pointF2, rectF.bottom); //其它邊界繪制邏輯。 }//end if
左邊界繪制
以上的情況是pointF1.x 在Chart內(nèi),見(jiàn)圖, 黃色為當(dāng)前的PointF1, 紫色為PointF2, 上面代碼李drawLine繪制的是PointF1跟PointF2之前的連線。
繼續(xù)看上面的那個(gè)圖,要繪制PointF1到Chart左邊邊界的線段,需要繼續(xù)找到PointF0,然后用PointF0、PointF1與Chart相交得到上圖黑色圈里的點(diǎn),記為pointFIntercept, drawLine(pointFIntercept, PointF1)
if (pointF1Child.getLeft() < parentLeft) {//左邊界,處理pointF1值顯示出來(lái)了的情況。 if (adapterPosition + 2 < entryList.size()) { float x = pointF1.x - pointF1Child.getWidth(); T barEntry0 = entryList.get(adapterPosition + 2); float y = ChartComputeUtil.getYPosition(barEntry0, parent, mYAxis, mLineChartAttrs); PointF pointF0 = new PointF(x, y); //PointF0、PointF1 跟Chart的交點(diǎn)pointFIntercept PointF pointFIntercept = ChartComputeUtil.getInterceptPointF(pointF0, pointF1, parentLeft); float[] points = new float[]{pointFIntercept.x, pointFIntercept.y, pointF1.x, pointF1.y}; drawChartLine(canvas, points); drawFill(parent, mLineChartAttrs, canvas, pointFIntercept, pointF1, rectF.bottom); } }
上面是 pointF1.x >= parentLeft,在左邊界內(nèi)的情況,當(dāng)pointF1.x < parentLeft時(shí),rectLeft 出來(lái)一小部分的情況,如下圖所示:紫色為當(dāng)前的PointF2點(diǎn)
這時(shí)需要求PointF1、PointF2跟Chart相交的點(diǎn)pointF, 然后drawLine(pointF, PointF2)即可, 見(jiàn)代碼:
if (pointF1.x < parentLeft && pointF1Child.getRight() >= parentLeft) {//左邊界,處理pointF1值沒(méi)有顯示出來(lái) PointF pointF = ChartComputeUtil.getInterceptPointF(pointF1, pointF2, parentLeft); float[] points = new float[]{pointF.x, pointF.y, pointF2.x, pointF2.y}; drawChartLine(canvas, points); drawFill(parent, mLineChartAttrs, canvas, pointF, pointF2, rectF.bottom); }
右邊界繪制
處理完左邊界的繪制,右邊界的繪制跟左邊界大致一樣,PointF2 往右兩個(gè)點(diǎn)PointF3, PointF4; 注意這里RecyclerView的LayoutManager為reverse, 所以當(dāng) PointF2對(duì)應(yīng)的下標(biāo)為i時(shí), PointF3對(duì)應(yīng)的為i-1, PointF4為i-2.
然后就是分情況討論P(yáng)ointF3.x 是否在Chart范圍內(nèi),跟parentRightBorad比較即可。
看PointF3.x 在 Chart范圍內(nèi)的情況,如圖:紫色為PointF2點(diǎn),黃色為PonitF3點(diǎn),黑色為PointF3,PointF4跟Chart的交點(diǎn),這里只需要繪制PointF3跟交點(diǎn)之間的Line;PointF2、PointF3之間的Line 在當(dāng)黃色點(diǎn)遍歷到i時(shí),紫色點(diǎn)位PointF1,所以這里不需要重復(fù)繪制了。
代碼邏輯
if (pointF3.x < parentRightBoard) {//pointF3 在界內(nèi)。 if (adapterPosition - 2 > 0) { float xInner = pointF3.x + child.getWidth(); T barEntry4 = entryList.get(adapterPosition - 2); float yInner = ChartComputeUtil.getYPosition(barEntry4, parent, mYAxis, mLineChartAttrs); PointF pointF4 = new PointF(xInner, yInner);//找到PointF4. PointF pointFInterceptInner = ChartComputeUtil.getInterceptPointF(pointF3, pointF4, parentRightBoard); float[] pointsInner = new float[]{pointF3.x, pointF3.y, pointFInterceptInner.x, pointFInterceptInner.y}; drawChartLine(canvas, pointsInner); drawFill(parent, mLineChartAttrs, canvas, pointF3, pointFInterceptInner, rectF.bottom); } }
最后就是 pointF3.x >parentRightBoard的情況,見(jiàn)圖:紫色為PointF2, 黃色為 PointF2、PointF3跟Chart的交點(diǎn):
代碼邏輯如下:
if (pointF3.x > parentRightBoard) {//在Chart之外。 PointF pointFIntercept = ChartComputeUtil.getInterceptPointF(pointF2, pointF3, parentRightBoard); float[] points = new float[]{pointF2.x, pointF2.y, pointFIntercept.x, pointFIntercept.y}; drawChartLine(canvas, points); drawFill(parent, mLineChartAttrs, canvas, pointFIntercept, pointF2, rectF.bottom); }
以上的邊界處理中涉及到的工具類方法求相交點(diǎn),簡(jiǎn)單的數(shù)學(xué)公司帶入:
public static PointF getInterceptPointF(PointF pointF1, PointF pointF2, float x) { float width = Math.abs(pointF1.x - pointF2.x); float height = Math.abs(pointF1.y - pointF2.y); float interceptWidth = Math.abs(pointF1.x - x); float interceptHeight = interceptWidth * 1.0f / width * height; float y; if (pointF2.y < pointF1.y) { y = pointF1.y - interceptHeight; } else { y = pointF1.y + interceptHeight; } return new PointF(x, y); }
見(jiàn)以上圖表中的紅色半透明的FillColor的繪制,每次drawLine()緊跟著就是drawFill(), 以下是drawFill的邏輯,跟X軸構(gòu)建一個(gè)path,然后drawPath 即可:
private void drawFill(RecyclerView parent, LineChartAttrs mBarChartAttrs, Canvas canvas, PointF pointF, PointF pointF1, float bottom) { if (mBarChartAttrs.enableLineFill) { float yBottom = parent.getBottom() - parent.getPaddingBottom(); float yTop = parent.getTop() + parent.getPaddingTop(); LinearGradient mLinearGradient = new LinearGradient( 0, yBottom, 0, yTop, new int[]{ mBarChartAttrs.lineShaderBeginColor, mBarChartAttrs.lineShaderEndColor}, null, Shader.TileMode.CLAMP ); mLineFillPaint.setShader(mLinearGradient); Path path = ChartComputeUtil.createColorRectPath(pointF, pointF1, bottom); LineChartDrawable drawable = new LineChartDrawable(mLineFillPaint, path); drawable.draw(canvas); } }
設(shè)置了一個(gè)Color的Linear漸變從bottom到top.
至此,RecyclerLineChart的主體圖表繪制邏輯介紹完畢。還有部分的細(xì)節(jié),當(dāng)前Point帶圓圈,以及邊界圓圈的繪制等,選中圓圈的處理等多處細(xì)節(jié),讀者想了解的,可以GitHub上下載看源碼demo, 連接在本專欄的第一篇里有鏈接。
以上就是Android RecyclerLineChart實(shí)現(xiàn)圖表繪制教程的詳細(xì)內(nèi)容,更多關(guān)于Android RecyclerLineChart圖表繪制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android AIDL和遠(yuǎn)程Service調(diào)用示例代碼
本文主要介紹Android AIDL和遠(yuǎn)程Service,這里詳細(xì)介紹了相關(guān)知識(shí),并附實(shí)例代碼和實(shí)現(xiàn)效果圖,有興趣的朋友參考下2016-08-08Android ScrollView 下嵌套 ListView 或 GridView出現(xiàn)問(wèn)題解決辦法
這篇文章主要介紹了ScrollView 下嵌套 ListView 或 GridView 會(huì)發(fā)列表現(xiàn)數(shù)據(jù)只能顯示一行。因?yàn)樗麄兌际菨L動(dòng)結(jié)構(gòu),兩個(gè)滾動(dòng)條放到一起就會(huì)引起沖突,這里提供解決辦法相關(guān)資料,需要的朋友可以參考下2017-07-07Android使用GridView實(shí)現(xiàn)日歷的方法
本篇文章主要介紹了Android使用GridView實(shí)現(xiàn)日歷的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08Android彈出DatePickerDialog并獲取值的方法
這篇文章主要為大家詳細(xì)介紹了Android彈出DatePickerDialog并獲取值的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05Android開(kāi)發(fā)之自帶下載器DownloadManager的使用示例代碼
本篇文章主要介紹了Android開(kāi)發(fā)之自帶下載器DownloadManager的使用示例代碼,Android自帶的DownloadManager是一個(gè)很好的下載文件的工具,有興趣的可以了解一下。2017-03-03Android利用startActivityForResult返回?cái)?shù)據(jù)到前一個(gè)Activity
這篇文章主要介紹了Android利用startActivityForResult返回?cái)?shù)據(jù)到前一個(gè)Activity,幫助大家更好的利用Android進(jìn)行開(kāi)發(fā),感興趣的朋友可以了解下2021-01-01Android實(shí)現(xiàn)邊錄邊播應(yīng)用
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)邊錄邊播應(yīng)用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11