Android图表控件MPAndroidChart——BarChart实现多列柱状图和LineChart多曲线 (完结)
首先才接触Android,目前自学一个月,花了一星期,做出了柱状图和曲线图,踩过坑也不少,上代码(主要提供思路,大部分代码可直接用)。
参考代码地址:①曲线:https://blog.csdn.net/ww897532167/article/details/74129478
②柱状图:https://blog.csdn.net/ww897532167/article/details/74171294
③MPAndroidChart 源码,Github 地址:https://github.com/PhilJay/MPAndroidChart。
效果图:
柱状图、曲线图。主要代码XML布局
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <com.github.mikephil.charting.charts.BarChart android:id="@+id/barChart" android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginBottom="20dp" /> </LinearLayout> <LinearLayout android:id="@+id/ll_Routine_submit" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <com.github.mikephil.charting.charts.LineChart android:id="@+id/lineChart" android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginBottom="20dp" /> </LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_marginTop="5dp" android:layout_marginRight="15dp" android:layout_marginBottom="15dp"> <TextView android:id="@+id/tv_chart_so2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="5dp" android:layout_toLeftOf="@+id/tv_chart_NOX" android:drawableLeft="@mipmap/chart_cvt_icon" android:drawablePadding="1dp" android:text="S0₂(t)" android:textColor="@color/black" android:textSize="11dp" /> <TextView android:id="@+id/tv_chart_NOX" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="5dp" android:layout_toLeftOf="@+id/tv_chart_klw" android:drawableLeft="@mipmap/his_line_2" android:drawablePadding="1dp" android:text="NOX(t)" android:textColor="@color/black" android:textSize="11dp" /> <TextView android:id="@+id/tv_chart_klw" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="5dp" android:layout_toLeftOf="@+id/tv_chart_fq" android:drawableLeft="@mipmap/chart_val_icon" android:drawablePadding="1dp" android:text="颗粒物(t)" android:textColor="@color/black" android:textSize="11dp" /> <TextView android:id="@+id/tv_chart_fq" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:drawableLeft="@mipmap/his_line_6" android:drawablePadding="1dp" android:text="废气(亿标m³)" android:textColor="@color/black" android:textSize="11dp" /> </RelativeLayout>
一、声明变量。
public class GK_QSTab_Fragment extends Fragment implements View.OnClickListener, OnChartValueSelectedListener { private View mView; private TextView entnum_yc, scsss_ty, zwsss_ty, pks_cb, zwbj, pwbj; private Button qs_7sc, qs_7zl, qs_7fq, qs_7fs; private gkonlineModel model = null; private List<gkonlineModel> jkModelList = null; private Type type = new TypeToken<List<gkonlineModel>>() { }.getType(); private BarChart barChart; private LineChart lineChart; private XAxis xAxisbar, xAxis; //X轴 private YAxis leftAxisbar, leftYAxis; //左侧Y轴 private YAxis rightAxisbar, rightYaxis; //右侧Y轴 private Legend legendbar, legend; //图例 private LimitLine limitLine; //限制线 // private MyMarkerView markerView; //标记视图 即点击xy轴交点时弹出展示信息的View 需自定义 private int[] colors = new int[]{ R.color.blue, R.color.cs, R.color.ls, R.color.his_line_color6 }; }
二、获取数据,调用图表方法。josn数据可自己去获取。
private class CallBackListener implements DoHttpRequest.CallbackListener { @Override public void callBack(String result) { // if (realtimeList_all != null && realtimeList_all.size() > 0) { entnum_yc.setText(model.QYYCSumCount); scsss_ty.setText(model.SCTYSumCount); zwsss_ty.setText(model.ZWTYSumCount); pks_cb.setText(model.PKCBSumCount); zwbj.setText(model.ZWSumCount); pwbj.setText(model.PWSumCount); initBarChart(barChart); initChart(lineChart); String json = ""; Gson gson = new Gson(); //把json序列化为List对象 List<IncomeBean> list = gson.fromJson(json, new TypeToken<List<IncomeBean>>() { }.getType()); ArrayList<ILineDataSet> dataSets = new ArrayList<>(); for (int i = 0; i < 4; i++) { //加上渐变色+线条颜色 LineDataSet set = showLineChart(list, getResources().getColor(colors[i]), i); dataSets.add(set); // Drawable drawable = getResources().getDrawable(R.drawable.fade_blue); // setChartFillDrawable(drawable); } LineData data = new LineData(dataSets); lineChart.setData(data); xAxis.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { String tradeDate = list.get((int) value % list.size()).getlEx(); return GK_QSTab_Fragment.DateUtil.formatDate(tradeDate);//获取日期 } }); xAxis.setLabelCount(6, false);//展示X数量 leftYAxis.setLabelCount(5);//展示Y数量 //选中 展示数据 setMarkerView(); //一周设施停运情况 String barjosn = ""; //把json序列化为List对象 List<IncomeBean> listbar = gson.fromJson(barjosn, new TypeToken<List<IncomeBean>>() { }.getType()); ArrayList<IBarDataSet> dataSetsbar = new ArrayList<>(); List<String> xValues = new ArrayList<>(); for (IncomeBean valueBean : listbar) { xValues.add(valueBean.getlEx()); } for (int i = 0; i < 2; i++) { //加上渐变色+线条颜色 BarDataSet set = showBarChart(xValues,listbar, Name[i], getResources().getColor(colors[i]), i); dataSetsbar.add(set); } BarData bardata = new BarData(dataSetsbar); int barAmount = 2; //需要显示柱状图的类别 数量 //设置组间距占比30% 每条柱状图宽度占比 70% /barAmount 柱状图间距占比 0% float groupSpace = 0.3f; //柱状图组之间的间距 float barWidth = (1f - groupSpace) / barAmount; float barSpace = 0f; //设置柱状图宽度 bardata.setBarWidth(barWidth); //(起始点、柱状图组间距、柱状图之间间距) bardata.groupBars(0f, groupSpace, barSpace); barChart.setData(bardata); xAxisbar.setAxisMaximum(7);//X 数量 leftAxisbar.setLabelCount(5);//展示Y数量 // setAdapter(); // } else { // Toast.makeText(getActivity(), "数据请求失败", Toast.LENGTH_SHORT).show(); // } if (getActivity() != null) { getActivity().removeDialog(UIEventListener.UI_EVENT_LOAD_DATA); } } }
三、调用对应样式和方法。
private class Operating implements DoHttpRequest.OperatingDataListener { @Override public void callOperatingData(String result) throws ParseException, JSONException { if (result != null && !result.equals("")) { String json = ""; Gson gson = new Gson(); model = gson.fromJson(json, gkonlineModel.class); // model = gson.fromJson(result, gkonlineModel.class); } } } private void setAdapter() { } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_qs_7fq: initData();//请求数据 break; case R.id.btn_qs_7fs: break; } } /** * 初始化BarChart图表 */ private void initBarChart(BarChart barChart) { /***图表设置***/ Description description = new Description(); // description.setText("需要展示的内容"); description.setEnabled(false); barChart.setDescription(description); //背景颜色 barChart.setBackgroundColor(Color.WHITE); //不显示图表网格 barChart.setDrawGridBackground(false); //背景阴影 barChart.setDrawBarShadow(false); barChart.setHighlightFullBarEnabled(false); //显示边框 barChart.setDrawBorders(false); //设置动画效果 barChart.animateY(1000); barChart.animateX(1000); barChart.setScaleEnabled(false); //是否支持缩放,默认true /***XY轴的设置***/ //X轴设置显示位置在底部 xAxisbar = barChart.getXAxis(); xAxisbar.setPosition(XAxis.XAxisPosition.BOTTOM); xAxisbar.setAxisMinimum(0f); // xAxisbar.setAxisMaximum(6); //将X轴的值显示在中央 xAxisbar.setCenterAxisLabels(true); xAxisbar.setGranularity(1f); leftAxisbar = barChart.getAxisLeft(); rightAxisbar = barChart.getAxisRight(); //保证Y轴从0开始,不然会上移一点 leftAxisbar.setAxisMinimum(0f); rightAxisbar.setAxisMinimum(0f); //不显示X轴网格线 xAxisbar.setDrawGridLines(false); //右侧Y轴网格线设置为虚线 rightAxisbar.enableGridDashedLine(10f, 10f, 0f); rightAxisbar.setEnabled(false);//去掉右侧Y轴 /***图例 标签 设置***/ legendbar = barChart.getLegend(); legendbar.setForm(Legend.LegendForm.SQUARE); legendbar.setTextSize(11f); //显示位置 legendbar.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); legendbar.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); legendbar.setOrientation(Legend.LegendOrientation.HORIZONTAL); //是否绘制在图表里面 legendbar.setDrawInside(false); } /** * 柱状图始化设置 一个BarDataSet 代表一列柱状图 * * @param barDataSet 柱状图 * @param color 柱状图颜色 */ private void initBarDataSet(BarDataSet barDataSet, int color) { barDataSet.setColor(color); barDataSet.setFormLineWidth(1f); barDataSet.setFormSize(15.f); barDataSet.setDrawValues(true);//柱状图顶部值 // barDataSet.setValueTextSize(10f); // barDataSet.setValueTextColor(color); } public BarDataSet showBarChart(List<String> xValues, List<IncomeBean> dataList, String name, int color, int ii) { List<BarEntry> entries = new ArrayList<>(); for (int i = 0; i < dataList.size(); i++) { IncomeBean data = dataList.get(i); switch (ii) { case 0: BarEntry entry = new BarEntry(i, (float) data.getlFQNUM()); entries.add(entry); break; case 1: BarEntry entry1 = new BarEntry(i, (float) data.getlFSNUM()); entries.add(entry1); break; } } //X轴自定义值 xAxisbar.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { String tradeDate= xValues.get((int) Math.abs(value) % xValues.size()); return GK_QSTab_Fragment.DateUtil.formatDatebar(tradeDate);//获取日期 } }); // 每一个BarDataSet代表一类柱状图 BarDataSet barDataSet = new BarDataSet(entries, name); initBarDataSet(barDataSet, color); return barDataSet; } /** * 初始化图表 */ private void initChart(LineChart lineChart) { /***图表设置***/ //右下角还有一个描述标签 Descripition Lable Description description = new Description(); // description.setText("需要展示的内容"); description.setEnabled(false); lineChart.setDescription(description); //是否展示网格线 lineChart.setDrawGridBackground(false); //是否显示边界 lineChart.setDrawBorders(true); //是否可以拖动 lineChart.setDragEnabled(false); //是否有触摸事件 lineChart.setTouchEnabled(true); //设置XY轴动画效果 lineChart.animateY(2500); lineChart.animateX(1500); /***XY轴的设置***/ lineChart.setDrawBorders(false);//是否显示边界 lineChart.getXAxis().setDrawGridLines(false); lineChart.getAxisRight().setDrawGridLines(false); lineChart.getAxisLeft().setDrawGridLines(true);//X Y轴网格线 lineChart.getAxisLeft().enableGridDashedLine(10f, 10f, 0f);//设置X Y轴网格线为虚线(实体线长度、间隔距离、偏移量:通常使用 0) lineChart.getAxisRight().setEnabled(false);//去掉右侧Y轴 xAxis = lineChart.getXAxis(); leftYAxis = lineChart.getAxisLeft(); rightYaxis = lineChart.getAxisRight(); //X轴设置显示位置在底部 xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); xAxis.setAxisMinimum(0f); xAxis.setGranularity(1f); //保证Y轴从0开始,不然会上移一点 leftYAxis.setAxisMinimum(0f); rightYaxis.setAxisMinimum(0f); /***折线图例 标签 设置***/ legend = lineChart.getLegend(); //设置显示类型,LINE CIRCLE SQUARE EMPTY 等等 多种方式,查看LegendForm 即可 legend.setForm(Legend.LegendForm.LINE); legend.setTextSize(12f); //显示位置 左下方 legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); legend.setOrientation(Legend.LegendOrientation.HORIZONTAL); //是否绘制在图表里面 legend.setDrawInside(false); legend.setEnabled(false);//隐藏图列 } /** * 曲线初始化设置 一个LineDataSet 代表一条曲线 * * @param lineDataSet 线条 * @param color 线条颜色 * @param mode */ private void initLineDataSet(LineDataSet lineDataSet, int color, LineDataSet.Mode mode) { lineDataSet.setColor(color); lineDataSet.setCircleColor(color); lineDataSet.setLineWidth(1.5f); lineDataSet.setCircleRadius(3f); lineDataSet.setDrawCircles(false);//*不显示点 lineDataSet.setDrawValues(false);//*不显示值 //显示值并对值 进行修改。 // lineDataSet.setValueFormatter(new IValueFormatter() { // @Override // public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { // DecimalFormat df = new DecimalFormat(".00"); // return df.format(value * 100) + "%"; // } // }); //设置曲线值的圆点是实心还是空心 lineDataSet.setDrawCircleHole(false); lineDataSet.setValueTextSize(10f); //设置折线图填充 lineDataSet.setDrawFilled(false);//线条背景色 lineDataSet.setFormLineWidth(1f); lineDataSet.setFormSize(15.f); if (mode == null) { //设置曲线展示为圆滑曲线(如果不设置则默认折线) lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER); } else { lineDataSet.setMode(mode); } } /** * 展示曲线 * * @param dataList 数据集合 * @param color 曲线颜色 */ public LineDataSet showLineChart(List<IncomeBean> dataList, int color, int ii) { List<Entry> entries = new ArrayList<>(); for (int i = 0; i < dataList.size(); i++) { IncomeBean data = dataList.get(i); Entry entry = new Entry(); switch (ii) { case 0: entry = new Entry(i, (float) data.getlSO2PFL());//获取曲线值 break; case 1: entry = new Entry(i, (float) data.getlNOXPFL());//获取曲线值 break; case 2: entry = new Entry(i, (float) data.getlDUSTPFL());//获取曲线值 break; case 3: entry = new Entry(i, (float) data.getlFQPFL() / 10000);//获取曲线值 break; } entries.add(entry); } // 每一个LineDataSet代表一条线 // LineDataSet d = new LineDataSet(entries, name); LineDataSet d = new LineDataSet(entries, ""); initLineDataSet(d, color, LineDataSet.Mode.LINEAR); return d; } /** * 数据获取到的时间为 20180502 我们需要显示 05-02 所以需要进行日期转换 * 一个日期转换工具 */ public static class DateUtil { public static String formatDate(String str) { SimpleDateFormat sf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm"); SimpleDateFormat sf2 = new SimpleDateFormat("dd日HH时"); String formatStr = ""; try { formatStr = sf2.format(sf1.parse(str)); } catch (ParseException e) { e.printStackTrace(); } return formatStr; } public static String formatDatebar(String str) { SimpleDateFormat sf1 = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat sf2 = new SimpleDateFormat("MM-dd"); String formatStr = ""; try { formatStr = sf2.format(sf1.parse(str)); } catch (ParseException e) { e.printStackTrace(); } return formatStr; } } /** * 设置线条填充背景颜色 * * @param drawable */ public void setChartFillDrawable(Drawable drawable) { if (lineChart.getData() != null && lineChart.getData().getDataSetCount() > 0) { LineDataSet lineDataSet = (LineDataSet) lineChart.getData().getDataSetByIndex(0); //避免在 initLineDataSet()方法中 设置了 lineDataSet.setDrawFilled(false); 而无法实现效果 lineDataSet.setDrawFilled(true); lineDataSet.setFillDrawable(drawable); lineChart.invalidate(); } } /** * 设置 可以显示X Y 轴自定义值的 MarkerView */ public void setMarkerView() { LineChartMarkView mv = new LineChartMarkView(this.getActivity(), xAxis.getValueFormatter()); mv.setChartView(lineChart); lineChart.setMarker(mv); lineChart.invalidate(); } private final RectF onValueSelectedRectF = new RectF(); //柱状图 点击明细 public void onValueSelected(Entry e, Highlight h) { if (e == null) return; RectF bounds = onValueSelectedRectF; barChart.getBarBounds((BarEntry) e, bounds); MPPointF position = barChart.getPosition(e, YAxis.AxisDependency.LEFT); Log.i("bounds", bounds.toString()); Log.i("position", position.toString()); Log.i("x-index", "low: " + barChart.getLowestVisibleX() + ", high: " + barChart.getHighestVisibleX()); MPPointF.recycleInstance(position); } @Override public void onNothingSelected() { }
/* * 曲线点击图表查看 明细。主要注意 上标和下标,必须通过Textview * */ public class LineChartMarkView extends MarkerView { private TextView tvDate,tvValue0,tvValue1,tvValue2,tvValue3,chart_so2; private IAxisValueFormatter xAxisValueFormatter; DecimalFormat df = new DecimalFormat(".00"); public LineChartMarkView(Context context, IAxisValueFormatter xAxisValueFormatter) { super(context, R.layout.layout_markview); this.xAxisValueFormatter = xAxisValueFormatter; tvDate = findViewById(R.id.tv_date1); tvValue0 = findViewById(R.id.tv_value0); tvValue1 = findViewById(R.id.tv_value1); tvValue2 = findViewById(R.id.tv_value2); tvValue3 = findViewById(R.id.tv_value3); } // @SuppressLint("SetTextI18n") @Override public void refreshContent(Entry e, Highlight highlight) { //角标上标 SpannableString m3 = new SpannableString("废气(亿标m3):"); m3.setSpan(new RelativeSizeSpan(0.5f), 6, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); m3.setSpan(new SuperscriptSpan(), 6, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //上标 SpannableStringBuilder fqBuilder = new SpannableStringBuilder(); fqBuilder.append(m3); //角标下标 SpannableString B = new SpannableString("SO2(t):"); B.setSpan(new RelativeSizeSpan(0.5f), 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);//一半大小 B.setSpan(new SubscriptSpan(), 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //上标 SpannableStringBuilder SO2Builder = new SpannableStringBuilder(); SO2Builder.append(B); Chart chart = getChartView(); if (chart instanceof LineChart) { LineData lineData = ((LineChart) chart).getLineData(); List<ILineDataSet> dataSetList = lineData.getDataSets(); for (int i = 0; i < dataSetList.size(); i++) { LineDataSet dataSet = (LineDataSet) dataSetList.get(i); float y = dataSet.getValues().get((int) e.getX()).getY(); switch (i){ case 0: SO2Builder.append(Float.toString(y)); tvValue0.setText(SO2Builder); break; case 1: tvValue1.setText("NOX(t):" + "" + y); break; case 2: tvValue2.setText("颗粒物(t):" + "" + y); break; case 3: fqBuilder.append(Float.toString(y)); tvValue3.setText(fqBuilder); break; } } tvDate.setText(xAxisValueFormatter.getFormattedValue(e.getX(), null)); } //???????X??? ???X?????? // tvDate.setText(xAxisValueFormatter.getFormattedValue(e.getX(), null)); // tvValue.setText("SO2(t)??" + e.getY()); super.refreshContent(e, highlight); } @Override public MPPointF getOffset() { return new MPPointF(-(getWidth() / 2), -getHeight()); } }
曲线图点击x y 对应值的页面
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/shape_square" android:orientation="vertical"> <TextView android:id="@+id/tv_date1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/white" /> <TextView android:id="@+id/tv_value0" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="1dp" android:textColor="@android:color/white" /> <TextView android:id="@+id/tv_value1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="1dp" android:textColor="@android:color/white" /> <TextView android:id="@+id/tv_value2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="1dp" android:textColor="@android:color/white" /> <TextView android:id="@+id/tv_value3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="1dp" android:textColor="@android:color/white" /> </LinearLayout>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」