Android折线效果制作
前面一段时间工作需要绘制一个折线图效果,但是网上很多框架写的代码都是比较麻烦,又是动画又是点击效果等等,而且还有很多地方的代码看不懂,我觉得还是自己写一个折线图,顺便复习一下这些简单的知识。
本文绘制简单的折线效果图,并分析折线绘制的实现过程。
效果如图:
一.需要的自定义View的基础知识
(一)编写自定义MyView类
1.这个MyView需要继承View。不用解释吧。
2.需要重写两个方法,onSizeChange和onDraw方法,这里是不需要重写onMeasure方法的,因为这个自定义View的视图大小没有发生改变,但是如果需要也是可以重写的。
执行顺序:onMeasure-》onSizeChange—》onDraw
自定义的View中三个最重要的方法解释:
onMeasure可以得到并控制本身和子View的大小
onSizeChange可以得到自身视图View的大小
onDraw绘制视图View的具体实现
(二)绘制的方法API
1.绘制一条直线
canvas.drawLine(x1,y1, x2, y2, mPaint);
说明:前面(x1,y1)代表第一个点的坐标,后面(x2,y2)
代表第二个点的坐标,四个数值就可以生成一条直线,最后一个参数是画笔对象。
2.绘制连续的线
canvas.drawLines(points, mPaint);
说明:points是所有点的数值的数组,4个数值代表一条直线,也就是说points的长度是4的整数倍。
3.绘制文字
canvas.drawText(“心跳的频率”, x, y, mPaint);
说明:第一个参数是要绘制的文字,第二三个参数代表文字绘制开始的左边,第四个参数代表画笔对象。
(三)本文折线的思路分析:
使用的方法主要是canvas.drawLines来绘制连续的线。
这里传人一个集合list的数据都是Y轴方向的分量。
1.X轴方向平分成list.size()段(int dipX=width/list.size()),后一个坐标点的x坐标比前一个坐标增加dipX的长度。
2.Y轴方向平分成若干段(int dipY=height/K),这里K的值根据集合中的最大值来确定,Y轴方向的实际长度是list.get(i)*dipY
3.显示的时候Y的方向和API的是不同的,API中Y是从上往下显示的,而我们实际显示是要从下往上显示。所以原来的Y坐标要变成height-Y(View的高度减去原来的长度)
4.上一条线结束的点的位置就是下一条线的点的起始位置,实现代码:
float startX = -pointWidth;
float startY = mHeight - datas.get(0) * pointHeight;
float stopX, stopY;
for (int i = 0; i < datas.size(); i++) {
stopX = startX + pointWidth; //x轴的坐标每次添加一个单位长度。
stopY = mHeight - datas.get(i) * pointHeight;
// 将起点和落点加入集合
addPoint(points, i, startX, startY, stopX, stopY);
// 将落点的值记录下来作为下一次的起点
startX = stopX;
startY = stopY;
}
5.这里不要用canvas.drawLine方法来一直绘制直线,这个效率是很低的,要使用canvas.drawLines方法
二.程序设计的代码
(一)自定义的View类LineCharView
package com.example.charview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 折线图
*/
public class LineChartView extends View {
private int mWidth;
private int mHeight;
private List<Integer> mDatas = new ArrayList<Integer>();
private int paintColors;
private Paint mPaint;
public LineChartView(Context context) {
this(context, null);
}
public LineChartView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 设置数据
*
* @param datas 集合里面还有每条线的集合数据
* @param colors 表示线的颜色集合
*/
public void setDatas(List<Integer> datas, int colors) {
if (datas != null && datas.size() != 0) {
mDatas.clear();
mDatas.addAll(datas);
paintColors = colors;
//重绘视图
postInvalidate();
}
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(2f);
mPaint.setColor(Color.GREEN);
setBackgroundColor(Color.BLACK);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDatas != null) { //传人数据后再实现重绘
mPaint.setColor(paintColors);
//画底下和顶上的线
drawBackGroudLines(canvas);
//画左边的标尺线
DrawLineLeft(canvas);
//画下边的标尺线
drawLineAndTextButtom(canvas);
//绘制上面的文字提示
drawTextTop(canvas);
} else {
canvas.drawText("正在绘制页面", 100, 100, mPaint);
}
}
/**
* 绘制底下的横线和文字
*
* @param canvas
*/
private void drawLineAndTextButtom(Canvas canvas) {
canvas.drawLine(0, mHeight - 50, mWidth, mHeight - 50, mPaint);
//直线底下的文字
for (int i = 0; i < 24; i++) {
String textString = i + ":00";
mPaint.setTextSize(10);
canvas.drawText(textString, 40 + i * (mWidth / 24) - 10, mHeight - 30, mPaint);
//在文字上面画一条小直线
canvas.drawLine(40 + i * (mWidth / 24), mHeight - 50, 40 + i * (mWidth / 24), mHeight - 70, mPaint);
}
}
/**
* 绘制左边的坐标线
*
* @param canvas
*/
private void DrawLineLeft(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);//实心
canvas.drawLine(50, 0, 50, mHeight, mPaint);
//直线边上的文字
for (int i = 0; i < 7; i++) {
String textString = i * 30 + "";
mPaint.setTextSize(20);
canvas.drawText(textString, 10, mHeight - i * 30 * (mWidth / 200), mPaint);
}
}
/**
* 绘制上面的文字提示
*
* @param canvas
*/
private void drawTextTop(Canvas canvas) {
drawLineChart(canvas, mDatas);
//画文字
mPaint.setTextSize(60);
canvas.drawText("心跳的频率", 50, 50, mPaint);
}
/**
* 传人点的集合数据,绘制线条
*
* @param canvas
* @param datas
*/
private void drawLineChart(Canvas canvas, List<Integer> datas) {
int size = datas.size();
if (size != 0 && size != 1) {
float[] points = new float[size * 4];
float pointWidth = (float) mWidth / (float) (size - 1);
float pointHeight = (float) mHeight / 255f;
// 起点和落点的 x,y 坐标!
//Y方向其实是向下偏移了(mHeight-2y)的长度,也就是在原来的基础上添加偏移量的值,即可得到目的的Y
//比如(x,y)---》(x,mHeight-y)
//根据API限定距离顶部是y的距离,目的是距离底部是y的距离,而距离顶部是(mHeight-y)的距离,是不是偏移了(mHeight-2y)的长度!
// (mHeight-2y)+y= mHeight-y;
float startX = -pointWidth;
float startY = mHeight - datas.get(0) * pointHeight;
float stopX, stopY;
for (int i = 0; i < datas.size(); i++) {
stopX = startX + pointWidth; //x轴的坐标每次添加一个单位长度。
stopY = mHeight - datas.get(i) * pointHeight;
// 将起点和落点加入集合
addPoint(points, i, startX, startY, stopX, stopY);
// 将落点的值记录下来作为下一次的起点
startX = stopX;
startY = stopY;
}
canvas.drawLines(points, mPaint);
}
}
/**
* 将起点和落点加入集合
*
* @param points 添加到哪个集合,所有点坐标的最终数组
* @param i 添加的是哪个点组合的数据,代表的是点的个数
* @param startX 起点 X 坐标
* @param startX 起点 Y 坐标
* @param stopX 落点 X 坐标
* @param stopY 落点 Y 坐标
*/
private void addPoint(float[] points, int i, float startX, float startY, float stopX, float stopY) {
int index = i * 4;
// 起点
points[index] = startX;
points[index + 1] = startY;
// 落点
points[index + 2] = stopX;
points[index + 3] = stopY;
}
/**
* //绘制底部和顶部的线
*/
private void drawBackGroudLines(Canvas canvas) {
canvas.drawLine(0, 0, mWidth, 0, mPaint);
canvas.drawLine(0, mHeight, mWidth, mHeight, mPaint);
}
/**
* 得到View的宽度和高度
*
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.e("TAG2", "onSizeChanged");
mWidth = getWidth();
mHeight = getHeight();
}
}
(二)主方法类调用自定义View
package com.example.charview;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class MyActivity extends Activity {
/**
* 自定义折线图的实现
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//数据
List<Integer> datas = new ArrayList<Integer>();
datas.add(66);
datas.add(77);
datas.add(66);
datas.add(88);
datas.add(64);
datas.add(20);
datas.add(44);
datas.add(22);
datas.add(99);
datas.add(35);
datas.add(35);
datas.add(66);
LineChartView lineChartView = (LineChartView) findViewById(R.id.lineCharView);
//给折线图设置数据,和颜色
lineChartView.setDatas(datas, Color.YELLOW);
}
//关闭页面
public void close(View view) {
finish();
}
}
(三)布局文件是非常简单的
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.example.charview.LineChartView
android:layout_width="match_parent"
android:layout_height="400dp"
android:id="@+id/lineCharView"
/>
<Button
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="关闭"
android:onClick="close"
/>
</LinearLayout>
运行后效果:
这里的折线图算是很简单的了,使用的方法也是比较简单的,对于初级学习是很有帮助的。
如果想要这个视图可以拉伸可以把它进行处理(photoView的使用):
http://blog.csdn.net/wenzhi20102321/article/details/64919739