利用Achartengine制作趋势图(含双Y轴,浮窗)

历史趋势图难点分析:

1、时间轴(X轴)的表示:

使用最普通的getLineChartView(context, mDataset,mRenderer)去制作呢?如果能的话,X轴将会使用自定义label,但又因为我们的自定义label是时间格式的字符串,很多人可能觉得太棘手了,放弃了此种尝试。事实上,这种方式使用起来更加灵活,而且可以实现双Y轴的历史趋势图效果,如果使用前者,双Y轴的效果无法实现,因为TimeSeries没有提供类似XYSeries的XYSeries(Stringtitle, intscaleNumber)构造方法。

2.拖动刷新趋势图:

既然是趋势图,自然要能够实现拖动刷新趋势图,这会使用到PanListener接口。通过addPanListener方法可以为返回的图表添加面板事件监听器,一旦拖动趋势图,就会触发public void panApplied()方法,在该方法里,我们可以去处理数据的更新。而另外一点需要注意的是,我们需要确定从X轴哪一个位置处继续往后增加X label,而X label如何处理、如何获取也是需要我们考虑。在本例的实现过程中,以全局变量i为X轴的索引,这样,每次拖动时,只需要将Y坐标添加到指定的X索引坐标处即可,同时,也要进行X坐标轴自定义Label的更新,处理思路也是如此。

3.浮窗显示某点数值:

由于屏幕大小有限,所以有时候趋势图会很小,,所以,点击某点时,弹出浮窗以显示该点的数值就很有必要了。实现该功能最简单的方法就是,使用Framelayout布局,趋势图和浮窗分属不同的Framelayout。首先获取被点击的点的真实位置,其次获取被点击点附近最近的趋势线上的点所表示的值。前者需要为我们的图表添加OnTouchListener监听器,后者需要使用到SeriesSelection seriesSelection = mChartView.getCurrentSeriesAndPoint()以获取当前点击处附近趋势线上点的相关信息。获取相关信息后,就可以在Framelayout里使用自定义绘图的方式,绘制一个浮窗,并设置浮窗的位置,以及需要显示的信息。

以上是趋势图绘制过程中不得不考虑的几点,好了闲话不说了。关键代码如下:

主Activity代码:

package com.lamelias.achartengine;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);CustomFragment cf=new CustomFragment();getFragmentManager().beginTransaction().replace(R.id.trend_container, cf).commit();}} 放置趋势图的Fragment:/** * @Title: CustomFragment.java * @Package com.mbelec.flowmeter.fragment * @Description: TODO * @author Lamelias * @date 2014年10月20日 上午11:23:23 * @version V1.0 */package com.lamelias.achartengine;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Random;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 org.achartengine.tools.PanListener;import com.llamelias.achartengine.R;import android.app.Fragment;import android.graphics.Color;import android.graphics.Paint.Align;import android.os.Bundle;import android.util.Log;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.View.OnTouchListener;import android.widget.FrameLayout;/** * @ClassName: CustomFragment * @Description: 自定义趋势图,含自定义x轴label的使用 * @author Lamelias * @date 2014年10月20日 上午11:23:23 */public class CustomFragment extends Fragment {private static final String TAG = CustomFragment.class.getSimpleName();private FrameLayout layoutTendency, layoutCursor, layoutBg; // layoutBg 是第三个Framelayout,可用来设置坐标系不同区域的底色,暂未使用private XYMultipleSeriesDataset mDataset = new XYMultipleSeriesDataset();private XYMultipleSeriesRenderer mRenderer = new XYMultipleSeriesRenderer(2);private GraphicalView mChartView;private XYSeries pefSeries = new XYSeries("PEF");// 定义XYSeriesprivate XYSeries fevSeries = new XYSeries("FEV1",1);// 定义XYSeriesprivate SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");private Random rand = new Random();private Calendar c = Calendar.getInstance();private int i ; //自定义label时,X轴刻度索引@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View view = inflater.inflate(R.layout.trend_view_fragment, container,false);layoutTendency = (FrameLayout) view.findViewById(R.id.layout_tendency_chart);layoutCursor = (FrameLayout) view.findViewById(R.id.layout_tendency_cursor);layoutBg = (FrameLayout) view.findViewById(R.id.layout_tendency_bg);return view;}@Overridepublic void onResume() {super.onResume();renderSettings();if (mChartView == null) {mChartView = ChartFactory.getLineChartView(getActivity(), mDataset,mRenderer);layoutTendency.addView(mChartView);getTendencyView();//初始化趋势图;//添加PanListener监听器,监测拖动趋势图的事件.mChartView.addPanListener(new PanListener() {@Overridepublic void panApplied() {update();//拖动图表时更新趋势图}});mChartView.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:layoutCursor.removeAllViews();break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:SeriesSelection seriesSelection = mChartView.getCurrentSeriesAndPoint();//获取当前点击的点的相关信息if(seriesSelection!=null){PopupView pv=new PopupView(getActivity()); // 浮窗pv.setStartX(event.getX()); //设置浮窗的位置pv.setStartY(event.getY());pv.setValue(String.valueOf(seriesSelection.getValue())); //设置浮窗上显示的数值layoutCursor.addView(pv); //将浮窗添加到Framelayout}break;}Log.w(TAG, "X坐标:"+event.getX()+" Y坐标:"+event.getY());return false;}});}}/** * @Title renderSettings * @Description 渲染整个坐标系 * @return void */public void renderSettings() {mRenderer.setApplyBackgroundColor(true);// 设置是否显示背景色mRenderer.setBackgroundColor(Color.WHITE);// 设置背景色mRenderer.setMarginsColor(Color.parseColor("#f0f3f6"));mRenderer.setAxesColor(Color.parseColor("#000000"));mRenderer.setXLabelsColor(Color.BLACK);mRenderer.setYLabelsColor(0, Color.BLACK);mRenderer.setYTitle("PEF");mRenderer.setYLabelsAlign(Align.LEFT);mRenderer.setLabelsColor(Color.BLACK);mRenderer.setChartTitleTextSize(20);mRenderer.setAxisTitleTextSize(16); // 设置轴标题文字的大小mRenderer.setChartTitleTextSize(20);// 设置整个图表标题文字大小mRenderer.setLabelsTextSize(15);// 设置刻度显示文字的大小(XY轴都会被设置)mRenderer.setLegendTextSize(15);// 图例文字大小mRenderer.setMargins(new int[] { 20, 20, 0, 20 });// 设置图表的外边框(上/左/下/右)mRenderer.setPointSize(8);// 设置点的大小(图上显示的点的大小和图例中点的大小都会被设置)mRenderer.setShowCustomTextGrid(true);//设置是否显示自定义的刻度的网格竖线mRenderer.setPanEnabled(true, false);// 不可上下拖动,可水平拖动mRenderer.setAntialiasing(true);mRenderer.setFitLegend(true);// 调节图例至适当位置//mRenderer.setClickEnabled(true);// 设置图表是否允许点击mRenderer.setSelectableBuffer(40);// 设置点的缓冲半径值(在某点附近点击时,多大范围内算点击这个点)// 自定义标签mRenderer.setXLabels(0);mRenderer.setYLabels(10);mRenderer.setYAxisMax(600);mRenderer.setYAxisMin(200);/*设置第二条Y轴*/ mRenderer.setYLabelsColor(1, Color.BLACK);mRenderer.setYTitle("Fev1", 1);mRenderer.setYAxisMin(1, 1);mRenderer.setYAxisMax(10, 1);mRenderer.setYAxisAlign(Align.RIGHT, 1);mRenderer.setYLabelsPadding(-5);//设置label距离Y轴的水平距离mRenderer.setYLabelsAlign(Align.RIGHT, 1);}/** * @Title update * @Description 拖动时更新趋势图 * @return void */public void update(){mDataset.clear();for (int j=0; j <= 5; j++,i++) {pefSeries.add(i, 400+rand.nextInt(200));//构造pef趋势线上新的数据点集fevSeries.add(i, 1+rand.nextInt(7));//构造pef趋势线上新的数据点集mRenderer.addXTextLabel(i, sdf.format(c.getTime())); //构造X轴新的自定义Label,自定义label只是改变了显示的名称,X轴本质上依然是0,1,2·······c.add(Calendar.DAY_OF_YEAR, 1);//获得下一天}mDataset.addSeries(pefSeries);// 在XYMultipleSeriesDataset中添加pefSeriesmDataset.addSeries(fevSeries);// 在XYMultipleSeriesDataset中添加fevSeriesmChartView.repaint();//重绘 等同于invalidate(),只不过是把它封装了}public void getTendencyView() {mDataset.clear();mRenderer.removeAllRenderers();pefSeries.clear();fevSeries.clear();for (int j=0; j <= 5; j++,i++){pefSeries.add(i, 400+rand.nextInt(200));fevSeries.add(i, 1+rand.nextInt(7));mRenderer.addXTextLabel(i, sdf.format(c.getTime()));c.add(Calendar.DAY_OF_YEAR, 1);}mDataset.addSeries(pefSeries);// 在XYMultipleSeriesDataset中添加XYSeriesmDataset.addSeries(fevSeries);// 在XYMultipleSeriesDataset中添加XYSeriesXYSeriesRenderer lowRenderer = new XYSeriesRenderer();// 定义XYSeriesRenderermRenderer.addSeriesRenderer(lowRenderer);// 将单个XYSeriesRenderer增加到XYMultipleSeriesRendererlowRenderer.setPointStyle(PointStyle.CIRCLE);// 点的类型是圆形lowRenderer.setFillPoints(true);// 设置点是否实心lowRenderer.setColor(Color.GREEN);XYSeriesRenderer highRenderer = new XYSeriesRenderer();// 定义XYSeriesRenderermRenderer.addSeriesRenderer(highRenderer);// 将单个XYSeriesRenderer增加到XYMultipleSeriesRendererhighRenderer.setPointStyle(PointStyle.CIRCLE);// 点的类型是圆形highRenderer.setFillPoints(true);// 设置点是否实心mChartView.repaint();}}自定义浮窗视图类:/** * @Title: PopupView.java * @Package com.mbelec.flowmeter.widget * @Description: 浮窗 * @author Lamelias * @date 2014年10月17日 下午1:53:08 * @version V1.0 */package com.lamelias.achartengine;import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.view.View;/** * @ClassName: PopupView * @Description: 弹窗 * @author Lamelias * @date 2014年10月17日 下午1:53:08 */public class PopupView extends View {private static final String TAG = PopupView.class.getSimpleName();private float startX; // 浮窗起始绘制位置X坐标private float startY; // 浮窗起始绘制位置Y坐标private String value; // 浮窗显示的点的值private Paint textPaint=new Paint();// 字体画笔private Paint paint=new Paint();public PopupView(Context context) {super(context);}@Overrideprotected void onDraw(Canvas canvas) {renderPaint();Resources rec = getResources();BitmapDrawable bitmapDrawable = (BitmapDrawable) rec.getDrawable(R.drawable.popup);Bitmap bitmap = bitmapDrawable.getBitmap();canvas.drawBitmap(bitmap, startX-bitmap.getWidth()/2, startY, null);canvas.drawText(value, startX-bitmap.getWidth()/4, startY+bitmap.getHeight()*2/3, textPaint);super.onDraw(canvas);}/** * @Title renderPaint * @Description 渲染画笔 * @return void */public void renderPaint(){paint.setColor(Color.parseColor("#97aca5"));paint.setStyle(Style.FILL);textPaint.setColor(Color.parseColor("#40a9b7"));textPaint.setTextSize(25);textPaint.setStyle(Style.FILL);}public float getStartX() {return startX;}public void setStartX(float startX) {this.startX = startX;}public float getStartY() {return startY;}public void setStartY(float startY) {this.startY = startY;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}}完整工程代码下载链接:,代码中已包含基准线,基准线的图例可以自行去掉

别为荒漠的艰难而哭泣,只为奔流入海功成名就那一天,

利用Achartengine制作趋势图(含双Y轴,浮窗)

相关文章:

你感兴趣的文章:

标签云: