android绘制曲线和折线图的方法
作者:会爬楼梯的鱼 发布时间:2021-11-19 02:26:02
标签:android,曲线,折线图
本文实例为大家分享了android绘制曲线和折线图的具体代码,供大家参考,具体内容如下
(曲线)
(折线)
1.CurveView.java
package com.package;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class CurveView extends View {
private Paint mBrokenLinePaint;
private Paint mInCirclePaint;
private Paint mFillCirclePaint;
private Paint mOutCirclePaint;
private Paint mBottomLinePaint;
private Paint mXTextPaint;
private Paint mYLinePaint;
private Paint mYTextPaint;
private boolean isCurve;
/**
* X轴的数量,按需调整
*/
private int mLength = 7;
/**
* 获取屏幕的宽度
*/
private final int mScreenWidth = getScreenWidth(getContext());
/**
* 获取实际屏幕的宽度
*/
private int cScreenWidth = getScreenWidth(getContext());
/**
* 整个折线图的高度=屏幕高度-顶部的状态栏高度
*/
private final int mHeight = getScreenHeight(getContext()) - dp2px(getContext(), 190);
/**
* 节点外圆的半径
*/
private final int outRadius = dp2px(getContext(), 6);
/**
* 节点内圆的半径
*/
private final int inRadius = dp2px(getContext(), 3);
/**
* 左右两边距边缘的距离
*/
private final float mSideLength = dp2px(getContext(), 20);
/**
* X轴底部文字的高度
*/
private final int mXTextHeight = dp2px(getContext(), 30);
/**
* 距离上边距的高度
*/
private final int mPaddingTop = dp2px(getContext(), 10);
/**
* 获取间隔距离
*/
private int mSpaceLength;
/**
* 用户设置的数据
*/
private final List<Pair<String, Float>> dataValue = new ArrayList<>();
/**
* 节点数据
*/
private final List<Pair<Float, Float>> nodeValue = new ArrayList<>();
/**
* 节点上标注的文字
*/
private final List<String> yValue = new ArrayList<>();
private Paint mShadowPaint;
public CurveView(Context context) {
this(context, null);
}
public CurveView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CurveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* dp转px
*/
public static int dp2px(Context context, float dp) {
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
return Math.round(px);
}
/**
* 获取屏幕宽度
*/
public static int getScreenWidth(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point size = new Point();
wm.getDefaultDisplay().getSize(size);
return size.x;
}
/*设置实际内容宽度*/
public void setContentWidth(int size) {
mLength = size + 1;
cScreenWidth = (mScreenWidth / 7) * size;
}
/**
* 获取屏幕高度
*/
public static int getScreenHeight(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point size = new Point();
wm.getDefaultDisplay().getSize(size);
return size.y;
}
/**
* 设置数据
*/
public void setData(List<String> xAxis, List<String> yAxis) {
dataValue.clear();
yValue.clear();
float yHeight = mHeight - mXTextHeight;
for (int i = 0; i < xAxis.size(); i++) {
yValue.add(yAxis.get(i));
float value = Float.parseFloat(yAxis.get(i));
dataValue.add(new Pair<>(xAxis.get(i), (yHeight - value / 200f * yHeight)));
}
invalidate();
requestLayout();
}
private void init() {
getSpaceLength();//获取间隔距离
initYLine();//初始化竖直方向的线条
initBottomLine();//初始化底部横线paint
initInCircle();//初始化节点内圆
initBrokenLine();//初始化曲线、折线
initXtext();//初始化X轴标签
initYtext();//初始化Y轴上数值
initShadowPaint();//初始化阴影
}
/**
* 获取间隔距离
* (屏幕宽度-两边的间距)/(x轴数量-1)
*/
private void getSpaceLength() {
mSpaceLength = (int) (mScreenWidth - mSideLength * 2) / (mLength - 1);
}
/**
* 初始化竖直方向的线条
*/
private void initYLine() {
mYLinePaint = new Paint();
mYLinePaint.setColor(Color.GRAY);
mYLinePaint.setStrokeWidth(1);
mYLinePaint.setStyle(Paint.Style.STROKE);
mYLinePaint.setAntiAlias(true);
}
/**
* 初始化底部横线paint
*/
private void initBottomLine() {
mBottomLinePaint = new Paint();
mBottomLinePaint.setColor(Color.GRAY);
mBottomLinePaint.setStrokeWidth(dp2px(getContext(), 0.5f));
mBottomLinePaint.setStyle(Paint.Style.STROKE);
mBottomLinePaint.setAntiAlias(true);
}
/**
* 初始化X轴标签
*/
private void initXtext() {
mXTextPaint = new Paint();
mXTextPaint.setColor(Color.GRAY);
mXTextPaint.setTextSize(dp2px(getContext(), 12));
mXTextPaint.setStyle(Paint.Style.FILL);
mXTextPaint.setAntiAlias(true);
}
/**
* 初始化Y轴上数值
*/
private void initYtext() {
mYTextPaint = new Paint();
mYTextPaint.setColor(Color.GRAY);
mYTextPaint.setTextSize(dp2px(getContext(), 12));
mYTextPaint.setStyle(Paint.Style.FILL);
mYTextPaint.setAntiAlias(true);
}
/**
* 初始化节点内圆
*/
private void initInCircle() {
//初始化外圆
mOutCirclePaint = new Paint();
mOutCirclePaint.setColor(Color.GREEN);
mOutCirclePaint.setStyle(Paint.Style.FILL);
mOutCirclePaint.setAntiAlias(true);
//内框
mInCirclePaint = new Paint();
mInCirclePaint.setColor(Color.GRAY);
mInCirclePaint.setStyle(Paint.Style.STROKE);
mInCirclePaint.setStrokeWidth(dp2px(getContext(), 2));
mInCirclePaint.setAntiAlias(true);
//内圆
mFillCirclePaint = new Paint();
mFillCirclePaint.setColor(Color.GRAY);
mFillCirclePaint.setStyle(Paint.Style.FILL);
mFillCirclePaint.setAntiAlias(true);
}
/**
* 初始化曲线、折线
*/
private void initBrokenLine() {
mBrokenLinePaint = new Paint();//折线
mBrokenLinePaint.setColor(Color.GRAY);
mBrokenLinePaint.setStrokeWidth(dp2px(getContext(), 2f));
mBrokenLinePaint.setStyle(Paint.Style.STROKE);
mBrokenLinePaint.setStrokeCap(Paint.Cap.ROUND);
mBrokenLinePaint.setAntiAlias(true);
}
/**
* 初始化阴影
*/
private void initShadowPaint() {
mShadowPaint = new Paint();
mShadowPaint.setStyle(Paint.Style.FILL);
mShadowPaint.setAntiAlias(true);
Shader shader = new LinearGradient(getWidth() / 2f, getHeight(), getWidth() / 2f, 0,
Color.parseColor("#3300FF00"), Color.parseColor("#3300FF00"), Shader.TileMode.MIRROR);
mShadowPaint.setShader(shader);
}
/**
* 绘制
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawYLine(canvas);
// 绘制底部的X轴线
drawBottomLine(canvas);
// 绘制节点和折线图
drawCircleLine(canvas);
// 绘制Y轴上的数据和背景
drawYtext(canvas);
// 绘制X轴标签文字
drawBottomText(canvas);
//阴影
drawShadow(canvas);
}
/**
* 绘制竖直方向上的线
*/
private void drawYLine(Canvas canvas) {
for (int i = 0; i < dataValue.size(); i++) {
canvas.drawLine(mSideLength + mSpaceLength * i, mPaddingTop, mSideLength + mSpaceLength * i, mHeight - mXTextHeight, mYLinePaint);
}
}
/**
* 绘制节点和曲线或者折线图
*/
private void drawCircleLine(Canvas canvas) {
nodeValue.clear();
for (int i = 0; i < dataValue.size(); i++) {
Pair<String, Float> pair = dataValue.get(i);
// 绘制节点外圆
canvas.drawCircle(mSideLength + mSpaceLength * i, pair.second, outRadius, mOutCirclePaint);
// 绘制节点内框
canvas.drawCircle(mSideLength + mSpaceLength * i, pair.second, inRadius, mFillCirclePaint);
// 绘制节点内圆
canvas.drawCircle(mSideLength + mSpaceLength * i, pair.second, inRadius, mInCirclePaint);
// 保存圆心坐标
Pair<Float, Float> pairs = new Pair<>(mSideLength + mSpaceLength * i, pair.second);
nodeValue.add(pairs);
}
drawScrollLine(canvas);//曲线
// drawLine(canvas);//折线
}
/**
* 绘制曲线图
*/
private void drawScrollLine(Canvas canvas) {
isCurve = true;
PointF pStart, pEnd;
List<PointF> points = getPoints();
Path path = new Path();
for (int i = 0; i < points.size() - 1; i++) {
pStart = points.get(i);
pEnd = points.get(i + 1);
PointF point3 = new PointF();
PointF point4 = new PointF();
float wd = (pStart.x + pEnd.x) / 2;
point3.x = wd;
point3.y = pStart.y;
point4.x = wd;
point4.y = pEnd.y;
path.moveTo(pStart.x, pStart.y);
path.cubicTo(point3.x, point3.y, point4.x, point4.y, pEnd.x, pEnd.y);
canvas.drawPath(path, mBrokenLinePaint);
}
}
/**
* 绘制折线
*/
private void drawLine(Canvas canvas) {
isCurve = false;
for (int i = 0; i < nodeValue.size(); i++) {
if (i != nodeValue.size() - 1) {
canvas.drawLine((float) nodeValue.get(i).first,
(float) nodeValue.get(i).second,
(float) nodeValue.get(i + 1).first,
(float) nodeValue.get(i + 1).second, mBrokenLinePaint);
}
}
}
/**
* 绘制阴影
*/
private void drawShadow(Canvas canvas) {
List<PointF> points = getPoints();
if (isCurve) {//曲线
PointF pStart, pEnd;
Path path = new Path();
for (int i = 0; i < points.size() - 1; i++) {
pStart = points.get(i);
pEnd = points.get(i + 1);
PointF point3 = new PointF();
PointF point4 = new PointF();
float wd = (pStart.x + pEnd.x) / 2;
point3.x = wd;
point3.y = pStart.y;
point4.x = wd;
point4.y = pEnd.y;
path.moveTo(pStart.x, pStart.y);
path.cubicTo(point3.x, point3.y, point4.x, point4.y, pEnd.x, pEnd.y);
//减去文字和指示标的高度
path.lineTo(pEnd.x, getHeight() - mXTextHeight);
path.lineTo(pStart.x, getHeight() - mXTextHeight);
}
path.close();
canvas.drawPath(path, mShadowPaint);
} else {
Path path = new Path();
path.moveTo(points.get(0).x, points.get(0).y);
for (int i = 1; i < points.size(); i++) {
path.lineTo(points.get(i).x, points.get(i).y);
}
//链接最后两个点
int index = points.size() - 1;
path.lineTo(points.get(index).x, getHeight() - mXTextHeight);
path.lineTo(points.get(0).x, getHeight() - mXTextHeight);
path.close();
canvas.drawPath(path, mShadowPaint);
}
}
/**
* 获取坐标点
*/
private List<PointF> getPoints() {
ArrayList<PointF> points = new ArrayList<>();
for (Pair<Float, Float> pair : nodeValue) {
points.add(new PointF((float) pair.first, (float) pair.second));
}
return points;
}
/**
* 绘制底部的X轴线
*/
private void drawBottomLine(Canvas canvas) {
canvas.drawLine(0, mHeight - mXTextHeight, cScreenWidth, mHeight - mXTextHeight, mBottomLinePaint);
}
/**
* 绘制X轴标签文字
*/
private void drawBottomText(Canvas canvas) {
for (int i = 0; i < dataValue.size(); i++) {
String xValue = dataValue.get(i).first;
// 获取Text内容宽度
Rect bounds = new Rect();
mXTextPaint.getTextBounds(xValue, 0, xValue.length(), bounds);
int width = bounds.right - bounds.left;
canvas.drawText(xValue, mSideLength - width / 2f + mSpaceLength * i, mHeight - mXTextHeight / 2f, mXTextPaint);
}
}
/**
* 绘制Y轴上的数据和背景
*/
private void drawYtext(Canvas canvas) {
for (int i = 0; i < dataValue.size(); i++) {
Pair<String, Float> pair = dataValue.get(i);
// 用Rect计算Text内容宽度
Rect bounds = new Rect();
mYTextPaint.getTextBounds(pair.first, 0, pair.first.length(), bounds);
int textWidth = bounds.right - bounds.left;
// 绘制节点上的文字
canvas.drawText(yValue.get(i), mSideLength + mSpaceLength * i - textWidth / 2f, pair.second - 25, mYTextPaint);
}
}
}
2.xml里引入
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.package.line.CurveView
android:id="@+id/curveView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f2f2f2"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3.activity中使用
package com.package;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.package.line.CurveView;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CurveView curveView = findViewById(R.id.curveView);
List<String> xList = Arrays.asList("1","2","3","4","5","6","7");
List<String> yList = Arrays.asList("0","50","55","51","53","56","59");
curveView.setData(xList, yList);
}
}
来源:https://blog.csdn.net/qq_30089721/article/details/121614214


