//图表库
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
X轴:XAxis Y轴:YAxis 图例:Legend 描述:Description 限制线:LimitLine 显示的视图:MarkerView (就是选择中的时候显示的样子)
MPAndroidChart最重要的核心功能
许多不同的图表类型:LineChart(线型图),BarChart(条状图,垂直,水平,堆叠,分组),PieChart(饼图),ScatterChart(散点图),CandleStickChart(K线图、蜡烛图),RadarChart(雷达图、蜘蛛网络图表),BubbleChart(气泡图) 组合图表(例如,一条线和一条条) 在两个轴上缩放(带有触摸手势,单独的轴或捏缩放) 拖动/平移(带有触摸手势) 分开的(双)y轴 突出显示值(带有可自定义的弹出视图) 将图表保存到SD卡(作为图像) 预定义的颜色模板 图例(自动生成,可自定义) 可自定义的轴(x轴和y轴) 动画(在x轴和y轴上建立动画) 极限线(提供其他信息,最大值等) 触摸,手势和选择回调的侦听器 完全可定制(绘画,字体,图例,颜色,背景,虚线等) 通过MPAndroidChart-Realm库支持Realm.io移动数据库 在Line-Chart和BarChart中平滑渲染多达10.000个数据点(在运行Android 6.0的2014 OnePlus One上测试) 轻量级(方法计数?1.4K) 可作为.jar文件使用(大小仅为500kb) 可作为gradle依赖项并通过Maven获得 Google-PlayStore演示应用程序 广泛使用,对GitHub和stackoverflow 都提供了强大的支持– mpandroidchart 也可用于iOS:图表(API的工作方式相同) 也可用于Xamarin:MPAndroidChart.Xamarin 对动态和实时数据的有限的支持
水平柱状图
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.mikephil.charting.charts.HorizontalBarChart
android:id="@+id/chart1"
android:layout_width="444px"
android:layout_height="444px"
android:background="@android:color/white" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="500px"
android:orientation="horizontal">
<Button
android:id="@+id/actionToggleBarBorders"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="边框"></Button>
<Button
android:id="@+id/actionToggleValues"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="显示条形框的值"></Button>
<Button
android:id="@+id/actionToggleIcons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="显示图标"></Button>
<Button
android:id="@+id/actionToggleHighlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="高亮显示"></Button>
<Button
android:id="@+id/actionTogglePinch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手势"></Button>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="590px"
android:orientation="horizontal">
<Button
android:id="@+id/actionToggleAutoScaleMinMax"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="比例"></Button>
<Button
android:id="@+id/animateX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X轴动画"></Button>
<Button
android:id="@+id/animateY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Y轴动画"></Button>
<Button
android:id="@+id/animateXY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="XY轴动画"></Button>
<Button
android:id="@+id/actionSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="保存"></Button>
</LinearLayout>
<SeekBar
android:id="@+id/seekBar2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_margin="8dp"
android:layout_marginRight="5dp"
android:layout_toLeftOf="@+id/tvYMax"
android:max="200"
android:paddingBottom="12dp" />
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="match_parent"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_above="@+id/seekBar2"
android:layout_margin="8dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="35dp"
android:layout_toLeftOf="@+id/tvXMax"
android:max="500"
android:paddingBottom="12dp" />
<TextView
android:id="@+id/tvXMax"
android:layout_width="50dp"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/seekBar1"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_marginBottom="15dp"
android:gravity="right"
android:text="@string/dash"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/tvYMax"
android:visibility="gone"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/seekBar2"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_marginBottom="15dp"
android:gravity="right"
android:text="@string/dash"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
package com.xxmassdeveloper.mpchartexample;import com.sbas.xueliapplication.R;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Bundle;
import androidx.core.content.ContextCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.github.mikephil.charting.charts.HorizontalBarChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.XAxis.XAxisPosition;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
import com.github.mikephil.charting.utils.MPPointF;
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
import java.util.ArrayList;
import java.util.List;
public class HorizontalBarChartActivity extends DemoBase implements
OnChartValueSelectedListener, View.OnClickListener {
private HorizontalBarChart chart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_horizontalbarchart);
setTitle("HorizontalBarChartActivity");
chart = findViewById(R.id.chart1);
chart.setOnChartValueSelectedListener(this);
// chart.setHighlightEnabled(false);
chart.setDrawBarShadow(false);
chart.setDrawValueAboveBar(true);
chart.getDescription().setEnabled(false);
// if more than 60 entries are displayed in the chart, no values will be
// drawn
chart.setMaxVisibleValueCount(60);
// scaling can now only be done on x- and y-axis separately
chart.setPinchZoom(false);
// draw shadows for each bar that show the maximum value
// chart.setDrawBarShadow(true);
chart.setDrawGridBackground(false);
XAxis xl = chart.getXAxis();
xl.setPosition(XAxisPosition.BOTTOM);
xl.setTypeface(tfLight);
xl.setDrawAxisLine(true);
xl.setDrawGridLines(false);
xl.setGranularity(10f);
YAxis yl = chart.getAxisLeft();
yl.setTypeface(tfLight);
yl.setDrawAxisLine(true);
yl.setDrawGridLines(true);
yl.setAxisMinimum(0f); // this replaces setStartAtZero(true)
// yl.setInverted(true);
YAxis yr = chart.getAxisRight();
yr.setTypeface(tfLight);
yr.setDrawAxisLine(true);
yr.setDrawGridLines(false);
yr.setAxisMinimum(0f); // this replaces setStartAtZero(true)
// yr.setInverted(true);
chart.setFitBars(true);
chart.animateY(2500);
// setting data
setDataHorizontalBarChar(5,50);
Legend l = chart.getLegend();
l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
l.setDrawInside(false);
l.setFormSize(8f);
l.setXEntrySpace(4f);
initButton();
}
private void initButton() {
Button actionToggleBarBorders = findViewById(R.id.actionToggleBarBorders);
actionToggleBarBorders.setOnClickListener(this::onClick);
Button actionToggleValues = findViewById(R.id.actionToggleValues);
actionToggleValues.setOnClickListener(this::onClick);
Button actionToggleIcons = findViewById(R.id.actionToggleIcons);
actionToggleIcons.setOnClickListener(this::onClick);
Button actionToggleHighlight = findViewById(R.id.actionToggleHighlight);
actionToggleHighlight.setOnClickListener(this::onClick);
Button actionTogglePinch = findViewById(R.id.actionTogglePinch);
actionTogglePinch.setOnClickListener(this::onClick);
Button actionToggleAutoScaleMinMax = findViewById(R.id.actionToggleAutoScaleMinMax);
actionToggleAutoScaleMinMax.setOnClickListener(this::onClick);
Button animateX = findViewById(R.id.animateX);
animateX.setOnClickListener(this::onClick);
Button animateY = findViewById(R.id.animateY);
animateY.setOnClickListener(this::onClick);
Button animateXY = findViewById(R.id.animateXY);
animateXY.setOnClickListener(this::onClick);
Button actionSave = findViewById(R.id.actionSave);
actionSave.setOnClickListener(this::onClick);
}
private void setDataHorizontalBarChar(int count, float range) {
float barWidth = 9f;
float spaceForBar = 10f;
ArrayList<BarEntry> values = new ArrayList<>();
for (int i = 0; i < count; i++) {
float val = (float) (Math.random() * range);
values.add(new BarEntry(i * spaceForBar, val,
getResources().getDrawable(R.drawable.star)));
}
BarDataSet set1;
if (chart.getData() != null &&
chart.getData().getDataSetCount() > 0) {
set1 = (BarDataSet) chart.getData().getDataSetByIndex(0);
set1.setValues(values);
chart.getData().notifyDataChanged();
chart.notifyDataSetChanged();
} else {
set1 = new BarDataSet(values, "DataSet 1");
set1.setDrawIcons(false);
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextSize(10f);
data.setValueTypeface(tfLight);
data.setBarWidth(barWidth);
chart.setData(data);
}
}
@Override
protected void saveToGallery() {
saveToGallery(chart, "HorizontalBarChartActivity");
}
private final RectF mOnValueSelectedRectF = new RectF();
@Override
public void onValueSelected(Entry e, Highlight h) {
if (e == null)
return;
RectF bounds = mOnValueSelectedRectF;
chart.getBarBounds((BarEntry) e, bounds);
MPPointF position = chart.getPosition(e, chart.getData().getDataSetByIndex(h.getDataSetIndex())
.getAxisDependency());
Log.i("bounds", bounds.toString());
Log.i("position", position.toString());
MPPointF.recycleInstance(position);
}
@Override
public void onNothingSelected() {}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.viewGithub: {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java"));
startActivity(i);
break;
}
case R.id.actionToggleValues: {
List<IBarDataSet> sets = chart.getData()
.getDataSets();
for (IBarDataSet iSet : sets) {
iSet.setDrawValues(!iSet.isDrawValuesEnabled());
}
chart.invalidate();
break;
}
case R.id.actionToggleIcons: {
List<IBarDataSet> sets = chart.getData()
.getDataSets();
for (IBarDataSet iSet : sets) {
iSet.setDrawIcons(!iSet.isDrawIconsEnabled());
}
chart.invalidate();
break;
}
case R.id.actionToggleHighlight: {
if(chart.getData() != null) {
chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled());
chart.invalidate();
}
break;
}
case R.id.actionTogglePinch: {
if (chart.isPinchZoomEnabled())
chart.setPinchZoom(false);
else
chart.setPinchZoom(true);
chart.invalidate();
break;
}
case R.id.actionToggleAutoScaleMinMax: {
chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled());
chart.notifyDataSetChanged();
break;
}
case R.id.actionToggleBarBorders: {
for (IBarDataSet set : chart.getData().getDataSets())
((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f);
chart.invalidate();
break;
}
case R.id.animateX: {
chart.animateX(2000);
break;
}
case R.id.animateY: {
chart.animateY(2000);
break;
}
case R.id.animateXY: {
chart.animateXY(2000, 2000);
break;
}
case R.id.actionSave: {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
saveToGallery();
} else {
requestStoragePermission(chart);
}
break;
}
}
}
}
package com.xxmassdeveloper.mpchartexample.notimportant;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.view.View;
import android.widget.Toast;
import com.github.mikephil.charting.charts.Chart;
import com.sbas.xueliapplication.R;
/**
* Base class of all Activities of the Demo Application.
*
* @author Philipp Jahoda
*/
public abstract class DemoBase extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
protected final String[] months = new String[] {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"
};
protected final String[] parties = new String[] {
"Party A", "Party B", "Party C", "Party D", "Party E", "Party F", "Party G", "Party H",
"Party I", "Party J", "Party K", "Party L", "Party M", "Party N", "Party O", "Party P",
"Party Q", "Party R", "Party S", "Party T", "Party U", "Party V", "Party W", "Party X",
"Party Y", "Party Z"
};
private static final int PERMISSION_STORAGE = 0;
protected Typeface tfRegular;
protected Typeface tfLight;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tfRegular = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf");
tfLight = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf");
}
protected float getRandom(float range, float start) {
return (float) (Math.random() * range) + start;
}
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(R.anim.move_left_in_activity, R.anim.move_right_out_activity);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSION_STORAGE) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
saveToGallery();
} else {
Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT)
.show();
}
}
}
protected void requestStoragePermission(View view) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Snackbar.make(view, "Write permission is required to save image to gallery", Snackbar.LENGTH_INDEFINITE)
.setAction(android.R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(DemoBase.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_STORAGE);
}
}).show();
} else {
Toast.makeText(getApplicationContext(), "Permission Required!", Toast.LENGTH_SHORT)
.show();
ActivityCompat.requestPermissions(DemoBase.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_STORAGE);
}
}
protected void saveToGallery(Chart chart, String name) {
if (chart.saveToGallery(name + "_" + System.currentTimeMillis(), 70))
Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!",
Toast.LENGTH_SHORT).show();
else
Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT)
.show();
}
protected abstract void saveToGallery();
}

