安卓 动态折线图
一、简述
记--一个简单的动态折线图。数据为随机数。
例子打包:链接: https://pan.baidu.com/s/12IdD6eayEvRPeFvoymuCcg 提取码: 9vu7
二、效果
三、工程结构
四、源文件
MainActivity.java文件
package com.liang.chart;
import java.util.Random;
import org.achartengine.chart.PointStyle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.app.Activity;
import android.graphics.Color;
public class MainActivity extends Activity {
private LineChart mLineChart;//直线图类
private boolean addData_thread_run; // 控制添加折线图数据线程的退出
private int x_index;// X轴的刻度值
private Random random;// 用来获取随机数
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//设置主界面
//创建折线图实例 (X轴标题,Y轴标题,X轴的最小值,X轴的最大值,Y轴的最小值,Y轴的最大值,坐标轴的颜色,刻度值的颜色)
mLineChart = new LineChart("时间(min)", "", 0, 100, 0, 9500, Color.WHITE, Color.WHITE);
x_index = 50;//初始化X轴的刻度值
random = new Random();
};
@Override
protected void onResume() //在本页面onStart()之后设置为绘图所在的页面
{
super.onResume();
//设置图表显示页面为本页面
mLineChart.setChartViewActivity(this);
//添加4条折线
mLineChart.addLineToChart("折线A", PointStyle.CIRCLE, Color.BLUE);//添加折线A
mLineChart.addLineToChart("折线B", PointStyle.DIAMOND, Color.GREEN);//添加折线B
mLineChart.addLineToChart("折线C", PointStyle.TRIANGLE, Color.CYAN);//添加折线C
mLineChart.addLineToChart("折线D", PointStyle.SQUARE, Color.YELLOW);//添加折线D
}
// 消息句柄(线程里无法进行界面更新,所以要把消息从线程里发送出来在消息句柄里进行处理)
public Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
case 0:
//添加数据 (添加折点)
mLineChart.addData("折线A", x_index, random.nextInt(9000) );
mLineChart.addData("折线B", x_index, random.nextInt(9000) );
mLineChart.addData("折线C", x_index, random.nextInt(9000) );
mLineChart.addData("折线D", x_index, random.nextInt(9000) );
x_index += 10; // X轴每次右移10个刻度
mLineChart.moveChart(10);// 图标右移10刻度值
//绘制折线图(更新UI)
mLineChart.mChartView.repaint();
break;
}
}
};
// "开始"按钮的点击响应动作
public void onButtonStartClicked(View v)
{
Button btn_start = (Button)v;// 拿到按钮句柄
if( btn_start.getText().toString().equals("开始") )
{// 点击的是"开始"
new Thread(addData_thread).start() ;// 开启子线程,开始动态的添加数据
btn_start.setText("停止");// 设置按钮文本为 "停止"
}
else // 点击的是"停止"
{
addData_thread_run = false;// 结束子线程,停止添加数据
btn_start.setText("开始");// 设置按钮文本为 "开始"
}
}
//添加折线图数据的线程
private Runnable addData_thread = new Runnable()
{
@Override
public void run()
{
addData_thread_run = true;
while(addData_thread_run)
{
try {
Thread.sleep(1000);// 延时1秒
mHandler.sendEmptyMessage(0);// 发送0类型信息,通知主线程更新图表
} catch (InterruptedException e)
{
break;
}
}
}
};
}//end with MainActivity
LineChart.java文件
package com.liang.chart;
import java.util.ArrayList;
import java.util.List;
import org.achartengine.ChartFactory;
import org.achartengine.GraphicalView;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.SeriesSelection;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import com.liang.chart.R;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Paint.Align;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.Toast;
//折线图 类
public class LineChart
{
// 渲染数据集 (图表的数据,折线的折点坐标信息等)
private XYMultipleSeriesDataset mDataset;
// 多重图层渲染器 (可以看做是总渲染器(含多个子渲染器),绘制背景网格,折线图等)
private XYMultipleSeriesRenderer mMltRenderer;
// 折线集合
private List<XYSeries> mSeriesList;
// 标记折线图是否可以拖动
public boolean mIsCanMove = true;
// 图表视图(就是最后绘制出来的整个折线图)
public GraphicalView mChartView;
//无参构造函数 (如果有 有参数的构造函数,就不会自动添加无参构造函数,最好自己加上无参构造函数,)
public LineChart(){}
/* 有参构造函数
* 设置图表属性
* xTitle:X轴标题
* yTitle:Y轴标题
* xMin:X轴的最小值
* xMax:X轴的最大值
* yMin:Y轴的最小值
* yMax:Y轴的最大值
* axisColor:坐标轴的颜色
* labelsColor:标签的颜色(标签:也就是坐标轴上的刻度值10,20...80)
*/
public LineChart(String xTitle, String yTitle, double xMin,
double xMax, double yMin, double yMax, int axisColor, int labelsColor)
{
mDataset = new XYMultipleSeriesDataset();// 实例化数据集对象
mMltRenderer = new XYMultipleSeriesRenderer();// 实例化多层渲染器对象
mSeriesList = new ArrayList<XYSeries>(); // 初始化折线集合
mMltRenderer.setXTitle(xTitle);// 设置X轴标题
mMltRenderer.setYTitle(yTitle);// 设置Y轴标题
mMltRenderer.setXAxisMin(xMin);// 设置X轴的最小值
mMltRenderer.setXAxisMax(xMax);// 设置X轴的最大值
mMltRenderer.setYAxisMin(yMin);// 设置Y轴的最小值
mMltRenderer.setYAxisMax(yMax);// 设置Y轴的最大值
mMltRenderer.setAxesColor(axisColor);// 设置坐标轴颜色
mMltRenderer.setLabelsColor(labelsColor);// 设置标签(刻度值)颜色
mMltRenderer.setShowGrid(true);// 显示网格
mMltRenderer.setGridColor(Color.GRAY);// 设置网格颜色
mMltRenderer.setXLabels(10);// 设置X轴的标签数(有几个刻度)
mMltRenderer.setXLabelsPadding(10);//设置X轴标签的间距
mMltRenderer.setYLabels(16);// 设置Y轴的标签数
mMltRenderer.setYLabelsAlign(Align.RIGHT);// 设置Y轴标签的方向
mMltRenderer.setPointSize((float) 2);//设置折线点的大小
mMltRenderer.setShowLegend(true);// 下面的 图例标注,如圆点的蓝色的折线是X轴...
mMltRenderer.setZoomButtonsVisible(false);// 隐藏放大缩小按钮
mMltRenderer.setZoomEnabled(true, false);// 设置缩放,这边是横向可以缩放,竖向不能缩放
mMltRenderer.setPanEnabled(true, false);// 设置滑动,这边是横向可以滑动,竖向不可滑动
}
//添加一条折线到图表
public void addLineToChart(String lineTitle, PointStyle pointStyle, int lineColor)
{
XYSeriesRenderer serRender = new XYSeriesRenderer();//创建1个子渲染器
XYSeries series = new XYSeries(lineTitle);//创建1条折线
mMltRenderer.addSeriesRenderer(serRender);// 将子渲染器添加到总渲染器
mDataset.addSeries(series);// 将折线添加到数据集
mSeriesList.add(series);// 将折线添加到折线集合
// 设置折线渲染属性
serRender.setPointStyle(pointStyle);// 设置折点的样式
serRender.setColor(lineColor);// 设置折线的颜色
serRender.setFillPoints(true);// 设置折点是实心还是空心
serRender.setDisplayChartValues(false);// 不显示折点的Y值
serRender.setDisplayChartValuesDistance(10);// 设置数值与折点的距离
}
//设置图表的显示页面 (activity:图表显示所在的页面)
public void setChartViewActivity(final Activity activity)
{
if (mChartView == null)
{
//获取一个layout,用来显示图表
LinearLayout layout = (LinearLayout) activity.findViewById(R.id.chart);
//生成图表
mChartView = ChartFactory.getLineChartView(activity, mDataset,mMltRenderer);
mMltRenderer.setClickEnabled(true);// 可以响应点击
mMltRenderer.setSelectableBuffer(10);// 设置点的缓冲半径值(在某点附近点击时,在点的半径范围内都算点击这个点)
//折线的点击响应
mChartView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 拿到点击的折线对象、折点
SeriesSelection seriesSelection = mChartView.getCurrentSeriesAndPoint();
if (seriesSelection != null)
{
// 将所点击的点的信息通过Toast展示(点击的是那一条折线,第几个折点,坐标值)
Toast.makeText(activity,
"折线:"+ seriesSelection.getSeriesIndex()
+ "\n点: "+ seriesSelection.getPointIndex()
+ "\nX="+ seriesSelection.getXValue() + ", Y="+ seriesSelection.getValue(),
Toast.LENGTH_SHORT).show();
}
}
});
// 将图表添加到layout中
layout.addView(mChartView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
else
{
mChartView.repaint();//绘制折线图(重绘,更新)
}
}
// 向某条折线添加折点(x,y) (添加数据)lineTitle:折线的标题
public void addData(String lineTitle, double x, double y)
{
if(mSeriesList.size() > 0 )//有折线
{
for(XYSeries ser : mSeriesList)//遍历折线集合
{
if(ser.getTitle().equals(lineTitle))// 找到指定的折线
{
ser.add(x, y);
break;
}
}
}
}
// 拖动图表 设置X轴的当前显示向右移val
public void moveChart(int val)
{
// 计算当前X轴可视长度,也就是当前看到的X轴上的右刻度与左刻度之差(缩放按钮能够影响看到的刻度值范围)
double dis = mMltRenderer.getXAxisMax() - mMltRenderer.getXAxisMin();
double max = val + mMltRenderer.getXAxisMax();
double min = max - dis;
mMltRenderer.setXAxisMin(min);// 设置X轴显示的左刻度值
mMltRenderer.setXAxisMax(max);// 设置X轴显示的右刻度值
}
}
布局文件
activity_main.xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<LinearLayout
android:id="@+id/linear_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onButtonStartClicked"
android:text="开始" />
</LinearLayout>
<LinearLayout
android:id="@+id/chart"
android:layout_width="fill_parent"
android:layout_height="300dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_below="@+id/linear_layout"
android:orientation="horizontal" >
</LinearLayout>
</RelativeLayout>
五、总结
1、产生指定范围的随机数
Random random = new Random();
int num = random.nextInt(n);// 作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n。
// 生成指定范围的随机数
int num = random.nextInt(b)%(b-a+1) + a;//生成[a,b],包含a,b
// 如生成[1,100],即1到100,包括1和100
int a = 1;
int b = 100;
int num = random.nextInt(b)%(b-a+1) + a;
2、结束线程:使用控制变量来结束线程循环 或者用stop()方法(好像说不安全)
3、线程一般不能二次start(),所以例子中每次开始新建子线程。