前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 曲线图的绘制示例代码

Android 曲线图的绘制示例代码

作者头像
砸漏
发布2020-11-03 21:23:53
1.7K0
发布2020-11-03 21:23:53
举报
文章被收录于专栏:恩蓝脚本

本文介绍了Android 曲线图的绘制示例代码,分享给大家,具体如下:

效果展示

效果展示.gif

使用方式

代码语言:javascript
复制
// 初始化数据表格相关
with(mTableView) {
  // 配置坐标系
  setupCoordinator("日", "人", /*这里是横坐标的值*/0f, 5f, 10f, 15f, 20f, 25f, 30f)
  // 添加曲线, 确保纵坐标的数值位数相等
  addWave(ContextCompat.getColor(this@MainActivity, R.color.colorYellow), false,
      0f, 10f, 30f, 54f, 30f, 100f, 10f)
  addWave(ContextCompat.getColor(this@MainActivity, R.color.colorGreen), false,
      0f, 30f, 20f, 20f, 46f, 25f, 5f)
  addWave(ContextCompat.getColor(this@MainActivity, R.color.colorPink), false,
      0f, 30f, 20f, 50f, 46f, 30f, 30f)
  addWave(Color.parseColor("#8596dee9"), true,
      0f, 15f, 10f, 10f, 40f, 20f, 5f)
}

实现思路

  1. 横坐标是固定的, 纵坐标需要跟随曲线传入的数值去动态的调整
  2. 绘制坐标轴: 纵横交错的网格
  3. 根据用户传入坐标数值去绘制坐标轴上的数值
  4. 给X轴和Y轴添加单位信息
  5. 根据用户传入的具体的数值绘制曲线(这里不采用Bezier, 不容易精确的控制顶点的位置)
  6. 绘制填充效果
  7. 添加属性动画

代码实现

