網站首頁 編程語言 正文
正文
上篇介紹了幾種圖表的公共組件X、Y軸、背景Board的繪制。這章節介紹柱狀圖表的繪制,相對其它圖表而言簡單一些,這里主要介紹圖表主體的繪制,以及高亮選中的其中一個的選中框的繪制的相關邏輯。對每個ItemView中的ItemDecoration上進行onDraw的操作,需要將View跟Model進行綁定在一起,單個柱子的一些屬性可以通過Model來獲取,整體的一些繪制的輔助信息color、size等可以通過Attribute類設置。View 跟Model的綁定不止是BarChart圖表,所以的都是一樣的。
以下是在BarAdapter中的onBindViewHolder方法中進行關聯二者:
根據之前的介紹繪制的邏輯都在ItemDecoration里,我們看下BarChartItemDecoration的onDrawOver, 對于X、Y軸、Board的繪制其實可以沉淀到BaseItemDecoration中的,這里直接寫了。
這里我們著重看下drawBarChart、drawHighLight、drawBarChartValues的繪制。
1.drawBarChart
繪制柱狀圖的主體,通過ItemView拿到對應的Entry對象,根據Entry中的Y值進行Y坐標值的轉化,然后繪制單個Item RectF的繪制。
//繪制柱狀圖, mYAxis這個坐標會實時變動,所以通過 BarChartItemDecoration 傳過來的精確值。
final public void drawBarChart(final Canvas canvas, @NonNull final RecyclerView parent, final YAxis mYAxis) {
final float parentRight = parent.getWidth() - parent.getPaddingRight();
final float parentLeft = parent.getPaddingLeft();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
BarEntry barChart = (BarEntry) child.getTag();
RectF rectF = ChartComputeUtil.getBarChartRectF(child, parent, mYAxis, mBarChartAttrs, barChart);
drawChart(canvas, rectF, parentLeft, parentRight);
}
}
繪制的邏輯具體在 drawChart(canvas, rectF, parentLeft, parentRight) 的方法里,這里我們先看看 RectF 的計算,工具類ChartComputeUtil.getBarChartRectF() 的方法。
public static <T extends BarEntry, V extends BaseYAxis, E extends BaseChartAttrs> RectF getBarChartRectF(View child, final RecyclerView parent, V mYAxis, E chartAttrs, T barEntry) {
final RectF rectF = new RectF();
float contentBottom = parent.getHeight() - parent.getPaddingBottom() - chartAttrs.contentPaddingBottom;
float realYAxisLabelHeight = contentBottom - parent.getPaddingTop() - chartAttrs.contentPaddingTop;
float width = child.getWidth();
float barSpaceWidth = width * chartAttrs.barSpace;
float barChartWidth = width - barSpaceWidth;//柱子的寬度
final float left = child.getLeft() + barSpaceWidth / 2;
final float right = left + barChartWidth;
float height = barEntry.getY() / mYAxis.getAxisMaximum() * realYAxisLabelHeight;
if (chartAttrs.yAxisReverse && barEntry.getY() > 0) {
float valueTemp = mYAxis.getAxisMaximum() - barEntry.getY();
height = valueTemp / mYAxis.getAxisMaximum() * realYAxisLabelHeight;
}
final float top = Math.max(contentBottom - height, parent.getPaddingTop());
rectF.set(left, top, right, contentBottom);
return rectF;
}
柱子RectF 的計算,Width根據 itemView的width 以及每個ItemView的空余所占比例的一個ChartAttrs中的參數
barSpace計算得來,算出RectF的 left、right; height 的計算,涉及到Entry 的Y值以及YAxis 當前顯示情況下的getAxisMaximum(),這里默認了Minmum為0,業務邏輯的Y值比例轉化成 屏幕Pixel對應的高度,然后根據ItemView的top、bottom計算得到 RectF的 top, bottom. (這里的計算,到時候其它圖表高度也可以用)。
獲取到 單個ItemView 中BarChart 所占的RectF確定后,畫RectF就比較簡單了,稍微有點難點的是處理一下邊界的問題,邊界問題,柱狀圖相比線形圖等簡單一些,處于邊界的柱子繪制的顏色不一樣,交給用戶mBarChartAttrs.chartEdgeColor傳color值,這里默認設置的是Gray。
private void drawChart(Canvas canvas, RectF rectF, float parentLeft, float parentRight) {
float radius = (rectF.right - rectF.left) *mBarChartAttrs.barChartRoundRectRadiusRatio;
// 浮點數的 == 比較需要注意
if (DecimalUtil.smallOrEquals(rectF.right, parentLeft)) {
//continue 會閃,原因是end == parentLeft 沒有過濾掉,顯示出來柱狀圖了。
return;
} else if (rectF.left < parentLeft && rectF.right > parentLeft) {
//左邊部分滑入的時候,處理柱狀圖的顯示
rectF.left = parentLeft;
Path path = CanvasUtil.createRectRoundPath(rectF, radius, RoundRectType.TYPE_RIGHT);
mBarChartPaint.setColor(mBarChartAttrs.chartEdgeColor);
canvas.drawPath(path, mBarChartPaint);
} else if (DecimalUtil.bigOrEquals(rectF.left, parentLeft) && DecimalUtil.smallOrEquals(rectF.right, parentRight)) {
//中間的; 浮點數的 == 比較需要注意
mBarChartPaint.setColor(mBarChartAttrs.chartColor);
Path path = CanvasUtil.createRectRoundPath(rectF, radius);
canvas.drawPath(path, mBarChartPaint);
} else if (DecimalUtil.smallOrEquals(rectF.left, parentRight) &&
rectF.right > parentRight) {
//右邊部分滑出的時候,處理柱狀圖,文字的顯示
float distance = (parentRight - rectF.left);
rectF.right = rectF.left + distance;
Path path = CanvasUtil.createRectRoundPath(rectF, radius, RoundRectType.TYPE_LEFT);
mBarChartPaint.setColor(mBarChartAttrs.chartEdgeColor);
canvas.drawPath(path, mBarChartPaint);
}
}
下面是個步數的周視圖:
2.drawHighLight
首先看下這里高亮具體是如何顯示的,直觀的看些圖:
當前的RecyclerView的getChildCount內每個ItemView對應的Entry,設定了一個 selected 的字段來確定顯示高亮,至于該字段的值的設定及變化,后續章節會介紹,這里假定已經確定了當前的某一個ItemView的Entry的selected是選中狀態的,它有可能在中間,或者在邊界需要處理邊界繪制的問題,這里分畫頂部的矩形框及drawTextValue值,底部繪制drawLine(這個不存在繪制的邊界問題)
//繪制選中時 highLight 標線及浮框。
public <E extends BaseYAxis> void drawHighLight(Canvas canvas, @NonNull RecyclerView parent, E yAxis) {
if (mBarChartAttrs.enableValueMark) {
int childCount = parent.getChildCount();
View child;
for (int i = 0; i < childCount; i++) {
child = parent.getChildAt(i);
T entry = (T) child.getTag();
RectF rectF = ChartComputeUtil.getBarChartRectF(child, parent, yAxis, mBarChartAttrs, entry);
float width = child.getWidth();
float childCenter = child.getLeft() + width / 2;
String valueStr = mHighLightValueFormatter.getBarLabel(entry);
if (entry.isSelected() && !TextUtils.isEmpty(valueStr)) {
int chartColor = getChartColor(entry);
float rectHeight = drawHighLightValue(canvas, valueStr, childCenter, parent, chartColor);//繪制頂部的poupWindow,高亮矩形框及drawText
float[] points = new float[]{childCenter, rectF.top, childCenter, rectHeight};
drawHighLightLine(canvas, points, chartColor);//繪制底部的Line
}
}
}
}
以上中 drawHighLightValue 中, 包含了繪制矩形、drawText兩項具體的內容:
//繪制柱狀圖選中浮框
protected float drawHighLightValue(Canvas canvas, String valueStr, float childCenter,
RecyclerView parent, int barChartColor) {
float parentTop = parent.getPaddingTop();
float contentRight = parent.getWidth() - parent.getPaddingRight();
float contentLeft = parent.getPaddingLeft();
String[] strings = valueStr.split(DefaultHighLightMarkValueFormatter.CONNECT_STR);
float leftEdgeDistance = Math.abs(childCenter - contentLeft);
float rightEdgeDistance = Math.abs(contentRight - childCenter);
float leftPadding = DisplayUtil.dip2px(8);
float rightPadding = DisplayUtil.dip2px(8);
float centerPadding = DisplayUtil.dip2px(16);
float rectBottom = parentTop;
float txtTopPadding = DisplayUtil.dip2px(8);
String leftStr = strings[0];
String rightStr = strings[1];
float txtLeftWidth = mHighLightValuePaint.measureText(leftStr);
float txtRightWidth = mHighLightValuePaint.measureText(rightStr);
float rectFHeight = TextUtil.getTxtHeight1(mHighLightValuePaint) + txtTopPadding * 2;
float txtWidth = txtLeftWidth + txtRightWidth + leftPadding +
rightPadding + centerPadding;
float edgeDistance = txtWidth / 2.0f;
float rectTop = parentTop - rectFHeight;
//繪制RectF
RectF rectF = new RectF();
mBarChartPaint.setColor(barChartColor);
if (leftEdgeDistance <= edgeDistance) {//矩形框靠左對齊
rectF.set(contentLeft, rectTop, contentLeft + txtWidth, rectBottom);
float radius = DisplayUtil.dip2px(8);
canvas.drawRoundRect(rectF, radius, radius, mBarChartPaint);
} else if (rightEdgeDistance <= edgeDistance) {//矩形框靠右對齊
rectF.set(contentRight - txtWidth, rectTop, contentRight, rectBottom);
float radius = DisplayUtil.dip2px(8);
canvas.drawRoundRect(rectF, radius, radius, mBarChartPaint);
} else {//居中對齊。
rectF.set(childCenter - edgeDistance, rectTop, childCenter + edgeDistance, rectBottom);
float radius = DisplayUtil.dip2px(8);
canvas.drawRoundRect(rectF, radius, radius, mBarChartPaint);
}
//繪文字
RectF leftRectF = new RectF(rectF.left + leftPadding, rectTop + txtTopPadding,
rectF.left + leftPadding +
txtLeftWidth, rectTop + txtTopPadding + rectFHeight);
mHighLightValuePaint.setTextAlign(Paint.Align.LEFT);
Paint.FontMetrics fontMetrics = mHighLightValuePaint.getFontMetrics();
float top = fontMetrics.top;//為基線到字體上邊框的距離,即上圖中的top
float bottom = fontMetrics.bottom;//為基線到字體下邊框的距離,即上圖中的bottom
int baseLineY = (int) (leftRectF.centerY() + (top + bottom) / 2);//基線中間點的y軸計算公式
canvas.drawText(leftStr, rectF.left + leftPadding, baseLineY, mHighLightValuePaint);
float dividerLineStartX = rectF.left + leftPadding + txtLeftWidth + centerPadding / 2.0f;
float dividerLineStartY = rectTop + DisplayUtil.dip2px(10);
float dividerLineEndX = dividerLineStartX;
float dividerLineEndY = rectBottom - DisplayUtil.dip2px(10);
float[] lines = new float[]{dividerLineStartX, dividerLineStartY,
dividerLineEndX, dividerLineEndY};
canvas.drawLines(lines, mHighLightValuePaint);
float rightRectFStart = rectF.left + leftPadding + txtLeftWidth + centerPadding;
RectF rightRectF = new RectF(rightRectFStart, rectTop + txtTopPadding,
rectF.right - rightPadding, rectBottom - txtTopPadding);
canvas.drawText(rightStr, rightRectF.left, baseLineY, mHighLightValuePaint);
return rectFHeight;
}
具體的文案繪制內容 valueStr 從 ValueFormatter里獲取,我這里需要拆分一下ValueStr,然后繪制leftStr, rightStr這里相當于各個項目自己的需求。
原文鏈接:https://juejin.cn/post/7165304701355819039
相關推薦
- 2022-02-10 Error: Cannot find module ‘webpack/lib/RuleSet‘ 解決
- 2022-07-20 如何go語言比較兩個對象是否深度相同_Golang
- 2022-03-09 軟件構建工具makefile基礎講解_C 語言
- 2022-03-19 Linux系統下安裝Redis數據庫過程_Redis
- 2023-03-15 React.memo?React.useMemo對項目性能優化使用詳解_React
- 2022-11-24 PyTorch?Dataset與DataLoader使用超詳細講解_python
- 2023-06-18 Go語言實現關閉http請求的方式總結_Golang
- 2022-04-04 Server is Started at port : 5500,但是卻不能打開網頁
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支