MPAndroidChart图表加载框架,适合各种直观的展示。
参考:https://blog.csdn.net/dapangzao/article/details/74949541
参考:https://www.jianshu.com/p/c6e8ea5e9ba0
如果想要了解其原理,请参考上面两个博客,本文将展示几个使用例子。
导入依赖
在根目录下的build文件中添加
allprojects {
repositories {
maven { url “https://jitpack.io” }
}
}
然后,在app下的build中添加依赖
compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'
然后,就可以使用了,先从饼状图开始,图片可以旋转。
下面是代码:
布局:
<com.github.mikephil.charting.charts.PieChart android:id="@+id/piechart" android:layout_width="200dp" android:layout_height="100dp" android:layout_gravity="center_horizontal" ></com.github.mikephil.charting.charts.PieChart>
因为考虑到这种图都是由后台给数据,然后展示出来,而且还有个对比,所以可以使用bean类进行封装,这是测试用的bean类,只有四个float类型
PieChart piechart = findViewById(R.id.piechart);
//创建集合,测试用的bean类,里面只有四个float类型 List<Bean.DistributionBean> energyDistribution = new ArrayList<>(); //增加假数据 Bean.DistributionBean distributionBean=new Bean.DistributionBean(); distributionBean.setChild((float) 2466.2); distributionBean.setChildPum((float) 330.25); distributionBean.setCoolingPum((float)291.0); distributionBean.setCoolingTow((float) 74.6); energyDistribution.add(distributionBean); Bean.DistributionBean distributionBean2=new Bean.DistributionBean(); distributionBean2.setChild((float) 27095.0); distributionBean2.setChildPum((float) 3602.1); distributionBean2.setCoolingPum((float) 3378.0); distributionBean2.setCoolingTow((float) 801.2); energyDistribution.add(distributionBean2); Bean.DistributionBean distributionBean3=new Bean.DistributionBean(); distributionBean3.setChild((float) 233049.8); distributionBean3.setChildPum((float)103586.84); distributionBean3.setCoolingPum((float) 38910.6); distributionBean3.setCoolingTow((float)10368.0); energyDistribution.add(distributionBean3);
//获取环形图数据 解释在下面的方法中 PieData loopPieData = getLoopPieData(this, energyDistribution.size(), 100, energyDistribution, 0); showLoopChart(piechart,loopPieData); //------------------------------------------------------------------------------
/** * 获取环形图数据 * @param context 上下文 * @param count 分成几部分 * @param range 总数多少 * @param energyDistribution 数据集合 * @param i 显示的第几个数据 */ public PieData getLoopPieData(Activity context, int count, float range, List<Bean.DistributionBean> energyDistribution, int i) { // 饼图数据 this, energyDistribution.size(), 100, energyDistribution, 1) /** * 将一个饼形图分成四部分, 四部分的数值比例为14:14:34:38 * 所以 14代表的百分比就是14% */ float quarterly1 = energyDistribution.get(i).getChild(); float quarterly2 = energyDistribution.get(i).getCoolingTow(); float quarterly3 = energyDistribution.get(i).getCoolingPum(); float quarterly4 = energyDistribution.get(i).getChildPum(); ArrayList<PieEntry> entries = new ArrayList<PieEntry>(); entries.add(new PieEntry(quarterly1, "")); entries.add(new PieEntry(quarterly2, "")); entries.add(new PieEntry(quarterly3, "")); entries.add(new PieEntry(quarterly4, "")); ArrayList<Integer> colors = new ArrayList<Integer>(); // 饼图颜色 colors.add(context.getResources().getColor(R.color.blue)); colors.add(context.getResources().getColor(R.color.light_pink)); colors.add(context.getResources().getColor(R.color.light_blue)); colors.add(context.getResources().getColor(R.color.blue_grey)); //y轴的集合 PieDataSet pieDataSet = new PieDataSet(entries, ""/*显示在比例图上*/); pieDataSet.setSliceSpace(1f); //设置个饼状图之间的距离 pieDataSet.setColors(colors); pieDataSet.setValueTextSize(10f);//百分比文字大小 pieDataSet.setValueLinePart1OffsetPercentage(0f); pieDataSet.setValueLinePart1Length(0.5f); // pieDataSet.setValueLinePart2Length(0.7f); // pieDataSet.setYValuePosition(PieDataSet.ValuePosition.INSIDE_SLICE); pieDataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); pieDataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); // DisplayMetrics metrics = context.getResources().getDisplayMetrics(); // float px = 5 * (metrics.densityDpi / 160f); pieDataSet.setSelectionShift(0); // 选中态多出的长度 // PieData pieData = new PieData(xValues, pieDataSet); pieDataSet.setValueFormatter(new PercentFormatter());//添加百分号 PieData pieData = new PieData(pieDataSet); return pieData; } /** * 环形图 * @param pieChart * @param pieData */ public void showLoopChart(PieChart pieChart, PieData pieData) { pieChart.setHoleRadius(50f); //半径 pieChart.setTransparentCircleRadius(0f); // 半透明圈 // pieChart.setTransparentCircleColor(Color.rgb(200,200,200)); // pieChart.setTransparentCircleAlpha(255); desc = new Description(); desc.setText(""); pieChart.setDescription(desc); pieChart.setExtraOffsets(5f,5.f,5.f,5.f);//饼状图内填充 pieChart.setDrawCenterText(false); //饼状图中间可以添加文字 pieChart.setEntryLabelTextSize(10f);//饼状图字体大小 pieChart.setDrawHoleEnabled(true);//设置内环是否显示 pieChart.setRotationAngle(270); // 初始旋转角度 pieChart.setRotationEnabled(true); // 可以手动旋转 pieChart.setUsePercentValues(true); //显示成百分比 pieChart.setCenterText(""); //饼状图中间的文字 pieChart.setHighlightPerTapEnabled(false); pieChart.setDrawEntryLabels(true); //设置数据 pieChart.setData(pieData); Legend mLegend = pieChart.getLegend(); //设置比例图 mLegend.setEnabled(false); pieChart.animateXY(1500, 1500); //设置动画 pieChart.invalidate(); }上面分区的颜色在colors文件中设置就可以了,res/values/colors
这样,一个可以旋转的环形图就做好了,接下来是线形图
为了展示上面点击的圆点,还有数据的展示,需要配备另外的类跟布局
布局:
<com.github.mikephil.charting.charts.LineChart android:layout_width="match_parent" android:layout_height="300px" android:id="@+id/line_chart" ></com.github.mikephil.charting.charts.LineChart>id:
LineChart linechart = findViewById(R.id.line_chart);
创建假的数据!!!!!注意的是,线形图跟柱形图,x坐标的起始点一定要是0.0,不然柱形图会报错下标溢出
//创建假x、y轴坐标 List<Float> x=new ArrayList<>(); List<Float> y=new ArrayList<>(); x.add((float) 0.0); x.add((float) 1.0); x.add((float) 2.0); x.add((float) 3.0); x.add((float) 4.0); x.add((float) 5.0); y.add((float) 1.3); y.add((float) 4.2); y.add((float) 3.1); y.add((float) 8.3); y.add((float) 4.3); y.add((float) 5.0);
//将坐标呈现键值对的方式加入到一个集合中 ArrayList<ArrayList<Entry>> list = new ArrayList<ArrayList<Entry>>(); ArrayList<Entry> entryList = new ArrayList<Entry>(); if (x.size() > y.size()) { for (int i = 0; i < x.size(); i++) { entryList.add(new Entry(x.get(i), y.get(i))); } } else { for (int i = 0; i < y.size(); i++) { entryList.add(new Entry(x.get(i), y.get(i))); } } list.add(entryList); //实例化工具类 SingleChartUtils singleChartUtils = new SingleChartUtils(); //调用线形图 singleChartUtils.showSingleLineChart(this, linechart, list, 0, false);工具类:
public class SingleChartUtils implements OnChartValueSelectedListener { private Context context; private LineChart lineChart; private ArrayList<ArrayList<Entry>> lists; private float aFloat; private GreenMarkerViewRight markerViewRight; private int dataSetIndex; private boolean isShow; private Entry e1; private GreenMarkerViewLeft markerViewLeft; /** * 单根平滑曲线图 * @param context 上下文 * @param mChart 控件 * @param list 坐标集合 * @param max 最大值,可以传0 * @param showLegend 是否启用设置,可以传入false */ public void showSingleLineChart(Context context, LineChart mChart, ArrayList<ArrayList<Entry>> list, float max, boolean showLegend) { this.context = context; lineChart = mChart; lists = list; // no description text Description description = mChart.getDescription(); description.setEnabled(false); description.setText(""); mChart.setTouchEnabled(true); mChart.setOnChartValueSelectedListener(this); mChart.setDragDecelerationFrictionCoef(0.9f); mChart.setDragEnabled(true); mChart.setScaleEnabled(true); mChart.setDrawGridBackground(false); mChart.setHighlightPerDragEnabled(true); // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); mChart.setScaleEnabled(true); mChart.setScaleXEnabled(true); mChart.setScaleYEnabled(true); // set an alternative background color mChart.setBackgroundColor(Color.TRANSPARENT); // add data 单根曲线的数值 setSingleData(mChart,list); mChart.animateX(2000); // get the legend (only possible after setting data) Legend l = mChart.getLegend(); //是否启用设置 l.setEnabled(showLegend); // modify the legend ... l.setForm(Legend.LegendForm.LINE); l.setFormLineWidth(1f); l.setFormSize(20f); l.setTextSize(11f); l.setFormToTextSpace(10f);//legend和文字之间的距离 l.setXEntrySpace(30f);//legend之间的距离 l.setTextColor(R.color.light_grey); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); l.setDrawInside(false); l.setYOffset(10f);//Y方向上的位移 XAxis xAxis = mChart.getXAxis(); xAxis.setTextSize(11f); xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); xAxis.setTextColor(R.color.light_grey); xAxis.setDrawGridLines(true); xAxis.setGranularityEnabled(true); xAxis.setLabelCount(12);//轴上的标签数量 YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTextColor(R.color.light_grey); // leftAxis.setAxisMaximum(max); leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(false); leftAxis.setDrawAxisLine(false); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setEnabled(false); } /** * 单根曲线的数值 * @param mChart * @param list */ private void setSingleData(LineChart mChart,ArrayList<ArrayList<Entry>> list) { ArrayList<Entry> yVals1 = list.get(0); LineDataSet set1; if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet) mChart.getData().getDataSetByIndex(0); set1.setValues(yVals1); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new LineDataSet(yVals1, "实际"); set1.setAxisDependency(YAxis.AxisDependency.LEFT); set1.setColor(context.getResources().getColor(R.color.light_green)); set1.setCircleColor(ColorTemplate.getHoloBlue()); set1.setDrawCircles(false); set1.setMode(LineDataSet.Mode.CUBIC_BEZIER); set1.setLineWidth(1f); set1.setCircleRadius(3f); set1.setFillAlpha(65); set1.setFillColor(context.getResources().getColor(R.color.light_green)); set1.setHighLightColor(context.getResources().getColor(R.color.light_blue)); set1.setDrawCircleHole(false); set1.setDrawValues(false);//曲线上是否显示数据 LineData data = new LineData(set1); data.setValueTextColor(Color.BLACK); data.setValueTextSize(9f); mChart.setData(data); } } /** * 曲线的点击加点事件 * @param e * @param h */ @Override public void onValueSelected(Entry e, Highlight h) { float x = h.getX(); float y = h.getY(); float yPx = h.getYPx();//所点击的点在曲线图中的像素值 float xPx = h.getXPx();//所点击的点在屏幕中的像素值 int dataSetIndex = h.getDataSetIndex(); Log.e("++++++++++++++++","x:"+x+"y:"+y+"xPx:"+xPx+"yPx:"+yPx); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); int width = wm.getDefaultDisplay().getWidth(); int bottom = lineChart.getBottom(); float dimension = context.getResources().getDimension(R.dimen.fifty_two); float n = (float) (823.0 / 1080.0); float space = width * (1 - n); float v = (float)555/(float)600; float i = (bottom * v);//x轴在曲线图的位置高度 aFloat = width - space - xPx; Log.e("++++++++++++++++","width:"+width+"+n:"+n+"+space:"+space+"+i:"+i+"+aFloat:"+ aFloat); if (aFloat <130){ markerViewRight = new GreenMarkerViewRight(context, R.layout.green_marker_view_right, lists.get(0).size()); markerViewRight.setChartView(lineChart); lineChart.setMarker(markerViewRight); }else { markerViewLeft = new GreenMarkerViewLeft(context, R.layout.green_marker_view_left, lists.get(0).size()); markerViewLeft.setChartView(lineChart); lineChart.setMarker(markerViewLeft); } this.dataSetIndex = h.getDataSetIndex(); if (isShow){ e1.setIcon(null); isShow = false; } if (!isShow && this.dataSetIndex !=2){ Bitmap originalBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.sign); int originalWidth = originalBitmap.getWidth(); int originalHeight = originalBitmap.getHeight(); int newWidth = 45; int newHeight = 45; // 自定义 高度 暂时没用 float scale = ((float) newHeight) / originalHeight; Matrix matrix = new Matrix(); matrix.postScale(scale, scale); Bitmap changedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalWidth, originalHeight, matrix, true); BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), changedBitmap); e.setIcon(bitmapDrawable); isShow = true ; e1 = e; lineChart.invalidate(); } } @Override public void onNothingSelected() { e1.setIcon(null); isShow = false; } }工具类配套类:
public class GreenMarkerViewRight extends MarkerView { private final TextView tvCusuomMarkerViewDate; private final TextView tvCusuomMarkerViewEnergy; private final int size; private int dataSetIndex; /** * Constructor. Sets up the MarkerView with a custom layout resource. * @param context * @param layoutResource the layout resource to use for the MarkerView * @param index */ public GreenMarkerViewRight(Context context, int layoutResource, int index) { super(context, layoutResource); this.size = index; tvCusuomMarkerViewDate = (TextView) findViewById(R.id.custom_tv_marker_view_date); tvCusuomMarkerViewEnergy = (TextView) findViewById(R.id.custom_tv_marker_view_energy); } @Override public void refreshContent(Entry e, Highlight highlight) { dataSetIndex = highlight.getDataIndex(); int stackIndex = highlight.getDataSetIndex(); int dataIndex = highlight.getDataIndex(); String s = Utils.formatNumber(e.getX(), 0, true); Log.e("+++++++++++++++-",dataIndex+"s"+s); if (e instanceof CandleEntry) { CandleEntry ce = (CandleEntry) e; tvCusuomMarkerViewEnergy.setText("" + Utils.formatNumber(ce.getHigh(), 0, true)); } else { tvCusuomMarkerViewEnergy.setText("" + Utils.formatNumber(e.getY(), 0, true)); String s1 = Utils.formatNumber(e.getX(), 0, true); if (s1.length()==1){ tvCusuomMarkerViewDate.setText("0"+s1+":00"); }else { tvCusuomMarkerViewDate.setText(s1+":00"); } String[] data = (String[]) e.getData(); } super.refreshContent(e, highlight); } @Override public MPPointF getOffset() { Log.e("--------->>>>>",getWidth()+"+"+getHeight()); return new MPPointF(-getWidth()-10, -(getHeight()/2)); } } 配套类1布局:
<?xml version="1.0" encoding="utf-8"?> <com.zhy.autolayout.AutoLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="149px" android:layout_height="176px" android:orientation="vertical" android:background="#0621f2" android:paddingTop="20px" android:paddingBottom="14px" android:paddingRight="15px" android:paddingLeft="20px"> <TextView android:id="@+id/custom_tv_marker_view_date" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:text="12:00" android:textSize="18px" android:textColor="@color/light_grey" android:ellipsize="end" android:lines="1" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/custom_tv_marker_view_energy" android:layout_marginTop="8px" android:gravity="center_horizontal" android:text="100000" android:textSize="30px" android:textColor="#222222" android:ellipsize="end" android:lines="1" android:textAppearance="?android:attr/textAppearanceSmall"/> <View android:layout_width="match_parent" android:layout_height="1px" android:background="@color/light_grey" android:layout_marginTop="8px" android:layout_marginLeft="5px" android:layout_marginRight="5px"></View> <com.zhy.autolayout.AutoLinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="10px"> <ImageView android:layout_width="wrap_content" android:layout_height="16px" android:src="@drawable/marker_high" /> <TextView android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/custom_tv_marker_view1" android:layout_marginLeft="2px" android:text="HIGH" android:textSize="16px" android:textColor="@color/light_green" android:ellipsize="end" android:lines="1" android:gravity="center_vertical" android:textAppearance="?android:attr/textAppearanceSmall"/> <TextView android:layout_width="50px" android:layout_height="16px" android:id="@+id/custom_tv_marker_view_state1" android:text="1111" android:textSize="16px" android:textColor="@color/light_green" android:ellipsize="end" android:lines="1" android:textAppearance="?android:attr/textAppearanceSmall"/> </com.zhy.autolayout.AutoLinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="8px"> <ImageView android:layout_width="wrap_content" android:layout_height="16px" android:src="@drawable/marker_low" android:layout_gravity="bottom" android:paddingBottom="2px"/> <TextView android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/custom_tv_marker_view2" android:layout_marginLeft="2px" android:text="LOW" android:textSize="16px" android:textColor="@color/light_pink" android:ellipsize="end" android:lines="1" android:textAppearance="?android:attr/textAppearanceSmall"/> <TextView android:layout_width="50px" android:layout_height="18px" android:id="@+id/custom_tv_marker_view_state2" android:text="11111" android:textSize="16px" android:textColor="@color/light_pink" android:ellipsize="end" android:lines="1" android:textAppearance="?android:attr/textAppearanceSmall"/> </LinearLayout> </com.zhy.autolayout.AutoLinearLayout>
配套类2:
public class GreenMarkerViewLeft extends MarkerView { private final TextView tvCusuomMarkerViewDate; private final TextView tvCusuomMarkerViewEnergy; private final int size; private int dataSetIndex; /** * Constructor. Sets up the MarkerView with a custom layout resource. * @param context * @param index * @param layoutResource the layout resource to use for the MarkerView */ public GreenMarkerViewLeft(Context context, int layoutResource, int index) { super(context, layoutResource); this.size = index; tvCusuomMarkerViewDate = (TextView) findViewById(R.id.custom_tv_marker_view_date); tvCusuomMarkerViewEnergy = (TextView) findViewById(R.id.custom_tv_marker_view_energy); } @Override public void refreshContent(Entry e, Highlight highlight) { dataSetIndex = highlight.getDataIndex(); int stackIndex = highlight.getDataSetIndex(); Log.e("+++++++++++++++",dataSetIndex+"+"+stackIndex); if (e instanceof CandleEntry) { CandleEntry ce = (CandleEntry) e; tvCusuomMarkerViewEnergy.setText("" + Utils.formatNumber(ce.getHigh(), 0, true)); } else { tvCusuomMarkerViewEnergy.setText("" + Utils.formatNumber(e.getY(), 0, true)); String s1 = Utils.formatNumber(e.getX(), 0, true); if (s1.length()==1){ tvCusuomMarkerViewDate.setText("0"+s1+":00"); }else { tvCusuomMarkerViewDate.setText(s1+":00"); } String[] data = (String[]) e.getData(); } super.refreshContent(e, highlight); } @Override public MPPointF getOffset() { return new MPPointF(10, -(getHeight()/2)); } }配套类2布局与1的一样
柱形图可以用曲线图一样的数据,方法也在工具类中有,直接调用就可以了。
//柱形图------ // 柱形图 注意,x轴坐标必须从零开始,不然会报错下标越界 BarData barData = charUtils.getBarData(this, x, y); charUtils.showBarChart(bar_chart, barData, x);唯一的问题就是,柱形图的需要重写一下,不能直接使用,这是布局
<qnkj.cn.practice.view.MyBarChart android:layout_width="match_parent" android:layout_height="300px" android:id="@+id/bar_chart" ></qnkj.cn.practice.view.MyBarChart>重写
public class MyBarChart extends BarChart { public MyBarChart(Context context) { super(context); } public MyBarChart(Context context, AttributeSet attrs) { super(context, attrs); } public MyBarChart(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } }