数据可视化工作,安卓图表库的应用

作者:u012591761

公司的日报系统中,有个查看报表功能。但是现在只能查看表格形式的数据,不直观。

这次的工作就是将数据画成图表,数据可视化工作。


开源图表库项目简介


之前做web开发时,有一个项目是vo,visual office,给客户网上办公用的。里面也涉及到数据可视化的工作,那时候了解到了有d3.js这样一个图表库。那么安卓上有没有类似的图表库呢?
我上github上找了下,找到了如下:
1.MPAndroidChart
https://github.com/PhilJay/MPAndroidChart
用的比较多的一个库。支持Android 2.2以上的项目。
2.achartengine
http://www.achartengine.org/
比较老的一个库了,这里有教程http://blog.csdn.net/lk_blog/article/details/7645509
3.XCL-Charts
https://github.com/xcltapestry/XCL-Charts
XCL-Charts基于Android原生Canvas来绘制各种图表,使用简便,定制灵活。
4.WilliamChart
https://github.com/diogobernardino/WilliamChart
绘制图表的库,支持LineChartView、BarChartView和StackBarChartView三中图表类型,并且支持 Android 2.2及以上的系统。有很漂亮的动画。
5.HelloCharts for Android
https://github.com/lecho/hellocharts-android
支持折线图、柱状图、饼图、气泡图、组合图;支持预览、放大缩小,滚动,部分图表支持动画;支持 Android 2.2 以上。

图表库的应用


1.导入jar包

在eclipse中导入MPChartLib之后,build一下,拿到bin文件夹下的jar包。

将jar包放到目标项目的lib文件夹下,右击add to build path

2.柱状图的绘画

创建一个ReportChartsActivity,用来进行数据可视化的处理。为其创建界面activity_report_chart.xml如图

设置全屏

		requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置全屏
		setContentView(R.layout.activity_report_chart);

设置横屏

		@Override
		protected void onResume() {
		 if(getRequestedOrientation()!=ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
		  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		 }
		 super.onResume();
		}

图表初始化

	private void initChart() {
	// TODO Auto-generated method stub
	//混合柱状图的初始化
        arChart = (BarChart) findViewById(R.id.chart_bar);
        barChart.setDrawBarShadow(false);
        barChart.setDrawGridBackground(false);
<span style="white-space:pre">	</span>barChart.setDrawHorizontalGrid(false);
        barChart.setDrawYValues(true);			//画出X,Y坐标系
	barChart.setDescription("");			//说明
	barChart.setMaxVisibleValueCount(40);	//一屏超过25列时不显示具体数值,设置超过60无效
	MyValueFormatter valueformatter = new MyValueFormatter();
	barChart.setValueFormatter(valueformatter); //设置自定义格式化方式
//	barChart.setValueFormatter(new LargeValueFormatter());
	barChart.setDrawValuesForWholeStack(false);  //是否显示每部分的数字,false则为显示和
	barChart.set3DEnabled(false); 			//3D视图
	barChart.setPinchZoom(false);   		//是否只能根据X,Y轴放大缩小
	barChart.setDrawBarShadow(false);		//设置该列空白部分是否用灰色补全
	
		//X,Y轴设定
	YLabels yLabels = barChart.getYLabels();
<span style="white-space:pre">	</span>yLabels.setPosition(YLabelPosition.LEFT);
        yLabels.setLabelCount(5);
        yLabels.setFormatter(valueformatter);
        XLabels xLabels = barChart.getXLabels();
        xLabels.setPosition(XLabelPosition.BOTTOM);
        xLabels.setCenterXLabelText(true);
        
	}

取得数据

在活动开始的时候,就向服务器发送一个请求,然后我们得到返回的json数据。
	private void init() {
		handler=new ReportDetailJsonHandler();
		formUtil = new GetServerReportInfoUtil(handler, this);
		startMonth=getIntent().getExtras().getString("startMonth");
		endMonth=getIntent().getExtras().getString("endMonth");
		currentYear=getIntent().getExtras().getString("currentYear");
		formUtil.getJsonInfoIdFromServer(startMonth,endMonth ,currentYear);
	}

	class ReportDetailJsonHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			switch (msg.what) {
				...
				...
				...
			case HandlerCASE.MSG_DONE_SECOND:
				for (ReportDetailInfoBean bean : formUtil.getResponseForm()
						.getData()) {
					if (!"部门名称".equals(bean.getAttr1())) {
						dataList.add(bean);
					}
				}
				drawChart();
				break;
			case HandlerCASE.MSG_NODATA:
				...
				...
				...
			default:
				break;
			}
		}
	}

至此我们得到了List<ReportDetailInfoBean> dataList

画出图表

然后根据dataList画出我们要的图表。于是调用drawChart().
	private void drawChart() {
		// TODO Auto-generated method stub
		int StartMonth = Integer.valueOf(startMonth); //开始月份
		int SumMonth = Integer.valueOf(endMonth) - StartMonth + 1;		//总月份
		name = new ArrayList<String>();
		yVals = new ArrayList<ArrayList<BarEntry>>();
		
		for(int n = 0; n < SumMonth; n++){
			yVals.add(new ArrayList<BarEntry>());
		}
		name.add(dataList.get(0).getAttr1());
		for (int i = 0; i < dataList.size(); i++) {
			ReportDetailInfoBean bean = dataList.get(i);
			if(!name.get(name.size() - 1).equals(bean.getAttr1())){
				name.add(bean.getAttr1());
			}
		}
		
		int month=0;
		int barIndex=0;
		int dataIndex=0;
		for (int i = 0; i < name.size()*SumMonth; i++) {
			ReportDetailInfoBean bean = dataList.get(dataIndex);
			if (month>=SumMonth) {
				month=0;
				barIndex++;
			}
			if (month+StartMonth==Integer.valueOf(bean.getAttr2())) {
				yVals.get(month).add(new BarEntry(Form(bean), barIndex));
				dataIndex++;
			} else {
				yVals.get(month).add(new BarEntry(new float[] {0,0,0}, barIndex));
			}
			month++;
		}
		
		ArrayList<BarDataSet> dataSets = new ArrayList<BarDataSet>();
		for(int n = 0; n < SumMonth; n++){
			BarDataSet set = new BarDataSet(yVals.get(n), (StartMonth + n) + "月数据");
			set.setStackLabels(new String[] {"已完成", "进行中", "未开始"});
			set.setColors(colorList.get(n));
			dataSets.add(set);
		}
		
		BarData data = new BarData(name, dataSets);
        	data.setGroupSpace(110f);
        
       		barChart.setData(data);
        	barChart.animateY(500);
        	barChart.invalidate();
        	initPieChart();
        
	//BarData的各个属性,未设置StackLables直接设置此项会报空指针异常
        Legend legend = barChart.getLegend();
        legend.setPosition(LegendPosition.RIGHT_OF_CHART_INSIDE);
        legend.setFormSize(8f);
        legend.setTextSize(8f);
        legend.setFormToTextSpace(4f);
        legend.setXEntrySpace(6f);
	}

效果图:




3.饼状图的绘画

我们用的是堆积条形图,现在我们添加一个功能在:在单击柱状图的时候,弹出饼状图现实各个部分所占比例。

设置监听器

在initChart()中为barChart设置单击事件
	barChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
			
			@Override
			public void onValueSelected(Entry entry, int barIndex) {
				// TODO Auto-generated method stub
				if (entry == null)
		            return;
				float[] vals = ((BarEntry)entry).getVals();
				String dev = name.get(entry.getXIndex());
				String month = (Integer.valueOf(startMonth) + barIndex) + "";
				ArrayList<String> choosen = new ArrayList<String>();
				ArrayList<Entry> yVals = new ArrayList<Entry>();
				choosen.add("已完成");
				choosen.add("进行中");
				choosen.add("未开始");
				for(int n = 0; n < vals.length; n++){
					if (vals[n]!=0) {
						yVals.add(new Entry(vals[n], n));
					}
				}
				pieChart.setCenterText(dev + "\n" + month + "月数据一览");
				PieDataSet dataSet = new PieDataSet(yVals, "");
				dataSet.setColors(ColorTemplate.APRIL_COLORS);
				PieData data = new PieData(choosen, dataSet);
				pieChart.setData(data);
				pieChart.invalidate();				
				Legend legend = pieChart.getLegend();		//说明模块设置
		        legend.setPosition(LegendPosition.RIGHT_OF_CHART);
		        legend.setTextColor(Color.WHITE);
		        legend.setXEntrySpace(7f);
		        legend.setYEntrySpace(5f);
			piechart.showAtLocation((View)findViewById(R.id.chart), Gravity.CENTER, 0, 0);
		}

初始化饼状图

可以看到刚刚的代码中,在drawChart()中有个initPieChart(),这个就是初始化饼状图的方法。
	private void initPieChart(){
		//PieChart的PopupWindow初始化
        View view = ReportChartsActivity.this.getLayoutInflater()
        		.inflate(R.layout.popup_report_chart_pie, null);
        
        piechart = new PopupWindow(view, fins, fins);
        piechart.setFocusable(true);
        piechart.setOutsideTouchable(false);
        piechart.setBackgroundDrawable(new BitmapDrawable());
        piechart.setAnimationStyle(R.style.AnimationFade);
        pieChart = (PieChart) view.findViewById(R.id.chart_pie);
        pieChart.setHoleRadius(60f);
        pieChart.setDescription("");
        pieChart.setDrawYValues(true);		
        pieChart.setDrawCenterText(true);	//中间字
        pieChart.setDrawHoleEnabled(true);
        pieChart.setRotationAngle(0);
        pieChart.setDrawXValues(true);		//画出各部分说明
        pieChart.setRotationEnabled(false);	//可旋转
        pieChart.setUsePercentValues(false);	//显示百分数
	}

效果图:




4.折线图的绘画

柱状图饼状图都有了,现在我们要查看单个部门的数据趋势。这个时候用折线图来处理吧。
入口怎么处理呢?用OptionsMenu吧。
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// TODO Auto-generated method stub
		getMenuInflater().inflate(R.menu.chart, menu);
		return super.onCreateOptionsMenu(menu);
	}
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// TODO Auto-generated method stub
        int id = item.getItemId();
        if (id == R.id.action_chart) {
        	Intent intent=new Intent(ReportChartsActivity.this, ReportDetialChartsActivity.class);
			Bundle detailData = new Bundle();
			detailData.putString("reportDetailTitle", "部门计划折线图");
			detailData.putSerializable("departmentName", name);
			detailData.putSerializable("dataList",  (Serializable) dataList);
			intent.putExtras(detailData);
        	startActivity(intent);
            return true;
        }
		return super.onOptionsItemSelected(item);
	}

我们要新写一个yReportDetialChartsActivit活动,然后再写出对应的界面,由于步骤差不多就略过了。这里可以看到我将ReportChartsActivity中已经取得的数据直接塞到Intent中带给下个活动了,这样ReportDetialChartsActivity中我就不再需要发送请求来取得dataList了。
布局是这样的:

图表上方添加一个下拉菜单选择部门,然后下方显示对应部门的折线图。

初始化数据

	private void init() {
		String titleText = getIntent().getExtras().getString("reportDetailTitle");
		name=(ArrayList<String>) getIntent().getExtras().getSerializable("departmentName");
		dataList=(List<ReportDetailInfoBean>) getIntent().getExtras().getSerializable("dataList");
	}

初始化折线图

	private void initChart() {
		// TODO Auto-generated method stub
        lineChart = (LineChart) findViewById(R.id.chart1);
        lineChart.setOnChartValueSelectedListener(this);
        //是否显示网格线
        lineChart.setDrawGridBackground(false);
        // mChart.setStartAtZero(true);
        // disable the drawing of values into the chart 画出X,Y坐标系
        lineChart.setDrawYValues(true);
        lineChart.setMaxVisibleValueCount(30);
        // enable value highlighting
        lineChart.setHighlightEnabled(true);
        lineChart.setHighlightIndicatorEnabled(false);
        // enable touch gestures
        lineChart.setTouchEnabled(true);
        // enable scaling and dragging
        lineChart.setDragEnabled(true);
        lineChart.setScaleEnabled(true);
        // if disabled, scaling can be done on x- and y-axis separately 是否只能根据X,Y轴放大缩小
        lineChart.setPinchZoom(false);
        lineChart.setDescription("");
        // create a custom MarkerView (extend MarkerView) and specify the layout
        // to use for it
        MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view);
        // define an offset to change the original position of the marker
        // (optional)
        mv.setOffsets(-mv.getMeasuredWidth() / 2, -mv.getMeasuredHeight());
        // set the marker to the chart
        lineChart.setMarkerView(mv);
        dep_tv.setText(name.get(0));
        setData(name.get(0));
	}