?如果不想要上边的线? ? ??yl.setEnabled(false);
YAxis yl = chart.getAxisLeft();
yl.setEnabled(false);
yl.setTypeface(tfLight);
yl.setDrawAxisLine(true);
yl.setDrawGridLines(false);
yl.setAxisMinimum(0f); // this replaces setStartAtZero(true)
// yl.setInverted(true);

如果想要圆角柱状图? 修改
HorizontalBarChartRenderer.java
?
?
if (isCustomFill) {
dataSet.getFill(pos)
.fillRect(
c, mRenderPaint,
buffer.buffer[j],
buffer.buffer[j + 1],
buffer.buffer[j + 2],
buffer.buffer[j + 3],
isInverted ? Fill.Direction.LEFT : Fill.Direction.RIGHT);
}
else {
// c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
// buffer.buffer[j + 3], mRenderPaint);
RectF rectF=new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],buffer.buffer[j + 3]);
c.drawRoundRect(rectF,(float)25,(float)25,mRenderPaint);
}

但是,如果想要自定义四个角的弧度,就需要使用Path类绘制。做以下修改:?
HorizontalBarChartRenderer.java
if (isCustomFill) {
dataSet.getFill(pos)
.fillRect(
c, mRenderPaint,
buffer.buffer[j],
buffer.buffer[j + 1],
buffer.buffer[j + 2],
buffer.buffer[j + 3],
isInverted ? Fill.Direction.LEFT : Fill.Direction.RIGHT);
}
else {
// c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
// buffer.buffer[j + 3], mRenderPaint);
// RectF rectF=new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],buffer.buffer[j + 3]);
// c.drawRoundRect(rectF,(float)25,(float)25,mRenderPaint);
RectF rectF=new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],buffer.buffer[j + 3]);
Path path = new Path();
//float数组中4个角分别是左上、右上、右下、左下
path.addRoundRect(rectF,new float[]{0, 0, 20, 20, 20, 20, 0, 0},Path.Direction.CCW);
c.drawPath(path,mRenderPaint);
}
?
?
调整图形的宽度。


?
?
|