- Implementar una de las mejores librerías, MpAndroidChart, para visualizar gráficas de barras, líneas, y pie con un rendimiento alto.
En este ejemplo descargaremos e instalaremos la librería MpAndroidChart.
Para hacerlo realizamos los siguientes pasos en el proyecto utilizado previamente:
-
Nos dirigimos al Gradle del módulo y agregamos las siguientes líneas de código.
// MPAndroidChart implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
-
Sincronizamos el proyecto.
-
Creamos el package items, donde agregaremos los ítems que pintarán las gráficas.
-
La primera clase es abstracta y tiene por nombre ChartItem, y tiene la tarea de agregar los métodos que implementarán los items, así como el tipo que tendrán. Para ello agregamos el siguiente código.
abstract class ChartItem internal constructor(var mChartData: ChartData<*>) { abstract val itemType: Int abstract fun getView(position: Int, convertView: View?, c: Context?): View? companion object { const val TYPE_BARCHART = 0 const val TYPE_LINE_CHART = 1 const val TYPE_PIE_CHART = 2 } }
-
La segunda clase tiene por nombre BarChartItem, y se añade con el siguiente código.
Nota: fue agregada una condicional dentro del constructor para obtener el color, según el tema del dispositivo “Dark / Day”.
abstract class BarChartItem(cd: ChartData<*>?, c: Context) : ChartItem(cd!!) { private val mTf: Typeface = Typeface.createFromAsset(c.assets, "OpenSans-Regular.ttf") private val resTextColor: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) c.resources .getColor(R.color.text_color, c.theme) else c.resources.getColor(R.color.text_color) @SuppressLint("InflateParams") override fun getView(position: Int, convertView: View?, c: Context?): View? { var convertView: View? = convertView val holder: ViewHolder if (convertView == null) { holder = ViewHolder() convertView = LayoutInflater.from(c).inflate(R.layout.list_item_barchart, null) holder.chart = convertView.findViewById(R.id.chart) convertView.tag = holder } else { holder = convertView.tag as ViewHolder } holder.chart!!.description.isEnabled = false holder.chart!!.setDrawGridBackground(false) holder.chart!!.setDrawBarShadow(false) val xAxis = holder.chart!!.xAxis xAxis.position = XAxisPosition.BOTTOM xAxis.typeface = mTf xAxis.textColor = resTextColor xAxis.setDrawGridLines(false) xAxis.setDrawAxisLine(true) val leftAxis = holder.chart!!.axisLeft leftAxis.typeface = mTf leftAxis.setLabelCount(5, false) leftAxis.textColor = resTextColor leftAxis.spaceTop = 20f leftAxis.axisMinimum = 0f val rightAxis = holder.chart!!.axisRight rightAxis.typeface = mTf rightAxis.textColor = resTextColor rightAxis.setLabelCount(5, false) rightAxis.spaceTop = 20f rightAxis.axisMinimum = 0f mChartData.setValueTypeface(mTf) val l = holder.chart!!.legend l.textColor = resTextColor holder.chart!!.data = mChartData as BarData holder.chart!!.setFitBars(true) holder.chart!!.animateY(700) return convertView } private class ViewHolder { var chart: BarChart? = null } }
-
La tercera clase tiene el nombre de LineChartItem, y esta se encarga de mostrar los datos de la gráfica de líneas. Agregamos el siguiente código para establecerla.
abstract class LineChartItem(cd: ChartData<*>?, c: Context) : ChartItem(cd!!) { private val mTf: Typeface = Typeface.createFromAsset(c.assets, "OpenSans-Regular.ttf") private val resTextColor: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) c.resources .getColor(R.color.text_color, c.theme) else c.resources.getColor(R.color.text_color) @SuppressLint("InflateParams") override fun getView(position: Int, convertView: View?, c: Context?): View? { var convertView: View? = convertView val holder: ViewHolder if (convertView == null) { holder = ViewHolder() convertView = LayoutInflater.from(c).inflate( R.layout.list_item_linechart, null ) holder.chart = convertView.findViewById(R.id.chart) convertView.tag = holder } else { holder = convertView.tag as ViewHolder } holder.chart!!.description.isEnabled = false holder.chart!!.setDrawGridBackground(false) val xAxis = holder.chart!!.xAxis xAxis.position = XAxisPosition.BOTTOM xAxis.typeface = mTf xAxis.textColor = resTextColor xAxis.setDrawGridLines(false) xAxis.setDrawAxisLine(true) val leftAxis = holder.chart!!.axisLeft leftAxis.typeface = mTf leftAxis.textColor = resTextColor leftAxis.setLabelCount(5, false) leftAxis.axisMinimum = 0f val rightAxis = holder.chart!!.axisRight rightAxis.typeface = mTf rightAxis.textColor = resTextColor rightAxis.setLabelCount(5, false) rightAxis.setDrawGridLines(false) rightAxis.axisMinimum = 0f val l = holder.chart!!.legend l.textColor = resTextColor holder.chart!!.data = mChartData as LineData holder.chart!!.animateX(750) return convertView } private class ViewHolder { var chart: LineChart? = null } }
-
Por último, la cuarta clase se añade con el nombre de PieChartItem, para mostrar los valores del pie, y se suma lo siguiente.
abstract class PieChartItem(cd: ChartData<*>?, c: Context) : ChartItem(cd!!) { private val mTf: Typeface = Typeface.createFromAsset(c.assets, "OpenSans-Regular.ttf") private val mCenterText: SpannableString private val resTextColor: Int @SuppressLint("InflateParams") override fun getView(position: Int, convertView: View?, c: Context?): View? { var convertView: View? = convertView val holder: ViewHolder if (convertView == null) { holder = ViewHolder() convertView = LayoutInflater.from(c).inflate( R.layout.list_item_piechart, null ) holder.chart = convertView.findViewById(R.id.chart) convertView.tag = holder } else { holder = convertView.tag as ViewHolder } holder.chart!!.description.isEnabled = false holder.chart!!.holeRadius = 52f holder.chart!!.transparentCircleRadius = 57f holder.chart!!.centerText = mCenterText holder.chart!!.setHoleColor(resTextColor) holder.chart!!.setCenterTextTypeface(mTf) holder.chart!!.setCenterTextSize(9f) holder.chart!!.setUsePercentValues(true) holder.chart!!.setExtraOffsets(5f, 10f, 50f, 10f) mChartData.setValueFormatter(PercentFormatter()) mChartData.setValueTypeface(mTf) mChartData.setValueTextSize(11f) mChartData.setValueTextColor(resTextColor) holder.chart!!.data = mChartData as PieData val l = holder.chart!!.legend l.verticalAlignment = Legend.LegendVerticalAlignment.TOP l.horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT l.orientation = Legend.LegendOrientation.VERTICAL l.textColor = resTextColor l.setDrawInside(false) l.yEntrySpace = 0f l.yOffset = 0f holder.chart!!.animateY(900) return convertView } private fun generateCenterText(): SpannableString { val s = SpannableString("MPAndroidChart\nBedu\nAdvanced") s.setSpan(RelativeSizeSpan(1.6f), 0, 14, 0) s.setSpan(ForegroundColorSpan(ColorTemplate.VORDIPLOM_COLORS[0]), 0, 14, 0) s.setSpan(RelativeSizeSpan(1.0f), 14, 20, 0) s.setSpan(ForegroundColorSpan(Color.GRAY), 14, 20, 0) s.setSpan(RelativeSizeSpan(1.4f), 20, s.length, 0) s.setSpan(ForegroundColorSpan(ColorTemplate.getHoloBlue()), 20, s.length, 0) return s } private class ViewHolder { var chart: PieChart? = null } init { mCenterText = generateCenterText() resTextColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) c.resources .getColor(R.color.text_color, c.theme) else c.resources.getColor(R.color.text_color) } }
-
Descarga la fuente de la siguiente dirección y agrégala en la carpeta de assets.
-
Una vez creado el código de los ítems, agregamos la interfaz de los mismos mediante el siguiente código.
9.1 xml list_item_barchart
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <com.github.mikephil.charting.charts.BarChart android:id="@+id/chart" android:layout_width="match_parent" android:layout_height="200dp" /> </LinearLayout>
9.2 xml list_item_linechart
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <com.github.mikephil.charting.charts.LineChart android:id="@+id/chart" android:layout_width="match_parent" android:layout_height="200dp" /> </LinearLayout>
9.3 xml list_item_piechart
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <com.github.mikephil.charting.charts.PieChart android:id="@+id/chart" android:layout_width="match_parent" android:layout_height="345dp" /> </LinearLayout>
-
Ahora nos dirigimos al ChartActivity y agregamos el siguiente código dentro del onCreate, el cual va a crear las 30 gráficas que veremos dentro de nuestra pantalla, además de seleccionar el color del texto a partir del tema, por lo que no debes olvidar agregar la variable resTextColor.
private int resTextColor; ... resTextColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? getResources().getColor(R.color.text_color, getTheme()) : getResources().getColor(R.color.text_color); ListView listView = findViewById(R.id.listView); ArrayList<ChartItem> list = new ArrayList<>(); for (int i = 0; i < 30; i++) { if (i % 3 == 0) { list.add(new LineChartItem(generateDataLine(i + 1), getApplicationContext()) { @Override public int getItemType() { return ChartItem.TYPE_LINE_CHART; } }); } else if (i % 3 == 1) { list.add(new BarChartItem(generateDataBar(i + 1), getApplicationContext()) { @Override public int getItemType() { return ChartItem.TYPE_BARCHART; } }); } else { list.add(new PieChartItem(generateDataPie(), getApplicationContext()) { @Override public int getItemType() { return ChartItem.TYPE_PIE_CHART; } }); } } ChartDataAdapter cda = new ChartDataAdapter(getApplicationContext(), list); listView.setAdapter(cda);
-
Ya que nuestra pantalla tendrá una lista, necesitamos crear un adaptador. Es posible agregar el siguiente código debajo del onCreate para ello.
private static class ChartDataAdapter extends ArrayAdapter<ChartItem> { ChartDataAdapter(Context context, List<ChartItem> objects) { super(context, 0, objects); } @Override public View getView(int position, View convertView, ViewGroup parent) { return getItem(position).getView(position, convertView, getContext()); } @Override public int getItemViewType(int position) { ChartItem ci = getItem(position); return ci != null ? ci.getItemType() : 0; } @Override public int getViewTypeCount() { return 3; } }
-
Como lo has notado, el código que agregamos en el onCreate muestra errores, ya que aún no hemos creado las funciones que generarán los datos para nuestras gráficas. A continuación se muestra el código de las tres gráficas.
12.1 Función generateDataLine
private LineData generateDataLine(int cnt) { ArrayList<Entry> values1 = new ArrayList<>(); for (int i = 0; i < 12; i++) { values1.add(new Entry(i, (int) (Math.random() * 65) + 40)); } LineDataSet d1 = new LineDataSet(values1, "New DataSet " + cnt + ", (1)"); d1.setLineWidth(2.5f); d1.setCircleRadius(4.5f); d1.setHighLightColor(resTextColor); d1.setValueTextColor(resTextColor); d1.setDrawValues(false); ArrayList<Entry> values2 = new ArrayList<>(); for (int i = 0; i < 12; i++) { values2.add(new Entry(i, values1.get(i).getY() - 30)); } LineDataSet d2 = new LineDataSet(values2, "New DataSet " + cnt + ", (2)"); d2.setLineWidth(2.5f); d2.setCircleRadius(4.5f); d2.setHighLightColor(resTextColor); d2.setColor(ColorTemplate.MATERIAL_COLORS[0]); d2.setCircleColor(ColorTemplate.MATERIAL_COLORS[0]); d1.setValueTextColor(resTextColor); d2.setDrawValues(false); ArrayList<ILineDataSet> sets = new ArrayList<>(); sets.add(d1); sets.add(d2); return new LineData(sets); }
12.2 Función generateDataBar
private BarData generateDataBar(int cnt) { ArrayList<BarEntry> entries = new ArrayList<>(); for (int i = 0; i < 12; i++) { entries.add(new BarEntry(i, (int) (Math.random() * 70) + 30)); } BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); d.setColors(ColorTemplate.MATERIAL_COLORS); d.setHighLightAlpha(255); d.setValueTextColor(resTextColor); BarData cd = new BarData(d); cd.setBarWidth(0.9f); cd.setValueTextColor(resTextColor); return cd; }
12.3 Función generateDataPie
private PieData generateDataPie() { ArrayList<PieEntry> entries = new ArrayList<>(); for (int i = 0; i < 4; i++) { entries.add(new PieEntry((float) ((Math.random() * 70) + 30), "Quarter " + (i + 1))); } PieDataSet d = new PieDataSet(entries, ""); d.setSliceSpace(2f); d.setValueTextColor(resTextColor); d.setColors(ColorTemplate.MATERIAL_COLORS); return new PieData(d); }
-
Ejecutamos el proyecto y hacemos clic en el botón Mp Android Chart. Ahora se visualizarán las gráficas de la siguiente forma.
¡Hecho! Ahora nuestra app puede mostrar gráficas con muy buen rendimiento a pesar de la cantidad de elementos en la lista.
Siguiente (Ejemplo 3)