Android开发中的AchartEngine及ListView

来源:互联网 发布:python sap 编辑:程序博客网 时间:2024/04/30 15:13

       在开发Android应用的过程中,常需要将数据库中的数据进行可视化显示,比较常用的显示方式有两种:图和表。这里将对图形库AchartEngine以及列表视图ListView进行说明,以期对您的项目有所帮助。

        一、AchartEngine的原理

就我的使用体会来讲,AchartEngine是将数据与图像分离,分别用XYMultipleSeriesDataset与XYMultipleSeriesRenderer来进行封装。XYMultipleSeriesDataset中可以添加各种类型的Series(如RangeCategorySeries,XYSeries等),而XYMultipleSeriesRenderer可以添加各种SeriesRenderer(如SimpleSeriesRenderer,XYSeriesRenderer等),然后通过AchartEngine的工厂类得到相应的图形View或者Intent。

         这里举一例进行说明,先给出一张截图如下:

上图是两种图形的叠加,即SimpleSeriesRenderer与XYSeriesRenderer。SimpleSeriesRenderer负责三个柱线图的显示,XYSeriesRenderer负责折线图的显示。

首先,获取两类图形的数据。柱线图要表示出最大值与最小值,用数组来表示。

// range graphdouble[] minValues = new double[LabelsGraphWhichDays.size()];double[] maxValues = new double[LabelsGraphWhichDays.size()];
当然,minValues的值一般来自数据库SQLite,这里需要对数据库进行读取(数据库文件、表的设立需要根据使用情况灵活对待,这里不重点讲述数据库了),读取方式不再赘述。由图形即可推测,上述数组长度为3,minValues的数据分别为3.1,6.1,13.8,maxValues的值类似。

得到柱线图的数据之后,需要获取折线图的数据,折线图的数据包含x轴坐标和y轴坐标。

double[] xData = new double[xList.size()];double[] yData = new double[yList.size()];
其中,xData分别为1,2,3,yData分别为3.1, 12.5,13.8。

得到数据以后,将数据添加到相应的Series。

