前言:对于学生的答题(学习)情况,数据是直观的表现,在实际开发中也需要涉及到大量的数据统计与分析,我们经常通过图表结合来更加直观的展现数据。
接下来,记录一下最近做的一个错题率统计与总成绩排行。整个界面是由上半部分的柱形图来展现错题记录,下半部分则是已提交(答题)人员的成绩排名。主要涉及到的几个点就是调用接口获取后台数据、数据的整理(排序)、数据的展现(包括柱形图与排行列表),我们按照开发的流程来了解。
第一步,界面设计
界面布局比较简单,直接贴代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.administrator.parentproject.activity.TJActivity"> <RelativeLayout android:id="@+id/relative" style="@style/TitleStyle_Relative"> <ImageView android:id="@+id/back" style="@style/TitleStyle_Back" android:layout_alignParentBottom="true" /> <TextView android:id="@+id/title_tv" style="@style/TitleStyle_Text" android:layout_centerHorizontal="true" android:singleLine="true" android:text="错题率" /> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="6" android:orientation="horizontal" android:layout_margin="@dimen/dp_10" android:background="@color/pink"> <com.github.mikephil.charting.charts.BarChart android:id="@+id/bar_chart" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="@dimen/dp_20"/> </LinearLayout> <LinearLayout android:layout_gravity="bottom" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="5" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:id="@+id/zcjph"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/dp_10" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="@color/pink" android:gravity="center_horizontal" android:text="姓名"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="@color/pink" android:gravity="center_horizontal" android:text="排名"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="@color/pink" android:gravity="center_horizontal" android:text="成绩"/> </LinearLayout> <ListView android:id="@+id/zcjph_lv" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="@null" android:paddingTop="@dimen/dp_10" /> </LinearLayout> </LinearLayout> </LinearLayout>
柱形图使用第三方库MPChartLib实现,这个库支持使用简单支持的样式较多,只不过如果直接导入到项目中的话会比较大,因此建议有时间的话还是熟悉一下代码,从而可以减去无关的代码和资源。这里附上一个网址方便大家熟悉https://blog.csdn.net/guijiaoba/article/details/41444697。
第二步,获取后台数据
我这里起两个请求,一个获取错题率另外一个则获取排名,共同调用一个方法,根据flag区分,
private void httpPost(final String flag) { if (flag.equals("tj")) { commitParam = new CommitParam(); commitParam.setToken(token); commitParam.setId(classPID); commitParam.setClassPid(id); map = commitParam.getData(); data = RetrofitBean.getApiApi().getDataExam("get_papers_tongji",map); } if (flag.equals("score")) { data = RetrofitBean.getApiApi().getZYList("get_student_post_papers", token, id, "score"); } data.enqueue(new SimpleCallBack(flag) { @Override protected void showData(int i, String json) { super.showData(i, json); Gson gson = new Gson(); if (flag.equals("tj")) { String result = ""; try { JSONObject jsonObject = new JSONObject(json); result = jsonObject.getString("result"); JSONObject rstJson = new JSONObject(result); Iterator it = rstJson.keys(); Map map = new TreeMap(); while (it.hasNext()) { String key = it.next().toString(); String value = rstJson.getString(key); map.put(key, value); } Iterator it1 = map.keySet().iterator(); String s = ""; while (it1.hasNext()) { //it.next()得到的是key,tm.get(key)得到obj Object obj = it1.next(); s += obj + "-" + map.get(obj).toString() + "+"; } Log.e("map",s+""); setDataChart(map); } catch (JSONException e) { e.printStackTrace(); } } if (flag.equals("score")) { mDataPg.clear(); PGZY pgzy = gson.fromJson(json, PGZY.class); mDataPg.addAll(pgzy.getResult()); myListViewAdapter = new MyListViewAdapter(getBaseContext(), mDataPg, "zcjph",0); zcjphLv.setAdapter(myListViewAdapter); myListViewAdapter.notifyDataSetChanged(); } } }); }
第三步,渲染柱形图数据
private void setDataChart(Map map) { //mBarData=getData(4,100); mChart.setOnChartValueSelectedListener(this); mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(true); mChart.getDescription().setEnabled(false); mChart.setMaxVisibleValueCount(100); mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); IAxisValueFormatter xAxisFormatter = new TmAxisValueFormatter(mChart, map); XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); //xAxis.setTypeface(mTfLight); xAxis.setDrawGridLines(false); xAxis.setGranularity(1f); // only intervals of 1 day xAxis.setLabelCount(map.size()); xAxis.setValueFormatter(xAxisFormatter); IAxisValueFormatter custom = new MyAxisValueFormatter(); YAxis leftAxis = mChart.getAxisLeft(); //leftAxis.setTypeface(mTfLight); leftAxis.setLabelCount(8, false); leftAxis.setValueFormatter(custom); leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART); leftAxis.setSpaceTop(15f); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) leftAxis.setAxisMaximum(100f); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); //rightAxis.setTypeface(mTfLight); rightAxis.setLabelCount(8, false); rightAxis.setValueFormatter(custom); rightAxis.setSpaceTop(15f); rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) rightAxis.setAxisMaximum(100f); Legend l = mChart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); l.setDrawInside(false); l.setForm(Legend.LegendForm.SQUARE); l.setFormSize(9f); l.setTextSize(11f); l.setXEntrySpace(4f); XYMarkerView mv = new XYMarkerView(this, xAxisFormatter); mv.setChartView(mChart); // For bounds control mChart.setMarker(mv); setDataChartM(map); mChart.invalidate(); } private void setDataChartM(Map map) { { float start = 1f; ArrayList<BarEntry> yVals1 = new ArrayList<BarEntry>(); Iterator it1 = map.keySet().iterator(); while (it1.hasNext()) { //it.next()得到的是key,tm.get(key)得到obj yVals1.add(new BarEntry(start, Float.parseFloat(map.get(it1.next()).toString()))); start += 1; } BarDataSet set1; if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); set1.setValues(yVals1); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "错题记录"); set1.setDrawIcons(false); set1.setColors(ColorTemplate.MATERIAL_COLOR1); ArrayList<IBarDataSet> dataSets = new ArrayList<IBarDataSet>(); dataSets.add(set1); BarData data = new BarData(dataSets); data.setValueTextSize(10f); //data.setValueTypeface(mTfLight); data.setBarWidth(0.1f); mChart.setData(data); } } }
第四步,定义XY轴的格式
public class TmAxisValueFormatter implements IAxisValueFormatter { private BarLineChartBase<?> chart; private Map map; public TmAxisValueFormatter(BarLineChartBase<?> chart) { this.chart = chart; } public TmAxisValueFormatter(BarLineChartBase<?> chart, Map map) { this.chart = chart; this.map = map; } @Override public String getFormattedValue(float value, AxisBase axis) { Iterator iterator=map.keySet().iterator(); int i =0; String vu = ""; while (iterator.hasNext()){ i++; Object obj = iterator.next(); if(i==value){ vu=obj.toString(); } } return "第"+vu+"题"; } }
public class MyAxisValueFormatter implements IAxisValueFormatter { private DecimalFormat mFormat; public MyAxisValueFormatter() { mFormat = new DecimalFormat("###,###,###,##0.0"); } @Override public String getFormattedValue(float value, AxisBase axis) { return (int)value + " %"; } }
最后,贴上结果图