Android绘制动态折线图
作者:Knick_Zhang 发布时间:2021-12-28 12:59:50
标签:Android,折线图
所谓动态折线图,就是折线图能随着手指的滑动进行动态绘制,这里很定会产生动画效果。基于这个效果,这里使用SurfaceView进行制图。
实现步奏如下:
(1): 这里新建一个绘图ChartView,继承SurfaceView并实现SurfaceHolder.Callback , Runnable接口,主要绘图工作在子线程中完成。
(2):现实 SurfaceHolder.Callback接口的三个方法,并在 surfaceCreated中开启子线程进行绘图。
(3):重写onTouchEvent方法,在Move事件中,根据手指的滑动距离计算偏移量,具体实现请看代码。
(4): 这里的折线图的坐标值是随意添加的,可以在实际项目中根据需求自己添加。
(5):此例中有大量从集合中添加和删除元素,建议使用LinkedList来进行保存数据。
自定义ChartView:
public class ChartView extends SurfaceView implements SurfaceHolder.Callback , Runnable
{
private Context mContext;
private Paint mPaint;
private Resources res;
private DisplayMetrics dm;
private int canvasHeight;
private int canvasWidth;
private int bHeight = 0;
private int bWidth;
private boolean isMeasure = true;
private boolean canScrollRight = true;
private boolean canScrollLeft = true;
//y轴最大值
private int maxValue;
//y轴间隔值
private int averageValue;
private int marginTop = 20;
private int marginBottom = 80;
//曲线上的总点数
private Point[] mPoints;
//纵坐标值
private LinkedList<Double> yRawData;
//横坐标值
private LinkedList<String> xRawData;
//根据间隔计算出的每个X的值
private LinkedList<Integer> xList = new LinkedList<>();
private LinkedList<String> xPreData = new LinkedList<>();
private LinkedList<Double> yPreData = new LinkedList<>();
private LinkedList<String> xLastData = new LinkedList<>();
private LinkedList<Double> yLastData = new LinkedList<>();
private int spacingHeight;
private SurfaceHolder holder;
private boolean isRunning = true;
private int lastX;
private int offSet;
private Rect mRect;
private int xAverageValue = 0;
public ChartView(Context context)
{
this(context , null);
}
public ChartView(Context context , AttributeSet attrs)
{
super(context, attrs);
this.mContext = context;
initView();
}
private void initView()
{
this.res = mContext.getResources();
this.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dm = new DisplayMetrics();
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(dm);
xPreData.add("05-18");
xPreData.add("05-17");
xPreData.add("05-16");
xPreData.add("05-15");
xPreData.add("05-14");
xPreData.add("05-13");
yPreData.add(4.53);
yPreData.add(3.45);
yPreData.add(6.78);
yPreData.add(5.21);
yPreData.add(2.34);
yPreData.add(6.32);
xLastData.add("05-26");
xLastData.add("05-27");
xLastData.add("05-28");
xLastData.add("05-29");
xLastData.add("05-30");
xLastData.add("05-31");
yLastData.add(2.35);
yLastData.add(5.43);
yLastData.add(6.23);
yLastData.add(7.33);
yLastData.add(3.45);
yLastData.add(2.45);
holder = this.getHolder();
holder.addCallback(this);
}
@Override
protected void onSizeChanged(int w , int h , int oldW , int oldH)
{
if (isMeasure)
{
this.canvasHeight = getHeight();
this.canvasWidth = getWidth();
if (bHeight == 0)
{
bHeight = canvasHeight - marginBottom;
}
bWidth = dip2px(30);
xAverageValue = (canvasWidth - bWidth) / 7;
isMeasure = false;
}
}
@Override
public void run()
{
while (isRunning)
{
drawView();
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
private void drawView()
{
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.WHITE);
mPaint.setColor(res.getColor(R.color.color_f2f2f2));
drawAllXLine(canvas);
mRect = new Rect(bWidth - 3, marginTop - 5 ,
bWidth + (canvasWidth - bWidth) / yRawData.size() * (yRawData.size() - 1) + 3, bHeight + marginTop + marginBottom);
//锁定画图区域
canvas.clipRect(mRect);
drawAllYLine(canvas);
mPoints = getPoints();
mPaint.setColor(res.getColor(R.color.color_ff4631));
mPaint.setStrokeWidth(dip2px(2.5f));
mPaint.setStyle(Paint.Style.STROKE);
drawLine(canvas);
mPaint.setStyle(Paint.Style.FILL);
for (int i = 0 ; i < mPoints.length ; i++)
{
canvas.drawCircle(mPoints[i].x , mPoints[i].y , 5 , mPaint);
}
holder.unlockCanvasAndPost(canvas);
}
//绘制折线图
private void drawLine(Canvas canvas)
{
Point startP = null;
Point endP = null;
for (int i = 0 ; i < mPoints.length - 1; i++)
{
startP = mPoints[i];
endP = mPoints[i + 1];
canvas.drawLine(startP.x , startP.y , endP.x , endP.y , mPaint);
}
}
//绘制所有的纵向分割线
private void drawAllYLine(Canvas canvas)
{
for (int i = 0 ; i < yRawData.size() ; i++)
{
if (i == 0)
{
canvas.drawLine(bWidth, marginTop , bWidth, bHeight + marginTop , mPaint);
}
if (i == yRawData.size() - 1)
{
canvas.drawLine(bWidth + xAverageValue * i, marginTop , bWidth + xAverageValue * i , bHeight + marginTop , mPaint);
}
xList.add(bWidth + xAverageValue * i);
canvas.drawLine(bWidth + xAverageValue * i + offSet, marginTop , bWidth + xAverageValue * i + offSet , bHeight + marginTop , mPaint);
drawText(xRawData.get(i) , bWidth + xAverageValue * i - 30 + offSet, bHeight + dip2px(26) , canvas);
}
}
//绘制所有的横向分割线
private void drawAllXLine(Canvas canvas)
{
for (int i = 0 ; i < spacingHeight + 1 ; i++)
{
canvas.drawLine(bWidth , bHeight - (bHeight / spacingHeight) * i + marginTop ,
bWidth + xAverageValue * (yRawData.size() - 1) , bHeight - (bHeight / spacingHeight) * i + marginTop , mPaint);
drawText(String.valueOf(averageValue * i) , bWidth / 2 , bHeight - (bHeight / spacingHeight) * i + marginTop, canvas);
}
}
//绘制坐标值
private void drawText(String text , int x , int y , Canvas canvas)
{
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setTextSize(dip2px(12));
p.setColor(res.getColor(R.color.color_999999));
p.setTextAlign(Paint.Align.LEFT);
canvas.drawText(text , x , y , p);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder)
{
new Thread(this).start();
Log.d("OOK" , "Created");
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2)
{
Log.d("OOK" , "Changed");
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder)
{
isRunning = false;
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getAction();
int rawX = (int) event.getX();
switch (action)
{
case MotionEvent.ACTION_DOWN:
lastX = rawX;
break;
case MotionEvent.ACTION_MOVE:
int offsetX = rawX - lastX;
if (xPreData.size() == 0 && offSet > 0)
{
offSet = 0;
canScrollRight = false;
}
if (xLastData.size() == 0 && offSet < 0)
{
offSet = 0;
canScrollLeft = false;
}
offSet = offSet + offsetX;
if (offSet > xAverageValue && canScrollRight)
{
offSet = offSet % xAverageValue;
xRawData.addFirst(xPreData.pollFirst());
yRawData.addFirst(yPreData.pollFirst());
xLastData.addFirst(xRawData.removeLast());
yLastData.addFirst(yRawData.removeLast());
canScrollLeft = true;
}
if (offSet < -xAverageValue && canScrollLeft)
{
offSet = offSet % xAverageValue;
xRawData.addLast(xLastData.pollFirst());
yRawData.addLast(yLastData.pollFirst());
xPreData.addFirst(xRawData.removeFirst());
yPreData.addFirst(yRawData.removeFirst());
canScrollRight = true;
}
lastX = rawX;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
private Point[] getPoints()
{
Point[] points = new Point[yRawData.size()];
for (int i = 0 ; i < yRawData.size() ; i++)
{
int ph = bHeight - (int)(bHeight * (yRawData.get(i) / maxValue));
points[i] = new Point(xList.get(i) + offSet , ph + marginTop);
}
return points;
}
public void setData(LinkedList<Double> yRawData , LinkedList<String> xRawData , int maxValue , int averageValue)
{
this.maxValue = maxValue;
this.averageValue = averageValue;
this.mPoints = new Point[yRawData.size()];
this.yRawData = yRawData;
this.xRawData = xRawData;
this.spacingHeight = maxValue / averageValue;
}
private int dip2px(float dpValue)
{
return (int) (dpValue * dm.density + 0.5f);
}
}
MainActivity代码:
public class MainActivity extends Activity
{
LinkedList<Double> yList;
LinkedList<String> xRawData;
ChartView chartView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
chartView = (ChartView) findViewById(R.id.chartView);
yList = new LinkedList<>();
yList.add(2.203);
yList.add(4.05);
yList.add(6.60);
yList.add(3.08);
yList.add(4.32);
yList.add(2.0);
yList.add(5.0);
xRawData = new LinkedList<>();
xRawData.add("05-19");
xRawData.add("05-20");
xRawData.add("05-21");
xRawData.add("05-22");
xRawData.add("05-23");
xRawData.add("05-24");
xRawData.add("05-25");
chartView.setData(yList , xRawData , 8 , 2);
}
}
此例页面布局比较简单,就是在主页面布局中添加一个自定义的ChartView即可,这里不再贴出。可能写得有点仓促,如果不妥之处,请大家批评指正,谢谢!
来源:https://blog.csdn.net/qq_33022345/article/details/52459232
0
投稿
猜你喜欢
- 译文链接:https://www.infoworld.com/art...C# 在 3.0 版本中提供了对 扩展方法 的支持,扩展方法常用于
- 手机游戏实现Android隐藏虚拟按键,供大家参考,具体内容如下在华为等型号的手机会有虚拟按键,在进入游戏的时候,需要全屏隐藏这个按键,并在
- 一般很多项目不是在springcloud的环境中使用的,但是需要用到分布式配置中心来管理一些外部或者项目的配置,这个时候我们可以使用spri
- 简述:观察Byte值转为字符写入文件如果在java里用byte打印出来只有33 到 126的输出字符比较正常此外发现Byte值为13是空格,
- 本文通过两个方法:(1)计算总的页数。 (2)查询指定页数据,实现简单的分页效果。思路:首先得在 DAO 对象中提供分页查询的方法,在控制层
- protobuf对象不能直接使用jsonlib去转,因为protobuf生成的对象的get方法返回的类型有byte[],而只有String类
- RestAPI中, 经常需要操作json字符串, 需要把json字符串"反序列化"成一个对象, 也需要把一个
- Java 数据库连接池详解数据库连接池的原理是:连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库
- 一、前言Hello,又见面了,今天分享如何使用Unity制作计算器,难度中等,可以用来学习,或者当成其他项目的小组件导入。当然,也可以导出来
- 本文实例讲述了c#用for语句输出一个三角形的方法。分享给大家供大家参考。具体分析如下:这是一道面试题,要求是这样的:只使用一个for循环输
- 1)如何获得MediaPlayer实例:可以使用直接new的方式:MediaPlayer mp = new MediaPlayer();也可
- java 制作验证码并进行验证实例详解在注册、登录的页面上经常会出现验证码,为了防止频繁的注册或登录行为。下面是我用java制作的一个验证码
- 前言前面说过了类的加载机制,里面讲到了类的初始化中时用到了一部分内存管理的知识,这里让我们来看下Java虚拟机是如何管理内存的。先让我们来看
- 在前面的《基于任务的异步编程模式(TAP)》文章中讲述了.net 4.5框架下的异步操作自我实现方式,实际上,在.net 4.5中部分类已实
- 一、Statementimport java.sql.*;public class TestJDBC { public stati
- Optional在JAVA中被定义为一个容器类,更确切的说只存一个元素的容器。container object which may or m
- 最近在开发一个项目,需要写一个后管系统,Bootstrap是美国Twitter公司的设计师Mark Otto和Jacob Thornton合
- 创建一个类,在该类的主方法中创建标准输入流的扫描器对象,提示用户输入一个整数,并通过扫描器的方法来接受这个整数,然后通过三元运算符判断该数字
- 隐藏标题栏基于xml<application android:theme="@style/Them
- 本文实例为大家分享了Java实现简单猜拳游戏的具体代码,供大家参考,具体内容如下看网上的猜拳游戏那么多,但都是用switch输入数字,所以用