RangeCategorySeries series0 = new RangeCategorySeries("Title");int length = minValues.length;for (int k = 0; k < length; k++) {BigDecimal min = new BigDecimal(minValues[k]); BigDecimal max = new BigDecimal(maxValues[k]);double minValue = min.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();double maxValue = max.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();//RangeCategorySeriesseries0.add(minValue, maxValue);}
XYSeries series1 = new XYSeries("Title");for(int i = 0; i<xData.length; i++){     //series1     series1.add(xHungryData[i],yHungryData[i]);}

得到两个含有数据的Series后,只需要将两个Series分别添加至XYMultipleSeriesDataset即可。

XYMultipleSeriesDataset multipleSeriesDataset = new XYMultipleSeriesDataset();// addSeriesmultipleSeriesDataset.addSeries(0, series1);multipleSeriesDataset.addSeries(0, series0.toXYSeries());

然后,设置相应的SeriesRenderer,即图形显示。

与上述设置数据的步骤类似,设置图形也需要从SeriesRenderer开始。对于柱线图的RangeCategorySeries,采用SimpleSeriesRenderer来显示,而对于XYSeries,也就用XYSeriesRenderer来显示。

// SeriesRenderer RangeSimpleSeriesRenderer renderer0 = new SimpleSeriesRenderer();renderer0.setDisplayChartValues(true);renderer0.setChartValuesTextSize(16);renderer0.setChartValuesSpacing(3);renderer0.setChartValuesTextAlign(Align.CENTER);renderer0.setColor(Color.rgb(0, 220, 173));renderer0.setGradientEnabled(false);// SeriesRenderer XYXYSeriesRenderer renderer1 = new XYSeriesRenderer();renderer1.setColor(Color.rgb(255, 7, 87));renderer1.setDisplayChartValues(false);renderer1.setLineWidth(2);// addSeriesRenderermultipleSeriesRenderer.addSeriesRenderer(0, renderer1);multipleSeriesRenderer.addSeriesRenderer(0, renderer0);
对于上述图形,还需要对multipleSeriesRenderer进行一些设置,这里不再详细说明。

最后,只需一步,即可完成。即通过工厂类获得相应的图形View。

String[] types = new String[] {RangeBarChart.TYPE, LineChart.TYPE};// displayView = ChartFactory.getCombinedXYChartView(getActivity(), multipleSeriesDataset, multipleSeriesRenderer, types);
获取displayView后,就可以将其在Layout中进行显示,得到上述图形。

细心的朋友会发现这里还有对X轴标签的设置。在对标签设置时,需要首先将原始的标签屏蔽掉。

//set X labelmultipleSeriesRenderer.setXLabels(0);multipleSeriesRenderer.setXLabelsColor(Color.BLACK);
然后添加自定义的标签。

for(int i = 0; i < LabelsGraphWhichDays.size(); i++){     multipleSeriesRenderer.addXTextLabel(i+1, xDateLabel[i] + "\n" + xTimesLabel[i]);}

好啦,大功告成。

二、ListView解析

在需要以列表显示大量信息时,ListView应该是一个很常用的View。ListView属于AdapterView,即在使用ListView时,需要为ListView配置一个Adapter。Adapter常见的实现类有ArrayAdapter,SimpleAdapter,SimpleCursorAdapter,BaseAdapter。ArrayAdapter属于简单、易用的Adapter,通常用于将数组或者List集合的多个值包装成列表项;SimpleAdapter功能非常强大,可用于将List集合的多个对象包装成多个列表项;SimpleCursorAdapter与SimpleAdapter不同的是,它用于包装Cursor提供的数据;BaseAdapter通常用于被扩展,扩展BaseAdapter可以对列表项进行最大限度的定制。

这里重点说明SimpleAdapter以及BaseAdapter。

SimpleAdapter可以将列表的每一项都采用一个布局文件来显示,因此,列表项的显示就可以很丰富(因为可以自定义显示内容以及布局)。SimpleAdapter的教程较多,这里不详细说明,只针对SimpleAdapter的创建进行阐述。

SimpleAdapter simpleAdapter = new SimpleAdapter(this, listItems, R.layout.simple_item, new String[]{"Name", "ID", "Gender"},new int[]{R.id.name, R.id.id, R.id.gender});
this是指代上下文,即Context,可能在某些应用中,这些需要用到getActivity()。listItems是一个重点参数,包含该Adapter的数据来源。它的格式应该是List<? extends Map<String, ?>>,该集合中每个Map<String, ?>对象将生产一个列表。R.layout.simple_item指定一个布局界面的ID,即用于在ListView中的每个Item的显示的布局,此布局根据自定义而实现SimpleAdapter的丰富显示。后两个数组中,String数组决定提取Map<String, ?>对象中哪些key对应的value值来生产列表项。int数组就是布局界面上组件的ID,决定哪些组件被填充。

建立此SimpleAdapter之后,将此Adapter应用某个ListView即可。

可能,你已经觉得SimpleAdapter已经足够强大,可以实现丰富的显示,但是,在很多对列表有个性化要求的场合,比如需要根据需要对不同的列表项进行颜色区分,SimpleAdapter依然显得不够用,这时,你就需要考虑扩展BaseAdapter了,好吧,重点来了。请先看下图:


如果某一天,你突然被要求实现上图中的效果,你会打算怎么实现呢?(请忽略图中的上部分的图形Title,只关注中间的数据列表)。说明一下,上述数据列表首先根据左侧的时间进行分栏,不同天之间用不同 颜色背景区分(比如这里18/04的背景颜色是Color.argb(100, 226, 215, 247),19/04的背景颜色是Color.argb(100, 243, 241, 250)),其次,在18/04的显示中,第四栏的背景颜色为Color.argb(100, 0xff, 00 , 00)。

好啦,说说实现方案吧,这里采用了两层扩展的BaseAdapter,外层BaseAdapter负责每一天的所有显示,即上图中的外层BaseAdapter的getCount()将返回2,分别为18/04和19/04。外层BaseAdapter的getView()方法返回的View的布局包括左侧的文本TextView(显示18/04)和右侧的ListView,这里即是ListView的嵌套。内层BaseAdapter的getCount()方法将根据每天的数据多少来决定,在上图中18/04为4,19/04为1。其getView()方法将返回每一行的从时间到最后的所有信息。其中的红色背景的那一行则根据对本行显示的数据的判断来决定。

具体实现时,首先获取外层ListView,设置其Adapter为adapterForListOuter。

LinearLayout history_data_list_layout = (LinearLayout) getActivity().getLayoutInflater().inflate(R.layout.history_data_list, null);ListView history_data_list = (ListView) history_data_list_layout.findViewById(R.id.myList);history_data_list.setAdapter(adapterForListOuter);
在adapterForListOuter的getView()方法中,获取内层ListView,并为其设置adapterForListInner。

ListView history_data_list_content = (ListView) history_data_list_outer.findViewById(R.id.history_data_list_content);// Set the data displayTextView history_data_list_date = (TextView) history_data_list_outer.findViewById(R.id.history_data_list_date);history_data_list_date.setText(history_data_list_date_display[position]);// Update outer positionhistory_data_list_content.setAdapter(adapterForListInner);
在adapterForListInner的getView()方法中,首先获取显示信息的行的布局。

if (convertView == null) {history_data_list_inner = getActivity().getLayoutInflater().inflate(R.layout.history_data_list_cell, parent, false);} else {history_data_list_inner = convertView;}
然后,对每一行的数据以及显示背景进行设置就可以了。

三、两个其他的问题。

在实现过程中,遇到两个其他的问题,这里指出,供交流。

第一:ListView中每一项的高度问题。在进行内层List显示时,出现过只能显示一行的情况,即上述18/04本来有四行,结果只能显示第一行,经调试,发现原因在于外层List的每一列表项的高度设置。这里需要根据显示的内容对其进行最小高度的设置。

history_data_list_outer.setMinimumHeight(thisDayTimes * 42 + history_data_list_content.getDividerHeight() * (adapterForListInner.getCount() - 1));
stackoverflow上面有对此问题的描述,具体分析可搜索stackoverflow。

第二:在进行图形及列表的绘制时,经常需要用到参数的传递。在传递过程中,可能会出现一种情况如下:

ObjectA objectA = new ObjectA();ObjectB objectB = new ObjectB();objectB.add(objectA);objectA.clear();
objectA传递给objectB的只是对对象ObejectA的一个引用(reference),将objectA清除掉以后,objectB中持有的对ObejectA的引用也随之清除,并不能达到将objectA的内容(value)传递给objectB。(这个问题是Java中基本的对象、引用、参数传递的问题,可在开发过程中却常犯的一个错误)不过对于基本数据类型,则不存在此问题。如int a = 1; int b =2; a = b; 之后即使将b clear, a 的值依然为2,因为这里不存在引用的问题(Integer则与此不同)。





很多时候,浮躁源于我们的眼睛看外界太多,看内心太少。

0 0
原创粉丝点击