您的位置:首页 > 移动开发 > Android开发

Android自定义View实现简单的折线图、柱状图

2017-04-21 15:03 323 查看






首先说第一个柱状图,实现很简单。一个自定义View,重现里面的OnDraw方法。然后利用paint,canvas绘制带填充的长方形即可。每个长方形的X轴平方View的x轴即可,长方形的高度通过简单的计算即可得到。下面上柱状图代码
package com.hrules.charter.demo.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.BounceInterpolator;

import com.hrules.charter.demo.R;

import java.util.Random;

/**
* 柱状图
* Created by 黄海 on 2017/4/17.
*/

public class Histogram extends View implements View.OnClickListener {
int width, height;
Paint paintBar, paintText;
float[] values = new float[15];
float[] valuesTemp = new float[15];
int colorBackground = R.color.default_barBackgroundColor;//柱状背景
int[] colorBar = new int[]{R.color.lightBlue500, R.color.lightBlue400, R.color.lightBlue300};//柱状颜色
float maxY;
int barMarginLeft = 7;
int tagHeight = 45;//x和Y轴的数字
boolean anim;
boolean showLineXNums = true, showLineYNums = true;//展示X、Y轴的数字
int lineYNums = 5;//Y轴展示的格数
ValueAnimator valueAnimator;

public Histogram(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}

public Histogram(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paintText = paintBar = new Paint();
paintBar.setAntiAlias(true);
paintText.setAntiAlias(true);
paintText.setTextSize(25);
paintText.setColor(getResources().getColor(colorBar[0]));
initValuesAndMaxY();
setOnClickListener(this);
}

private void initValuesAndMaxY() {
Random random = new Random();
for (int i = 0; i < values.length; i++) {
values[i] = random.nextFloat() * 100;
}
for (float i : values) {
maxY = maxY < i ? i : maxY;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
width = getMeasuredWidth();
height = getMeasuredHeight();
int barWidth = (width - tagHeight) / values.length;
int j = 0;
if (!anim)
valuesTemp = values.clone();
for (int i = 0; i < valuesTemp.length; i++) {
RectF rect = new RectF();
rect.left = tagHeight + i * barWidth + barMarginLeft;
rect.top = (height - tagHeight) * (1 - 1.0f * valuesTemp[i] / maxY);
rect.right = rect.left + barWidth - barMarginLeft;
rect.bottom = height - tagHeight;
//draw the barBackground
paintBar.setColor(getResources().getColor(colorBackground));
canvas.drawRect(rect.left, 0, rect.right, rect.bottom, paintBar);
//paint the bar
j = j > colorBar.length - 1 ? 0 : j;
paintBar.setColor(getResources().getColor(colorBar[j++]));
canvas.drawRect(rect, paintBar);
if (showLineXNums) {
//draw x-coordinate num
float textWidth = paintText.measureText(String.valueOf(i));
float textLeft = rect.left + rect.width() / 2 - textWidth / 2;
canvas.drawText(String.valueOf(i), textLeft, height, paintText);
}
}
//draw y-coordinate num
if (showLineYNums) {
int avgHeight = (height - tagHeight) / lineYNums;
for (int i = 0; i < lineYNums; i++) {
float x = 0;
float y = (height - tagHeight) - avgHeight * (i + 1);
int valueY = (int) (maxY * (i + 1) / lineYNums);
// canvas写字是从x、y轴往右上写的
canvas.drawText(String.valueOf(valueY), x, y + 30, paintText);
}
}
}

@Override
public void onClick(View v) {
anim = true;
valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valuesTemp = values.clone();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
for (int i = 0; i < valuesTemp.length; i++) {
//update valuesTemps
float animatedValue = (float) animation.getAnimatedValue();
valuesTemp[i] = maxY * animatedValue < values[i] ? maxY * animatedValue : values[i];
}
invalidate();
}
});
// valueAnimator.setInterpolator(new LinearInterpolator());
//落地回调效果
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.setDuration(2000l);
valueAnimator.start();
}
}


实现比较简单,注释都有。最后说下onClick方法的作用:valuesTemp是原有柱状数据的副本,view点击后让valuesTemp的每个数据从maxY的最小百分比开始不断的增长,从而实现一个动态改变的动画效果,如下:



折线图运用的paint,canvas与path完成,计算每个点的位置和柱状图类似,折线图形成一个填充效果也简单就是path close一下,然后绘制close后的path就是。下面看源码
package com.hrules.charter.demo.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.hrules.charter.demo.R;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
* 折线图
* Created by 黄海 on 2017/4/18.
*/

public class LineChart extends View {
Path path = new Path();
float[] values = new float[15];
float maxY;
Paint paintXy, paintLine;
int width, height;//view 宽高

public LineChart(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}

public LineChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initValuesAndMaxY();
paintXy = new Paint(Paint.ANTI_ALIAS_FLAG);
paintXy.setStyle(Paint.Style.STROKE);
paintXy.setStrokeWidth(6);
paintXy.setColor(getResources().getColor(R.color.lightBlue500));
paintLine = new Paint(Paint.ANTI_ALIAS_FLAG);
paintLine.setStyle(Paint.Style.FILL);
paintLine.setColor(getResources().getColor(R.color.colorPrimary));
}

private void initValuesAndMaxY() {
Random random = new Random();
for (int i = 0; i < values.length; i++) {
values[i] = random.nextFloat() * 100;
maxY = maxY < values[i] ? values[i] : maxY;
}
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
width = getMeasuredWidth();
height = getMeasuredHeight();
//每格宽度
int widthAvg = width / values.length;
//画x轴y轴
path.moveTo(0, 0);
path.lineTo(0, height);
path.lineTo(width, height);
canvas.drawPath(path, paintXy);
List<PointF> list = new ArrayList<>();
for (int i = 0; i < values.length; i++) {//收集坐标信息
PointF point = new PointF(widthAvg / 2 + i * widthAvg, 0);
point.set(widthAvg / 2 + i * widthAvg, (1 - values[i] / maxY) * height);
list.add(point);
}

//画线
path.reset();//清除 画x轴y轴而产生的闭合影响
path.moveTo(list.get(0).x, list.get(0).y);
for (int i = 1; i < values.length; i++) {
path.lineTo(list.get(i).x, list.get(i).y);
// path.cubicTo(ps.x, ps.y,(ps.x+pe.x)/2,(ps.y+pe.y)/2, pe.x, pe.y);//画三次贝塞尔曲线
}
canvas.drawPath(path, paintXy);
//折线图闭合
path.lineTo(list.get(values.length - 1).x, height);
path.lineTo(list.get(0).x, height);
path.close();
canvas.drawPath(path, paintLine);
//画点
for (int i = 0; i < values.length; i++) {
canvas.drawCircle(list.get(i).x, list.get(i).y, 9, paintLine);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: