Android 图表开发开源库 MPAndroidChart 使用总结

1. 引言

电视项目中需要一个折线图表示节电数据变化情况,类比 H5 来说,Android 中也应该有比较成熟的控件,经过调研后,发现 MPAndroidChart 功能比较强大,网上也有人说可能是目前 Android 开发最好用的一个三方库了,功能非常强大,集成简单。

这里把我的集成使用过程及使用中发现的强大惊喜记录下来,留作团队财富,可供后续参考。

首先,简单的介绍下强大的 MPAndroidChart,它支持常用的各种图:柱状图(横向,竖向)、线状图(多种效果)、饼状图、点状图,属性也很简单,我们使用的时候只需要熟悉控件的 11 各种属性即可。核心功能如下:

  • 支持 x,y 轴缩放

  • 支持拖拽

  • 支持手指滑动

  • 支持高亮显示

  • 支持保存图表到文件中

  • 支持从文件(txt)中读取数据

  • 预先定义颜色模板

  • 自动生成标注

  • 支持自定义 x,y 轴的显示标签

  • 支持 x,y 轴动画

  • 支持 x,y 轴设置最大值和附加信息

  • 支持自定义字体,颜色,背景,手势,虚线等

2. 集成

集成很简单,直接导入作为依赖就可以,以下写了一个最小集成的例子,主要为了说明步骤:

2.1 引入依赖到工程中

// 项目工程的 build.gradle
repositories {
   maven { url "https://jitpack.io" }
}

// Module的 build.gradle
dependencies {
       implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
}

2.2 Layout 中添加控件

<!--节电折线图-->
<com.github.mikephil.charting.charts.LineChart
    android:id="@+id/chart1"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_alignParentTop="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentStart="true"
    android:layout_alignParentEnd="true"
    android:layout_marginTop="@dimen/tvcommon_px131"
    android:layout_marginBottom="@dimen/tvcommon_px60"
    android:layout_marginStart="@dimen/tvcommon_px44"
    android:layout_marginEnd="@dimen/tvcommon_px30" />

2.3 Activity 中使用

// 3.4 节电详情页
mChart = findViewById(R.id.chart1)
initChat()
setChatData()

2.3.1 初始化

private fun initChat() {
    mChart.setBackgroundColor(resources.getColor(R.color.color_00000000))
    mChart.description.isEnabled = false

    // 2. X 轴样式
    val xAxis: XAxis = mChart.getXAxis()
    xAxis.setDrawGridLines(false)
    // xAxis.enableGridDashedLine(10f, 10f, 0f);
    xAxis.position = XAxis.XAxisPosition.BOTTOM

    // 3. Y轴样式
    mChart.axisRight.isEnabled = false  // disable dual axis (only use LEFT axis)
    var yAxis: YAxis = mChart.axisLeft
    yAxis.axisMaximum = 200f
    yAxis.axisMinimum = 0f
}

2.3.2 Activity 中设置数据

public void setChatData(){
        List<Entry> entries=new ArrayList<>();
        List<Entry> entries1=new ArrayList<>();
        entries.add(new Entry(3f,20));
        entries1.add(new Entry(5f,30));
 
        LineDataSet dataSet=new LineDataSet(entries,"数据一");
        LineDataSet dataSet1=new LineDataSet(entries1,"数据二");
        List<ILineDataSet> list=new ArrayList<>();
        list.add(dataSet);
        list.add(dataSet1);
        LineData lineData=new LineData(list);

        mChat.setData(lineData); //将模拟数据用于线形图,在线形图显示
    }

2.3.3 大功告成

3. 使用总结

MPAndroidChart 的强大之处在于它的很多功能都可定制,只要你有想法,大部分都有解决方法,哪怕一时没有,只要肯找,说不准就能发现。

电视项目中要求的是一个折线图,所以这里的使用总结大多集中在折线及我们要实现的效果上,其它未涉及的图和属性暂时不写,后续使用的时候再作探索及总结。

3.1 整体功能及专业术语一览(网上找的一张图,镇楼,哈哈)

3.2 基础设置 (非数据类型,在初始化时设置)

首先,在 initChat()函数中,进行了一些基础设置,把一些用不到的功能关闭:

private fun initChat() {
    mChart.setBackgroundColor(resources.getColor(R.color.color_00000000))
    mChart.description.isEnabled = false
    mChart.setTouchEnabled(false)
    mChart.setDrawGridBackground(false)
    mChart.isDragEnabled = false
    mChart.setScaleEnabled(false)
    mChart.setPinchZoom(false)
    mChart.legend.isEnabled = false
}

3.3 x、y 轴设置 (非数据类型,在初始化时设置)

这个也不属于数据设置,所以在初始化时进行,如下,分别进行了线宽,线颜色,Label 的字体大小、颜色、还有网格线:

private fun initChat() {
    ...
    // 2. X 轴样式
    val xAxis: XAxis = mChart.getXAxis()
    xAxis.setDrawGridLines(false)
    // xAxis.enableGridDashedLine(10f, 10f, 0f);
    xAxis.position = XAxis.XAxisPosition.BOTTOM
    xAxis.axisLineWidth = resources.getDimension(R.dimen.tvcommon_px1)
    xAxis.axisLineColor = resources.getColor(R.color.color_979797)
    xAxis.textSize = resources.getDimension(R.dimen.tvcommon_sp16)
    xAxis.textColor = resources.getColor(R.color.white_60alpha)
    xAxis.valueFormatter = object : ValueFormatter() {
        override fun getAxisLabel(value: Float, axis: AxisBase?): String {
            // 自定义 X 轴显示内容
            return super.getAxisLabel(value, axis) + "月"
        }
    }
    
    // 3. Y轴样式
    mChart.axisRight.isEnabled = false  // disable dual axis (only use LEFT axis)
    var yAxis: YAxis = mChart.axisLeft
    yAxis.axisLineWidth = resources.getDimension(R.dimen.tvcommon_px1)
    yAxis.axisLineColor = resources.getColor(R.color.color_979797)
    yAxis.textSize = resources.getDimension(R.dimen.tvcommon_sp16)
    yAxis.textColor = resources.getColor(R.color.white_60alpha)
    yAxis.setDrawGridLines(true)        // horizontal grid lines
    yAxis.enableGridDashedLine(resources.getDimension(R.dimen.tvcommon_px6),
        resources.getDimension(R.dimen.tvcommon_px6),
        0f)
    yAxis.axisMaximum = 200f
    yAxis.axisMinimum = 0f
}

3.4 折线类型设置

需要在 填充数据时,给 LineDataSet 设置一个 mode,如下

private fun setChatData() {
    ...
    val set1 = LineDataSet(values, "DataSet 1")
    set1.mode = LineDataSet.Mode.HORIZONTAL_BEZIER  // 折线类型
}

setMode(LineDataSet.Mode mode),设置模式有四种: 1.CUBIC_BEZIER 立方曲线 2.LINEAR 直线 3.STEPPED 阶梯 4.HORIZONTAL_BEZIER 水平曲线

电视项目这里需要的是一个曲线,所以设置为 HORIZONTAL_BEZIER

3.5 顶点设置

顶点可进行 icon 绘制、小圆点(实心/空心)、自定义值显示

private fun setChatData() {
    ...
    val set1 = LineDataSet(values, "DataSet 1")
    set1.mode = LineDataSet.Mode.HORIZONTAL_BEZIER  // 折线类型
    // 顶点
    set1.setValueFormatter(object : ValueFormatter() {
        override fun getFormattedValue(value: Float): String {
            return "" // 这里返回空,即顶点不显示值,否则不太好看
        }
    })
    set1.valueTextSize = resources.getDimension(R.dimen.tvcommon_sp12)
    set1.setDrawIcons(false) // 不显示端点的icon
    set1.setDrawCircles(false) // 显示顶点圆quan
    set1.setDrawCircleHole(true) // 空心还是实心
}

3.6 折线及填充设置

private fun setChatData() {
    ...
    // 折线
    set1.color = resources.getColor(R.color.blue_gray_32B5E6)
    set1.setCircleColor(resources.getColor(R.color.blue_gray_32B5E6))
    set1.lineWidth = resources.getDimension(R.dimen.tvcommon_px2)
    set1.circleRadius = resources.getDimension(R.dimen.tvcommon_px4)
    
    // 折线包裹起来的区域
    set1.fillFormatter = IFillFormatter { dataSet, dataProvider -> mChart.getAxisLeft().getAxisMinimum() }
    if (Utils.getSDKInt() >= 18) {
        // drawables only supported on api level 18 and above
        val drawable = ContextCompat.getDrawable(this, R.drawable.shape_chat_fill_blue)
        set1.fillDrawable = drawable
    } else {
        set1.fillColor = R.color.blue_gray_32B5E6
    }
    set1.setDrawFilled(true)
}

3.7 X 轴自定义显示 (非数据类型,在初始化时设置)

再回到 X 轴,由于项目要求,X 轴要显示节电的日期,但是构造数据时只是一个数组,默认显示的是数组的下标,所以这里需要自定义

private fun initChat() {
    ... 
    // 2. X 轴样式
    val xAxis: XAxis = mChart.getXAxis()
    ...
    xAxis.valueFormatter = object : ValueFormatter() {
        override fun getAxisLabel(value: Float, axis: AxisBase?): String {
            // 自定义 X 轴显示内容,这里可以通过 axis 中的 postion 从数据中进行一个映射,找到它对应的日期
            return super.getAxisLabel(value, axis) + "月"
        }
    }
}

4. 踩坑及解决

4.1 宽度或 margin 跟自己设置的不一样

1. 效果出来后,发现最右侧不是我设置的,比我预计的靠左了,离边比较远,达不到 UI 设计的效果,如下:

2. 这里的设置应该不是通过简单的 XML 位置属性就能修改的了,因为这属于 MPAndroidChat 控件的内部了,所以得深入 MPAndroidChat,找到产生间隙的原因,于是开始翻源码

3. 经过翻阅源码得知,它内部有一个 minOffset

@Override
public void calculateOffsets() {
    ...
    float minOffset = Utils.convertDpToPixel(mMinOffset);
    mViewPortHandler.restrainViewPort(
        Math.max(minOffset, offsetLeft),
        Math.max(minOffset, offsetTop),
        Math.max(minOffset, offsetRight),
        Math.max(minOffset, offsetBottom));
    ...
}
public void setMinOffset(float minOffset) {
    mMinOffset = minOffset;
}

  再往下查,原来 mMinOffset,是可能通过 setMinOffset()设置进去的,所以在我们的代码 initChat()中添加上一行,再看效果,OK 啦~

private fun initChat() {
    ...
    mChart.minOffset = 0f
    ...
}

4.2 设置 x 轴的 Label 后,发现最底下一层有一点显示不全,被截断了,如下

1. 分析原因

(1)这个应该也是 MPAndroidChat 内部实现导致的,修改外部 Layout 中的 margin, padding 应该不生效,果然,试过之后,没有生效,问题依旧

(2)16sp 时显示是这个效果,而缩小字号后,比如 12sp, 就没有问题,能够显示,猜测,是它内部写死了一个距离,但看源码内的相关说明,它的高度是自动计算的,如下

/**
 * Class representing the x-axis labels settings. Only use the setter methods to
 * modify it. Do not access public variables directly. Be aware that not all
 * features the XLabels class provides are suitable for the RadarChart.
 *
 * @author Philipp Jahoda
 */
public class XAxis extends AxisBase {

    /**
     * width of the x-axis labels in pixels - this is automatically
     * calculated by the computeSize() methods in the renderers
     */
    public int mLabelWidth = 1;

    /**
     * height of the x-axis labels in pixels - this is automatically
     * calculated by the computeSize() methods in the renderers
     */
    public int mLabelHeight = 1;

(3)按理说不应该出现这种情况,难道是 MPAndroidChat 的 Bug,是不是高版本就好了呢,于是上网查了一下,https://gitee.com/jiangsongbai/MPAndroidChart, Github 打不开,到这里的一个 fork 上看一下,发现我用的已经是最新版了 v3.1.0 (吐槽:版本号竟然带个 v,看来不专业啊~)

(4)小插曲:由于项目中使用的是 dimen 中定义的 tvcommon_sp16,在不同分辨率下,可能不一样,我原先是写死的,抱着一点小希望,在代码中使用 dimen 试一下,期望字体能够变小一点,不触发此问题,结果又失望了

xAxis.textSize = resources.getDimension(R.dimen.tvcommon_sp16)

2. extraBottomOffset

(1)再翻阅源码,还是在 BarLineChatBase.java 中的 calculateOffsets() 函数,发现了端倪,如下:

@Override
public void calculateOffsets() {
    ....
    offsetTop += getExtraTopOffset();
    offsetRight += getExtraRightOffset();
    offsetBottom += getExtraBottomOffset();
    offsetLeft += getExtraLeftOffset();
    
    float minOffset = Utils.convertDpToPixel(mMinOffset);
    
    mViewPortHandler.restrainViewPort(
            Math.max(minOffset, offsetLeft),
            Math.max(minOffset, offsetTop),
            Math.max(minOffset, offsetRight),
            Math.max(minOffset, offsetBottom));
    ....
}

(2)这里有一个 getExtraBottomOffset(),再往下看,它是在基类 chat.java 中,有一个 mExtraBottomOffset,

/**
 * @return the extra offset to be appended to the viewport's bottom
 */
public float getExtraBottomOffset() {
    return mExtraBottomOffset;
}

(3)看注释,像是有点用,尝试一下

private fun initChat() {
    ....
    mChart.minOffset = 0f
    mChart.extraBottomOffset = resources.getDimension(R.dimen.tvcommon_px2) // 因为X轴的Label字体设为 16sp 后,最底下有一点显示不全,需要在这里打上一个小补丁。
    ....
}

(4)大功告成~

4.3 顶点的数据显示想要隔位置显示效果

跟产品讨论的时候,把 UI 原先设计的选中点的值显示出来的效果去掉了(因为电视上没有触摸,通过遥控器交互暂时先不做选中效果),如果显示出来所有的数据,会显得比较凌乱,所以想进行隔几个显示或什么效果,研究后,发现重截函数中没有位置信息,无法进行计算是哪一个 postion,暂时无法做到不同的效果,只能统一显示或不显示,后续再进行深入研究,看是否能够做到

private fun setChatData() {
    ...
    val set1 = LineDataSet(values, "DataSet 1")
    set1.mode = LineDataSet.Mode.HORIZONTAL_BEZIER  // 折线类型
    // 顶点
    set1.setValueFormatter(object : ValueFormatter() {
        override fun getFormattedValue(value: Float): String {
            return "" // 这里返回空,即顶点不显示值,否则不太好看
        }
    })
    ...
}

5. 小结

这里记录了在电视项目中使用 MPAndroidChat 的一些使用心得,重点集中在折线上,通过对它的各种属性进行修改自定义了自己的 UI 界面,达到与 UI 设计图一样的效果,也发掘出了 minOffset、extraBottomOffset 这样的小众属性的使用场景及效果,希望能够引起大家的一些共鸣,发现问题时,快速找到解决方法。

6. 团队介绍

「三翼鸟数字化技术平台-场景设计交互平台」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/663836.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

玩机进阶教程------修改gpt.bin分区表地址段 完全屏蔽系统更新 fast刷写分区表 操作步骤解析【二】

上期博文简单说明了分区表的基本常识。我们在有些环境中需要屏蔽手机的系统更新选项。除了以前博文中说明的修改系统更新下载文件夹的方法。还可以通过修改分区表类达到目的。在一些辅助维修工具上面带修改分区表功能。修改后效果为屏蔽系统更新和可以恢复出厂。原则上不深刷都…

kafka学习笔记06

Kafka数据存储流程和log日志讲解 讲解分布式应用核心CAP知识 Kafka数据可靠性保证原理之副本机制Replica介绍《上》 Kafka数据可靠性保证原理之副本机制Replica介绍《下》 Kafka数据可靠性保证原理之ISR机制讲解 Kafka的HighWatermark的作用你知道多少

安装flask:后端框架的学习之旅

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、创建虚拟环境 1. 引入虚拟环境的概念 2. 创建Flask虚拟环境 三、安装Flask…

web 前端开发技术---网页的制作

这是一个网页代码 上年包含了电子邮件&#xff0c;选项建 等等 分享给大家 <!-- prj_7_1.html --> <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title>留言板设计</title><style type&…

Android Studio启动第一个项目的报错——笔记

零基础第一次使用Android Studio开发遇到的问题 1 新建项目下载gradle慢的问题 直接叉掉打开gradle官网手动下载自己想要的版本 gradle官网 然后在刚刚建好的项目里找到gradle-wrapper.properties&#xff0c;修改distributionUrl&#xff0c;并把自己从官网下载的包放到和gr…

【Python】解决Python报错:IndexError: queue index out of range

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

基于Pytorch框架的深度学习EfficientNetV2神经网络中草药识别分类系统源码

第一步&#xff1a;准备数据 5种中草药数据&#xff1a;self.class_indict ["百合", "党参", "山魈", "枸杞", "槐花", "金银花"] &#xff0c;总共有900张图片&#xff0c;每个文件夹单独放一种数据 第二步&a…

初识Spring Boot:构建项目结构与组件解析

目录 前言 第一点&#xff1a;项目的结构 第二点&#xff1a;controller类的创建与使用&#xff08;构造器&#xff09; 第二点&#xff1a;service类的创建与使用&#xff08;逻辑层&#xff09; 第三点&#xff1a;Mapper类的创建与使用(数据操作) 总结 前言 在进行Sp…

HQChart使用教程100-uniapp如何在vue3运行微信小程序

HQChart使用教程100-uniapp如何在vue3运行微信小程序 症状原因分析解决思路解决步骤1. 修改vender.js2. 修改HQChartControl.js 完整实例HQChart代码地址 症状 HQChart插件在uniappvue3的项目编译成小程序以后&#xff0c; 运行会报错&#xff0c;见下图。 原因分析 查了下…

抖音太可怕了,我卸载了

这两天刷短视频&#xff0c;上瘾了&#xff0c;太可怕了。 自己最近一直在研究短视频制作&#xff0c;所以下载了抖音&#xff0c;说实话&#xff0c;我之前手机上并没有抖音&#xff0c;一直在用B站。 用了两天抖音&#xff0c;我发现&#xff0c;这玩意比刷B站还容易上瘾啊…

【深度学习-第6篇】使用python快速实现CNN多变量回归预测(使用pytorch框架)

上一篇我们讲了使用CNN进行分类的python代码&#xff1a; Mr.看海&#xff1a;【深度学习-第5篇】使用Python快速实现CNN分类&#xff08;模式识别&#xff09;任务&#xff0c;含一维、二维、三维数据演示案例&#xff08;使用pytorch框架&#xff09; 这一篇我们讲CNN的多变…

对网工的误解,早就不是一点半点了

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 上午好&#xff0c;我的网工朋友 很多人对网工是有误解的&#xff0c;同为网工的我深有感受。 虽然我的阅历不如老杨总多&#xff0c;但也在这行…

开源与闭源 AI 模型:发展路径的比较与前瞻

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

解决 iOS 端小程序「saveVideoToPhotosAlbum:fail invalid video」问题

场景复现&#xff1a; const url https://mobvoi-digitalhuman-video-public.weta365.com/1788148372310446080.mp4uni.downloadFile({url,success: (res) > {uni.saveVideoToPhotosAlbum({filePath: res.tempFilePath,success: (res) > {console.log("res > &…

chap4 simple neural network

全连接神经网络 问题描述 利用numpy和pytorch搭建全连接神经网络。使用numpy实现此练习需要自己手动求导&#xff0c;而pytorch具有自动求导机制。 我们首先先手动算一下反向传播的过程&#xff0c;使用的模型和初始化权重、偏差和训练用的输入和输出值如下&#xff1a; 我…

R语言绘图 --- 折线图(Biorplot 开发日志 --- 1)

「写在前面」 在科研数据分析中我们会重复地绘制一些图形&#xff0c;如果代码管理不当经常就会忘记之前绘图的代码。于是我计划开发一个 R 包&#xff08;Biorplot&#xff09;&#xff0c;用来管理自己 R 语言绘图的代码。本系列文章用于记录 Biorplot 包开发日志。 相关链接…

通过强化学习彻底改变大型数据集特征选择

文章目录 一、说明二、强化学习&#xff1a;特征选择的马尔可夫决策问题三、用于使用强化学习进行特征选择的 python 库3.1. 数据预处理3.2. 安装和导入FSRLearning库 四、结论和参考文献 一、说明 了解强化学习如何改变机器学习模型的特征选择。通过实际示例和专用的 Python 库…

Qt6.4.2基于CMake添加Qt3DCore模块报错

在文档中说明是添加 find_package(Qt6 REQUIRED COMPONENTS 3dcore) target_link_libraries(mytarget PRIVATE Qt6::3dcore)find_package是没有问题&#xff0c;但是target_link_libraries会报错&#xff0c;报拼写错误&#xff0c;无法链接上Qt6::3dcore 需要使用“3DCore”…

工厂如何最大化mes系统的价值

mes系统&#xff08;Manufacturing Execution System&#xff09;是现代工厂管理中的一个重要系统&#xff0c;它可以实现生产过程中的信息约束与控制&#xff0c;促进生产流程的跟踪和分析&#xff0c;提高生产效率及质量。 一、整合mes系统和erp系统 mes系统和erp系统是两个…

STM32 IIC协议

本文代码使用 HAL 库。 文章目录 前言一、什么是IIC协议二、IIC信号三、IIC协议的通讯时序1. 写操作2. 读操作 四、上拉电阻作用总结 前言 从这篇文章开始为大家介绍一些通信协议&#xff0c;包括 UART&#xff0c;SPI&#xff0c;IIC等。 UART串口通讯协议 SPI通信协议 一、…