猜你喜欢
- 我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还
- 本文就是会将数组里面的单词进行倒序排列 例如 how old are you -> you are old how示例程序输出结果:t
- 最近在用SpringMvc写项目的时候,遇到一个问题,就是方法的鉴权问题,这个问题弄了一天了终于解决了,下面看下解决方法项目需求:需要鉴权的
- 前言最近的项目中需要用到VideoView实现视频播放,自己花了一天多时间才能出来,有点想打自己再见,在学校的时候没好好学。使用VideoV
- 在我们开发过程中用 Mybatis 经常会用到下面的例子Mapper如下Map<String ,String > testArr
- 将Fragment与Layout结合使用,一般都是主Activity以frame填充Activity的方式交互管理Fragment :1.由
- 目录结构:Data.xls数据: 后台页面:public void doGet(HttpServletRequest reques
- 最近做项目中遇到ToolBar因为不同的界面toobar不同为了描述统一的风格。相信大家也非常清楚,大多数ToolBar包括以下几个方面左标
- 值传递当调用方法进行值传递时,方法内部会产生一个局部变量,在方法内部使用局部变量的值,并不影响传入原来数据的值,包括在使用基本数据类型的包装
- 前言大家好,我是小郭,前面我们学习了利用Semaphore来防止多线程同时操作一个资源,通常我们都会利用并行来优化性能,但是对于串行化的业务
- springboot整合nacos1、引入依赖<dependency> <groupId>
- 在上一篇文章中我介绍了使用Intent显式来实现页面向下跳转,接下来这篇文章主要介绍的是使用Intent隐式来实现向上跳转,什么意思呢,就是
- 由于要做一个新项目,所以打算做一个简单的图片验证码。先说说思路吧:在服务端,从一个文件夹里面找出8张图片,再把8张图片合并成一张大图,在8个
- ViewPager这个小demo实现的是可以左右循环滑动图片,下面带索引,滑到最后一页在往右滑动就要第一页,第一页往左滑动就到最后一页,先上
- 一、LinkedHashMap的类继承关系二、源码分析1.自己对LinkedHashMap的理解从继承关系上,我们看到LinkedHashM
- 首先 函数指针是指向一组同类型的函数的指针;而类成员函数我们也可以相似的认为,它是指向同类中同一组类型的成员函数的指针,当然这里的成员函数更
- 昨晚,一同事问到我,怎么利用java反射解析内部类静态成员变量的值,于是顺手写下了。废话不多说,直接上代码!待解析类结构如下:/** * @
- 缘起经过前面三章的入门,我们大概了解了Mybatis的主线逻辑是什么样子的,在本章中,我们将正式进入Mybatis的源码海洋。Mybatis
- 本文通过一个简单的小例子简述SharpZipLib压缩文件的常规用法,仅供学习分享使用,如有不足之处,还请指正。什么是SharpZipLib
- 概述非对称加密算法与对称加密算法的主要差别在于非对称加密算法用于加密和解密的密钥不相同,非对称加密算法密钥分为公钥和私钥,公钥加密只能用私钥