我们来看看启StartAnimationRunnable 做了什么,从这里我们可以看到nowIndex初始化为0,limitX初始化为左边距 ,然后调用AnimationRunnable。
AnimationRunnable
nowIndex 现在要绘制的终点位置角标
remainder 取的角标余数
说这么多我们看来看log日志,根据这个日志我们可以看到remainder 这个值 就是两点之间的比例
nowIndex 就是我们当前终点角标
接下来我们画线的时候 使用到这些参数来完成动画效果,这里我们可以看到 nowIndex 绘制线的数量,通过判断 只有最后一条的时候要根据取到的余数 继续后面的绘制,noew跟数据的长度一样,最后这里就不需要绘制了。
我们可以看到 取到两点的x,y距离
然后根据remainder 计算出我现在应该处于的位置,在原有的x,y基础上+计算出的x,y 距离
MoveTheLineChart
package com.xiaoxiongchart.chart;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import com.xiaoxiongchart.utils.ChartUtil;
public class MoveTheLineChart extends AbsChart {
private Paint mLinePaint;
public void setSpeed(int speed){
this.speed=speed;
}
public MoveTheLineChart(Context context) {
super(context);
initMoveLinePaint();
}
@Override
public void setValues(float[] values) {
super.setValues(values);
nowIndex=values.length;
}
public MoveTheLineChart(Context context, AttributeSet attrs) {
super(context, attrs);
initMoveLinePaint();
}
private void initMoveLinePaint() {
if(mLinePaint==null){
mLinePaint=new Paint();
}
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(Color.BLUE);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setStrokeWidth(ChartUtil.dip2px(3,getContext()));
mLinePaint.setShader(getShader(new int[]{0x00ffffff,Color.BLUE}));
mLinePaint.setStrokeJoin(Paint.Join.ROUND);
}
@Override
protected void onDraw(Canvas canvas) {
if(mPoints==null)return;
initLineDraw(canvas);
initLine(canvas);
}
private void initLine(Canvas canvas) {
Path path=new Path();
mLinePaint.setAlpha(255);
for (int i = 0; i < nowIndex; i++) {
Point mPoint =mPoints[i];
float x=mPoint.x;
float y=mPoint.y;
if(i==0){
path.moveTo(x,y);
}else {
path.lineTo(x,y);
}
if(i==nowIndex-1 &&i!=mPoints.length-1){
/**获取两点之间的x距离*/
float vx=mPoints[1].x-mPoints[0].x;
/**获取两点之间的y距离*/
float vy=mPoints[i+1].y-mPoints[i].y;
/**绘制最后一条线的时候根据remainder 绘制剩余部分*/
float x1=x+vx*remainder;
float y1=y+vy*remainder;
path.lineTo(x1,y1);
}
}
canvas.drawPath(path,mLinePaint);
Path path1=new Path();
for (int i = 0; i < mPoints.length; i++) {
Point mPoint =mPoints[i];
float x=mPoint.x;
float y=mPoint.y;
if(i==0){
path1.moveTo(x,y);
}else {
path1.lineTo(x,y);
}
}
mLinePaint.setAlpha(100);
canvas.drawPath(path1,mLinePaint);
}
/**启动动画*/
public void StartAnimationRunnable(){
this.nowIndex=0;
this.limitX=l;
postDelayed(new AnimationRunnable(),100);
}
/**限制x坐标最大值*/
private float limitX;
/**余数绘制线时用的*/
private float remainder;
/**目前要绘制的线数量*/
private int nowIndex;
/**移动的速度,越大 移动越快*/
private int speed=5;
private class AnimationRunnable implements Runnable{
@Override
public void run() {
if(mPoints!=null)
if(mPoints.length>nowIndex){
limitX=limitX+speed;
/**每条数据的间距*/
float pading =mPoints[1].x-mPoints[0].x;
float index=(limitX-l)/pading;
remainder= (float) (index-Math.floor(index));
nowIndex= (int) ((limitX-l)/pading);
postDelayed(this,15);
postInvalidate();
}
}
}
}
AbsChart主要绘制一些公用部分
package com.xiaoxiongchart.chart;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.ViewGroup;
import com.xiaoxiongchart.utils.ChartUtil;
public class AbsChart extends ViewGroup {
/**左上右下 边距*/
public int l,t,r,b;
/**整个View的宽,高*/
public int width,height;
/**要绘制的值*/
private float[] values;
/**最终得x,y*/
public Point[] mPoints;
/**x线画笔*/
private Paint mLinePaint;
/**文本画笔*/
private Paint mTextPaint;
/**图表的高度*/
private float mNeedDrawHeight;
/**图表的宽度*/
private float mNeedDrawWidth;
/**最大值,最小值,总数值*/
private float maxVlaue,minValue,calculateValue;
/**每条横线之前的间距,线的条数*/
private float averageLineValue;
private int numberLine=5;
/**设置最大值。最小值*/
public void setMaxAndMin(float maxVlaue,float minValue){
this.maxVlaue=maxVlaue;
this.minValue=minValue;
}
public void setNumberLine(int numberLine){
this.numberLine=numberLine;
}
/**设置左上右下边距*/
public void setChartPading(int l,int t,int r,int b){
Context context=getContext();
this.l=ChartUtil.dip2px(l,context);
this.t=ChartUtil.dip2px(t,context);
this.r=ChartUtil.dip2px(r,context);
this.b=ChartUtil.dip2px(b,context);
}
public void setValues(float[] values){
this.values=values;
mPoints=getPoints(values);
}
public AbsChart(Context context) {
super(context);
initPaint();
}
private void initPoint(){
mPoints=getPoints(values);
}
public AbsChart(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
public void initPaint(){
if(mLinePaint==null) {
mLinePaint = new Paint();
mLinePaint.setTextSize(ChartUtil.dip2px(13,getContext()));
mLinePaint.setColor(0x66888888);
mLinePaint.setAntiAlias(true);
mLinePaint.setStyle(Paint.Style.STROKE);
}
if(mTextPaint==null) {
mTextPaint = new Paint();
mTextPaint.setTextSize(ChartUtil.dip2px(13,getContext()));
mTextPaint.setColor(Color.BLACK);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.RIGHT);
mTextPaint.setStyle(Paint.Style.FILL);
}
}
/**获取线画笔*/
public Paint getLinePaint(){
return mLinePaint;
}
/**获取文本画笔*/
public Paint getTextPaint(){
return mTextPaint;
}
/**初始化绘制的宽高*/
private void initNeedDrawWidthAndHeight(){
this.mNeedDrawWidth = width-l-r;
this.mNeedDrawHeight = height-t-b;
}
public LinearGradient getShader(int[] color){
return new LinearGradient(l-1 ,t,l,t,color, null, Shader.TileMode.CLAMP);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
this.width=getMeasuredWidth();
this.height=getMeasuredHeight();
initNeedDrawWidthAndHeight();
/**计算总值*/
calculateValue=maxVlaue-minValue;
/**计算框线横线间隔的数据平均值*/
averageLineValue = calculateValue/(numberLine-1);
initPoint();
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
}
/**绘制图标基础线,文本*/
public void initLineDraw(Canvas canvas){
DrawHorizontalLine(canvas);
DrawVerticalLine(canvas);
DrawLineAndText(canvas);
}
/**绘制横线*/
public void DrawHorizontalLine(Canvas canvas){
canvas.drawLine(l,height-b,width,height-b,mLinePaint);
}
/**绘制竖线*/
public void DrawVerticalLine(Canvas canvas){
canvas.drawLine(l,t,l,height-b,mLinePaint);
}
/**绘制边框线和边框文本*/
public void DrawLineAndText(Canvas canvas) {
/**绘制边框分段横线与分段文本*/
float averageHeight=mNeedDrawHeight/(numberLine-1);
for (int i = 0; i < numberLine; i++) {
float nowadayHeight= averageHeight*i;
float v=averageLineValue*(numberLine-1-i)+minValue;
canvas.drawText(v+"",l-ChartUtil.dip2px(2,getContext()),nowadayHeight+t,mTextPaint);
}
}
/**根据值计算在该值的 x,y坐标*/
public Point[] getPoints(float[] values){
return ChartUtil.getPoints(values,mNeedDrawHeight,mNeedDrawWidth,calculateValue,minValue,l,t);
}
}
ChartUtil
package com.xiaoxiongchart.utils;
import android.content.Context;
import android.graphics.Point;
public class ChartUtil {
/**
*根据值计算在该值的 x,y坐标
* @param values float值数组
* @param height 要计算的高度
* @param width 要计算的高度
* @param max 最大值
* @param min 最小值
* @param left x轴左边距
* @param top y 上边距
* @return
*/
public static Point[] getPoints(float[] values, float height, float width, float max , float min, float left, float top) {
float leftPading = width / (values.length-1);//绘制边距
Point[] points = new Point[values.length];
for (int i = 0; i < values.length; i++) {
double value = values[i]-min;
//计算每点高度所以对应的值
double mean = (double) max/height;
//获取要绘制的高度
float drawHeight = (float) (value / mean);
int pointY = (int) (height+top - drawHeight);
int pointX = (int) (leftPading * i + left);
Point point = new Point(pointX, pointY);
points[i] = point;
}
return points;
}
public static int dip2px(float dipValue ,Context context) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
}
Activity 调用
package com.xiaoxiongchart;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import com.xiaoxiongchart.chart.MoveTheLineChart;
import java.util.Random;
public class MainActivity extends Activity implements OnClickListener {
private MoveTheLineChart mMoveTheLineChart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMoveTheLineChart =findViewById(R.id.move_line);
mMoveTheLineChart.setChartPading(40,20,10,20);
mMoveTheLineChart.setMaxAndMin(60,-10);
mMoveTheLineChart.setValues(getFloat(10,60));
mMoveTheLineChart.setSpeed(3);
mMoveTheLineChart.postInvalidate();
mMoveTheLineChart.StartAnimationRunnable();
}
private float[] getFloat(int length,int max){
Random random=new Random();
float[] floats=new float[length];
for (int i = 0; i < floats.length; i++) {
float f= random.nextFloat();
floats[i]=f*max;
}
return floats;
}
@Override
public void onClick(View view) {
mMoveTheLineChart.StartAnimationRunnable();
}
}
Xml 布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.xiaoxiongchart.chart.MoveTheLineChart
android:background="@android:color/white"
android:id="@+id/move_line"
android:layout_width="match_parent"
android:layout_height="200dp">
</com.xiaoxiongchart.chart.MoveTheLineChart>
<Button
android:onClick="onClick"
android:id="@+id/start"
android:text="启动动画"
android:layout_width="match_parent"
android:layout_height="50dp"/>
</LinearLayout>