设置数据

		private void setData(String department){
			ArrayList<String> xVals = new ArrayList<String>();
			ArrayList<LineDataSet> dataSets = new ArrayList<LineDataSet>();
			ArrayList<Entry> values1 = new ArrayList<Entry>();
			ArrayList<Entry> values2 = new ArrayList<Entry>();
			ArrayList<Entry> values3 = new ArrayList<Entry>();
			for(int i = 0; i < dataList.size(); i++){
				ReportDetailInfoBean reportDetail = dataList.get(i);
				if(reportDetail.getAttr1().equals(department)){
					xVals.add(reportDetail.getAttr2());
					values1.add(new Entry(Integer.valueOf(reportDetail.getAttr4()), xVals.size() - 1));
					values2.add(new Entry(Integer.valueOf(reportDetail.getAttr5()), xVals.size() - 1));
					values3.add(new Entry(Integer.valueOf(reportDetail.getAttr3()), xVals.size() - 1));
				}
			}
			LineDataSet d1 = new LineDataSet(values1, "已完成");
			LineDataSet d2 = new LineDataSet(values2, "进行中");
			LineDataSet d3 = new LineDataSet(values3, "总任务");
			d1.setColor(ColorTemplate.VORDIPLOM_COLORS[2]);
			d1.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[2]);
			d1.setLineWidth(2.5f);
			d1.setCircleSize(4f);
			d2.setColor(ColorTemplate.VORDIPLOM_COLORS[3]);
			d2.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[3]);
			d2.setLineWidth(2.5f);
			d2.setCircleSize(4f);
			d3.setColor(ColorTemplate.VORDIPLOM_COLORS[4]);
			d3.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[4]);
			d3.setLineWidth(2.5f);
			d3.setCircleSize(4f);
			
			dataSets.add(d1);
			dataSets.add(d2);
			dataSets.add(d3);
	
	        LineData data = new LineData(xVals, dataSets);
	        lineChart.setData(data);
	        lineChart.animateY(500);
	        lineChart.invalidate();
		}


设置markView

	class MyMarkerView extends MarkerView {
	    private TextView tvContent;
	    public MyMarkerView(Context context, int layoutResource) {
	        super(context, layoutResource);
	        tvContent = (TextView) findViewById(R.id.tvContent);
	    }
	    // callbacks everytime the MarkerView is redrawn, can be used to update the
	    // content
	    @Override
	    public void refreshContent(Entry e, int dataSetIndex) {
	        if (e instanceof CandleEntry) {
	            CandleEntry ce = (CandleEntry) e;
	            tvContent.setText("" + Utils.formatNumber(ce.getHigh(), 0, true));
	        } else {
	            tvContent.setText("" + Utils.formatNumber(e.getVal(), 0, true));
	        }
	    }
	}

效果图:

实现部门选择

在init()方法中,设置部门的单击事件。
	private void init() {
		String titleText = getIntent().getExtras().getString("reportDetailTitle");
		name=(ArrayList<String>) getIntent().getExtras().getSerializable("departmentName");
		dataList=(List<ReportDetailInfoBean>) getIntent().getExtras().getSerializable("dataList");
		
		setTitleBarText(titleText);
		initTitleBarBack();
		
		dep_lin = (LinearLayout) findViewById(R.id.dep_lin);
		dep_img = (ImageView) findViewById(R.id.dep_img);
		dep_tv = (TextView) findViewById(R.id.dep_tv);
		dep_tv.setText(name.get(0));
		View layout_area = LayoutInflater.from(ReportDetialChartsActivity.this).inflate(
				R.layout.pop_list, null);
		ListView listview = (ListView) layout_area.findViewById(R.id.listView1);
		listview.setAdapter(getMenuAdapter(name));
		listview.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
					long arg3) {
				menu_dep.dismiss();
				currentDep = name.get(arg2);
				dep_tv.setText(currentDep);
				setData(currentDep);
			}
		});


		menu_dep = new PopupWindow(layout_area,
				WindowManager.LayoutParams.MATCH_PARENT,
				WindowManager.LayoutParams.WRAP_CONTENT);
		menu_dep.setFocusable(true);


		menu_dep.setBackgroundDrawable(getResources().getDrawable(
				android.R.color.transparent));
		menu_dep.setOnDismissListener(new OnDismissListener() {


			@Override
			public void onDismiss() {
				dep_img.setBackgroundResource(R.drawable.arrow_blue_down);
				dep_tv.setTextColor(getResources().getColor(
						R.color.textcontentcolor));
			}
		});
		menu_dep.setAnimationStyle(R.style.AnimationFade);
		
		dep_lin.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				menu_dep.showAsDropDown(dep_lin);
				dep_img.setBackgroundResource(R.drawable.arrow_blue_up);
			}
		});
	}
效果图

折线图效果


完成效果图


发表评论

0个评论

我要留言×

技术领域:

我要留言×

留言成功,我们将在审核后加至投票列表中!

提示x

知识工程知识库已成功保存至我的图谱现在你可以用它来管理自己的知识内容了

删除图谱提示×

你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?

删除节点提示×

无法删除该知识节点,因该节点下仍保存有相关知识内容!

删除节点提示×

你确定要删除该知识节点吗?