代码语言:javascript
复制
/**
* Created by FrankChoo on 2017/12/29.
* Email: frankchoochina@gmail.com
* Version: 1.0
* Description: 表格自定义View
*/
public class TableView extends View {
private List<WaveConfigData  mWaves;// 数值集合
// 坐标轴的数值
private int mCoordinateYCount = 8;
private float[] mCoordinateXValues;// 外界传入
private float[] mCoordinateYValues;// 动态计算
// 坐标的单位
private String mXUnit;
private String mYUnit;
// 所有曲线中所有数据中的最大值
private float mGlobalMaxValue;// 用于确认是否需要调整坐标系
private Paint mCoordinatorPaint;
private Paint mTextPaint;
private Paint mWrapPaint;
// 坐标轴上描述性文字的空间大小
private int mTopUnitHeight;// 顶部Y轴单位高度
private int mBottomTextHeight;
private int mLeftTextWidth;
// 网格尺寸
private int mGridWidth, mGridHeight;
private float mAnimProgress;
public TableView(Context context) {
this(context, null);
}
public TableView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public TableView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
post(new Runnable() {
@Override
public void run() {
showAnimator();
}
});
}
private void init() {
// 初始化数据集合的容器
mWaves = new ArrayList< ();
// 坐标系的单位
mBottomTextHeight = dp2px(40);// X轴底部字体的高度
mLeftTextWidth = mBottomTextHeight;// Y轴左边字体的宽度
mTopUnitHeight = dp2px(30);// 顶部Y轴的单位
// 初始化坐标轴Paint
mCoordinatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mCoordinatorPaint.setColor(Color.LTGRAY);
// 初始化文本Paint
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mTextPaint.setColor(Color.GRAY);
mTextPaint.setTextSize(sp2px(12));
// 初始化曲线Paint
mWrapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mWrapPaint.setPathEffect(new CornerPathEffect(200f));
}
/**
* 配置坐标轴信息
*
* @param xUnit       X 轴的单位
* @param yUnit       Y 轴的单位
* @param coordinateXValues X 坐标轴上的数值
*/
public void setupCoordinator(String xUnit, String yUnit, float... coordinateXValues) {
mXUnit = xUnit;
mYUnit = yUnit;
mCoordinateXValues = coordinateXValues;
}
/**
* 添加一条曲线, 确保与横坐标的数值对应
*
* @param color
* @param isCoverRegion
* @param values
*/
public void addWave(int color, boolean isCoverRegion, float... values) {
mWaves.add(new WaveConfigData(color, isCoverRegion, values));
// 根据value的值去计算纵坐标的数值
float maxValue = 0;
for (float value : values) {
maxValue = Math.max(maxValue, value);
}
if (maxValue < mGlobalMaxValue) return;
mGlobalMaxValue = maxValue;
// 保证网格的数值都为 5 的倍数
float gridValue = mGlobalMaxValue / (mCoordinateYCount - 1);
if (gridValue % 5 != 0) {
gridValue += 5 - (gridValue % 5);
}
// 给纵坐标的数值赋值
mCoordinateYValues = new float[mCoordinateYCount];
for (int i = 0; i < mCoordinateYCount; i++) {
mCoordinateYValues[i] = i * gridValue;
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
drawCoordinate(canvas);
drawWrap(canvas);
}
public void showAnimator() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimProgress = (float) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
/**
* 绘制坐标系
*/
private void drawCoordinate(Canvas canvas) {
Point start = new Point();
Point stop = new Point();
// 1. 绘制横轴线和纵坐标单位
int xLineCount = mCoordinateYValues.length;
mGridHeight = (getHeight() - getPaddingTop() - getPaddingBottom() - mBottomTextHeight - mTopUnitHeight) / (xLineCount - 1);
for (int i = 0; i < xLineCount; i++) {
start.x = getPaddingLeft() + mLeftTextWidth;
start.y = getHeight() - getPaddingBottom() - mBottomTextHeight - mGridHeight * i;
stop.x = getRight() - getPaddingRight();
stop.y = start.y;
// 绘制横轴线
canvas.drawLine(start.x, start.y, stop.x, stop.y, mCoordinatorPaint);
// 绘制纵坐标单位
if (i == 0) continue;
String drawText = String.valueOf((int) mCoordinateYValues[i]);
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
float offsetY = ((fontMetrics.bottom - fontMetrics.top) / 2 + fontMetrics.bottom) / 2;
float baseLine = start.y + offsetY;
float left = getPaddingLeft() + mLeftTextWidth / 2 - mTextPaint.measureText(drawText) / 2;
canvas.drawText(drawText, left, baseLine, mTextPaint);
// 绘制Y轴单位
if (i == xLineCount - 1) {
drawText = mYUnit;
baseLine = getPaddingTop() + mTopUnitHeight / 2;
canvas.drawText(drawText, left, baseLine, mTextPaint);
}
}
// 2. 绘制纵轴线和横坐标单位
int yLineCount = mCoordinateXValues.length;
mGridWidth = (getWidth() - getPaddingLeft() - getPaddingRight() - mLeftTextWidth) / (yLineCount - 1);
for (int i = 0; i < yLineCount; i++) {
start.x = getPaddingTop() + mLeftTextWidth + mGridWidth * i;
start.y = getPaddingTop() + mTopUnitHeight;
stop.x = start.x;
stop.y = getHeight() - mBottomTextHeight - getPaddingBottom();
// 绘制纵轴线
canvas.drawLine(start.x, start.y, stop.x, stop.y, mCoordinatorPaint);
// 绘制横坐标单位
String drawText = String.valueOf((int) mCoordinateXValues[i]);
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
float offsetY = ((fontMetrics.bottom - fontMetrics.top) / 2 + fontMetrics.bottom) / 2;
float baseLine = getHeight() - getPaddingBottom() - mBottomTextHeight / 2 + offsetY;
float left = start.x - mTextPaint.measureText(drawText) / 2;
// 绘制X轴单位
if (i == 0) {
drawText = mXUnit;
left = getPaddingLeft() + mLeftTextWidth / 2 - mTextPaint.measureText(drawText) / 2;
}
canvas.drawText(drawText, left, baseLine, mTextPaint);
}
}
/**
* 绘制曲线
*/
private void drawWrap(Canvas canvas) {
canvas.clipRect(new RectF(
mLeftTextWidth,
getPaddingTop() + mTopUnitHeight,
(getRight() - getPaddingRight()) * mAnimProgress,
getHeight() - getPaddingBottom() - mBottomTextHeight)
);
float yHeight = mGridHeight * (mCoordinateYCount - 1);
for (WaveConfigData wave : mWaves) {
Path path = new Path();
path.moveTo(0, getHeight());
float maxY = mCoordinateYValues[mCoordinateYCount - 1];// Y轴坐标的最大值
for (int index = 1; index < wave.values.length; index++) {
path.lineTo(
mLeftTextWidth + mGridWidth * index,
getHeight() - getPaddingBottom() - mBottomTextHeight
- yHeight * (wave.values[index] / maxY)
);
}
if (wave.isCoverRegion) {
mWrapPaint.setStyle(Paint.Style.FILL);
path.lineTo(getRight() - getPaddingRight(), getHeight());
path.close();
} else {
mWrapPaint.setStyle(Paint.Style.STROKE);
mWrapPaint.setStrokeWidth(10);
}
mWrapPaint.setColor(wave.color);
canvas.drawPath(path, mWrapPaint);
}
}
private int dp2px(float dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dp, getResources().getDisplayMetrics());
}
private int sp2px(float sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
sp, getResources().getDisplayMetrics());
}
public static class WaveConfigData {
int color;
boolean isCoverRegion;
float values[];
public WaveConfigData(int color, boolean isCoverRegion, float[] values) {
this.color = color;
this.isCoverRegion = isCoverRegion;
this.values = values;
}
}
}

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档