转自: https://blog.csdn.net/u014769864/article/details/72723180

MPAndroidChart系列:

MPAndroidChart 之LineChart(1)

MPAndroidChart之LineChart(2)MarkerView

MPAndroidChart之LinChart(3)scale缩放

MPAndroidChart LineChart 缩放

MPAndroidChart使用版本:

    compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'  

对于MPAndroidChart 折线图的基本设置和属性不懂得建议先去了解也可以看这篇 MPAndroidChart 之LineChart(1) 。如果对最新版本使用过并且有点熟悉的话,我们接下来看看最新版本中折线图的缩放。

下面都是以LineChart为例子 下面都是以LineChart为例子 下面都是以LineChart为例子

1、 为什么要缩放?

答:看上面的截图,借用官方的demo截的图,第一幅是数据大小挺合适的时候,第二幅是数据超级多的时候,什么感觉?明白缩放能干嘛了吗?当然是为了解决上面的这种数据过多,一屏显示不下问题,提高用户体验。

2、 缩放是什么样子的?

答:上面就是对X轴进行了缩放一定比例,然后你就可以左右拖动了,就算数据再多也不怕密密麻麻了,当然你也可以对Y轴进行缩放,那样就能上下拖动。

3、怎么设置缩放?

答:我们可以

  • 双击进行缩放;
  • 手势双指缩放;
  • 通过代码进行设置缩放。

下面是在缩放前可以选择设置的些我们需要设置的属性:

    //设置chart是否可以触摸  
    mLineChart.setTouchEnabled(true);  
    //设置是否可以拖拽  
    mLineChart.setDragEnabled(true);  
    //设置是否可以缩放 x和y,默认true  
    mLineChart.setScaleEnabled(false);  
    //是否缩放X轴  
    mLineChart.setScaleXEnabled(true);  
    //X轴缩放比例  
    mLineChart.setScaleX(1.5f);  
    //Y轴缩放比例  
    mLineChart.setScaleY(1.5f);  
    //是否缩放Y轴  
    mLineChart.setScaleYEnabled(true);  
    //设置是否可以通过双击屏幕放大图表。默认是true  
    mLineChart.setDoubleTapToZoomEnabled(false);  

一、例子

这里以一种情况为例:不能双击缩放,不能手势手指缩放,只能通过代码设置固定的缩放

基础属性的设置自己去了解,下面只是是缩放代码设置

    //缩放第一种方式  
    Matrix matrix = new Matrix();  
    //1f代表不缩放  
    matrix.postScale(3f, 1f);  
    mLineChart.getViewPortHandler().refresh(matrix, mLineChart, false);  
    //重设所有缩放和拖动,使图表完全适合它的边界(完全缩小)。  
    mLineChart.fitScreen();  
    //缩放第二种方式  
    mLineChart.getViewPortHandler().getMatrixTouch().postScale(3f, 1f);  

在最新版本里(当前是3.0.2),LineChart设置好基础属性后(如果基本设置和属性不懂的可以看看 MPAndroidChart 之LineChart(1) ),http://www.ramlife.org/2021/12/05/479.html 在按上面代码其中之一进行设置缩放3f后(这里为了简单些,只缩放X轴),效果图如下:

蛋疼的地方来了,大家系好安全带,因为下面全是解决缩放问题的东西,而且篇幅也不会很短,我尽可能写得详细点。

上面gif图我代码设置了X轴缩放是3f,单纯的我以为设置了缩放就能达到我想要的预期效果了,我认为的预期效果如下图

如果眼睛近视程度没有很深的话,上面两幅gif图区别还是能看出来的,不过我有个疑问,为什么设置了缩放后(从这里倒数第二幅gif图)是那样的,而不是我预期的那样(从这里倒数第一幅gif图)? --- 哎 也无所谓了,太多需求不一样吧。

实现了预期效果后看起来好简单,可是走过的路确是那么的艰难和漫长,官网没有提供好点的demo,博客10篇9篇是转载或乱七八糟的(也可能是我搜索关键词不对?),搜索后除了坑爹就剩下蛋疼,一脸茫然无助,只好自己研究研究了。

二、存在问题

X轴设置缩放后问题:

  1. 我们看到图表左右随着你拖动而拖动了,但是X轴的竖线(X轴竖下来的3条线)是定死的,不管怎样左右拖动,图表是动了但是那3条竖线没动,而是一直固定在那里;
  2. 当左右拖动时,X轴的label(也就是字符串1、字符串2、字符串3....)只是改变了数值,也没有和图表一起联动起来;
  3. .....你来提....

三、解决问题

解决问题1步骤:

1、初始化chart基本属性

首先是把LineChart基本属性设置了,这里如果还不清楚一些基本属性设置的话,可以看看 MPAndroidChart 之LineChart(1) ,http://www.ramlife.org/2021/12/05/479.html 然后记得要设置缩放的倍数,设置好后大概就像下面的gif图一样,当然,数据不一样没什么影响,关键是效果一样,OK;

2、从何处入手

从哪里入手呢?在MPAndroidChart中有一个类“AxisRenderer”,翻译过来Axis就是渲染器的意思,该类是干什么的,你可以点击进去查看源码有哪些方法并且做了什么,也可以看官方给的doc中查看该类做什么的, 前往官方doc , https://javadoc.jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.2/javadoc/ 如下图,官方对“AxisRenderer”类说得很清楚了,下图中是该累拥有的方法,并且说明了每个方法做了什么(看不懂英文就去在线翻译),其中看到1个我圈出来的方法,也就是我解决上面提到存在问题1的关键方法

我为什么知道该类?首先我是一步一步来的并且在我这篇 ” MPAndroidChart 之LineChart(1) http://www.ramlife.org/2021/12/05/479.html 博客中,解决了几个问题(主要是X轴第一个和最后一个label 超出了Y轴的左右两边的轴线 ), 自然而然就知道“AxisRenderer”类的存在并且有哪些方法,能干嘛的了 继承“AxisRenderer”有“XAxisRenderer”和“YAxisRenderer”2个类,这里要解决的问题,只用到“XAxisRenderer”类(也就是解决问题1和2都是是属于X轴的东西嘛),OK

下面是源码AxisRenderer类里的renderGridLines方法,方法里面也是源码代码,经过打印测试,我把重要的地方都注释有了

代码:

     // x轴垂直竖线线
        @Override
        public void renderGridLines(Canvas c) {
            //源码
            if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())
                return;

            int clipRestoreCount = c.save();
            c.clipRect(getGridClippingRect());

            //if保证mRenderGridLinesBuffer长度为X轴标签数乘以2(mAxis.mEntryCount为label数)
            if (mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2) {
                mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2];
            }
            float[] positions = mRenderGridLinesBuffer;

            //mEntries数组里面装的是x轴setValueFormatter里的value中从最小到大开始取的值
            for (int i = 0; i < positions.length; i += 2) {
                positions[i] = mXAxis.mEntries[i / 2];
                positions[i + 1] = mXAxis.mEntries[i / 2];
            }

            //用所有矩阵变换点数组。非常重要:保持矩阵顺序“值触摸偏移”时转化。
            mTrans.pointValuesToPixel(positions);

            //设置画笔属性
            setupGridPaint();

            Path gridLinePath = mRenderGridLinesPath;
            gridLinePath.reset();

            for (int i = 0; i < positions.length; i += 2) {
                //根据positions数组里的值画出X轴竖线
                drawGridLine(c, positions[i], positions[i + 1], gridLinePath);
            }

            c.restoreToCount(clipRestoreCount);
        }

上面是官方画X轴标签竖线的源码,可以看到思路:

  1. 创建一个标签数乘以2大小的数组;
  2. 把需要画的竖线的位置数据赋值给positions数组;
  3. 调用mTrans.pointValuesToPixel(positions)方法把数组positions里的数据变换;
  4. 根据mTrans.pointValuesToPixel(positions)方法变换好的数据,调用drawGridLine方法,把竖线画出来。

ok,这样画出来的Chart,如果设置了X轴缩放大小,X轴的竖线是固定的,并不随左右拖动而拖动(也就是竖线并没有缩放),也就会存在前面说的问题1,如下gif图

存在这样的问题,原因就在于源码是设置完数组positions数据后(这就等于固定了X轴竖线的位置了,同时也代表positions里的数据是不缩放的值)才调用 mTrans .pointValuesToPixel(positions) 方法。

3、具体解决问题1

上面我应该算很清楚的注释了,要解决问题1,最重要的是mTrans.pointValuesToPixel(positions)方法,点击进入源码我们看到如下图

翻译过来如下图

恩,也就是说我想要竖线不固定(随着X轴缩放竖线也缩放)那就得调下顺序,得设置一条竖线的数据(每条线的数据就得自己摸索了)就调用mTrans.pointValuesToPixel(positions)方法,然后在调用drawGridLine方法画出来(和源码设置完所有竖线数据在调用mTrans.pointValuesToPixel(positions)和drawGridLine不一样)。

想要看得懂后面的某段代码,下面例子一定要看明白。。。。。。。。。。。。。。。。。。。。。。。。。。。。

每条线数据例子:

如下图,有一根线,现在只知道第一个刻度和最后一个刻度值为1和17,每个刻度是平均分的,现在我要求出打问号的另外3个刻度的值,怎么算呢?

第一个问号:((17-1)/4)1+1; (最后一个值-第一个值) 第二个问号:((17-1)/4)2+1; ========》得出公式:每个刻度值 = [--------------------------------------]下标+第一个值; 第三个问号:((17-1)/4)3+1; 份数(多少份)

上面的例子和公式对于下面修改源码中如何获取每条线X轴坐标值思路基本是一样的。

恩,按照这个思路我们修改一下下源码,先写个类MyXAxisRenderer继承XAxisRenderer类,然后重写renderGridLines方法

代码

    import android.graphics.Canvas;
    import android.graphics.Path;

    import com.github.mikephil.charting.components.XAxis;
    import com.github.mikephil.charting.renderer.XAxisRenderer;
    import com.github.mikephil.charting.utils.ViewPortHandler;
    import com.mpandroidchartcsdn.mychart.MyLineChart;

    import static android.R.attr.label;

    public class MyXAxisRenderer extends XAxisRenderer {

        private MyLineChart myLineChart;

        public MyXAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, MyLineChart myLineChart) {
            super(viewPortHandler, xAxis, trans);
            this.myLineChart = myLineChart;
        }

        // x轴垂直线
        @Override
        public void renderGridLines(Canvas c) {
            //源码拷贝过来
            if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())
                return;

            int clipRestoreCount = c.save();
            c.clipRect(getGridClippingRect());

            //if保证mRenderGridLinesBuffer长度为X轴标签数乘以2(mAxis.mEntryCount为label数)
            if (mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2) {
                mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2];
            }
            float[] positions = mRenderGridLinesBuffer;

            //mEntries数组里面装的是x轴setValueFormatter里的value中从最小到大开始取的值
            for (int i = 0; i < positions.length; i += 2) {
                positions[i] = mXAxis.mEntries[i / 2];
                positions[i + 1] = mXAxis.mEntries[i / 2];
            }

           /* //用所有矩阵变换点数组。非常重要:保持矩阵顺序“值触摸偏移”时转化。
            mTrans.pointValuesToPixel(positions);*/

            //设置画笔属性
            setupGridPaint();

            Path gridLinePath = mRenderGridLinesPath;
            gridLinePath.reset();

            for (int i = 0; i < positions.length; i += 2) {
                /*
                 *                        最后一个坐标X坐标值-第一个坐标X坐标值
                 * X轴竖线X坐标 = (---------------------------------------------)*(i/2)+第一个坐标X坐标值
                 *                                  label-1
                 */
                //下面4行是调整的代码

                //第一个坐标X坐标值
                float fX = myLineChart.getData().getDataSets().get(0).getEntryForIndex(0).getX();
                //最后一个坐标X坐标值
                float eX = myLineChart.getData().getDataSets().get(0).getEntryForIndex(myLineChart.getData().getEntryCount() - 1).getX();
                positions[i] = ((eX - fX) / (mXAxis.mEntryCount - 1)) * (i / 2) + (fX);
                mTrans.pointValuesToPixel(positions);
                //根据positions数组里的值画出X轴竖线
                drawGridLine(c, positions[i], positions[i + 1], gridLinePath);
            }

            c.restoreToCount(clipRestoreCount);

        }

然后在创建一个MyLineChart

代码

    package com.mpandroidchartcsdn.mychart;

            import android.content.Context;
            import android.util.AttributeSet;

            import com.github.mikephil.charting.charts.LineChart;

    /**
     * Created by tujingwu on 2017/5/4 
     * . 
     */

    public class MyLineChart extends LineChart {

        public MyLineChart(Context context) {
            super(context);
        }

        public MyLineChart(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        @Override
        protected void init() {
            super.init();
            mXAxisRenderer = new MyXAxisRenderer(mViewPortHandler, mXAxis, mLeftAxisTransformer,this);
        }

    } 

最后像使用LineChart一样使用MyLineChart就行了,最后gif结果图如下

ok,对于X轴的竖线已经解决了问题1,也达到了预期效果,对于问题2,也就是X轴的标签,那就更好解决了,同样的道理。

解决问题2步骤:
1、先写个类MyXAxisRenderer继承XAxisRenderer类,然后重写drawLabels方法

代码

    package com.mpandroidchartcsdn.mychart;

    import android.graphics.Canvas;

    import com.github.mikephil.charting.components.XAxis;
    import com.github.mikephil.charting.renderer.XAxisRenderer;
    import com.github.mikephil.charting.utils.MPPointF;
    import com.github.mikephil.charting.utils.Transformer;
    import com.github.mikephil.charting.utils.Utils;
    import com.github.mikephil.charting.utils.ViewPortHandler;

    public class MyXAxisRenderer extends XAxisRenderer {

        private MyLineChart myLineChart;

        public MyXAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, MyLineChart myLineChart) {
            super(viewPortHandler, xAxis, trans);
           /* this.mChart = mChart;
            this.mXAxis = xAxis;*/
            this.myLineChart = myLineChart;
        }

        @Override
        protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
            //把代码复制过来
            final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
            boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();

            float[] positions = new float[mXAxis.mEntryCount * 2];

            for (int i = 0; i < positions.length; i += 2) {

                // only fill x values
                if (centeringEnabled) {
                    positions[i] = mXAxis.mCenteredEntries[i / 2];
                } else {
                    positions[i] = mXAxis.mEntries[i / 2];
                }
            }

            // mTrans.pointValuesToPixel(positions);
            for (int i = 0; i < positions.length; i += 2) {

                float x = positions[i];
                if (mViewPortHandler.isInBoundsX(x)) {

                    String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis);

                    if (mXAxis.isAvoidFirstLastClippingEnabled()) {

                        // avoid clipping of the last mXAxis.mEntryCount-1为x轴标签数
                        if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) {
                            float width = Utils.calcTextWidth(mAxisLabelPaint, label);

                            if (width > mViewPortHandler.offsetRight() * 2
                                    && x + width > mViewPortHandler.getChartWidth())
                                x -= width / 2;
                            // avoid clipping of the first
                        } else if (i == 0) {

                            float width = Utils.calcTextWidth(mAxisLabelPaint, label);
                            x += width / 2;
                        }
                    }
                    drawLabel(c, label, positions[0], pos, anchor, labelRotationAngleDegrees);
                }
            }

        }

    }
2、思路还是和解决问题1一样,稍微修改一下下源码,上面代码修改后

代码

    import android.graphics.Canvas;

    import com.github.mikephil.charting.components.XAxis;
    import com.github.mikephil.charting.renderer.XAxisRenderer;
    import com.github.mikephil.charting.utils.MPPointF;
    import com.github.mikephil.charting.utils.Utils;
    import com.github.mikephil.charting.utils.ViewPortHandler;
    import com.mpandroidchartcsdn.mychart.MyLineChart;

    import static android.R.attr.x;

    public class MyXAxisRenderer extends XAxisRenderer {

        private MyLineChart myLineChart;

        public MyXAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, MyLineChart myLineChart) {
            super(viewPortHandler, xAxis, trans);
            this.myLineChart = myLineChart;
        }

        @Override
        protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
            //把代码复制过来
            final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
            boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();

            float[] positions = new float[mXAxis.mEntryCount * 2];

            for (int i = 0; i < positions.length; i += 2) {

                // only fill x values
                if (centeringEnabled) {
                    positions[i] = mXAxis.mCenteredEntries[i / 2];
                } else {
                    positions[i] = mXAxis.mEntries[i / 2];
                }
            }

            // mTrans.pointValuesToPixel(positions);

            for (int i = 0; i < positions.length; i += 2) {

                /*   
                 *                  最后一个坐标X坐标值-第一个坐标X坐标值
                 * X轴竖线X坐标 = (---------------------------------------------)*(i/2)+第一个坐标X坐标值
                 *                                  label-1
                 */
                //下面四行是修改后的代码

                //第一个坐标X坐标值
                float fX = myLineChart.getData().getDataSets().get(0).getEntryForIndex(0).getX();
                //最后一个坐标X坐标值
                float eX = myLineChart.getData().getDataSets().get(0).getEntryForIndex(myLineChart.getData().getEntryCount() - 1).getX();
                positions[i] = ((eX - fX) / (mXAxis.mEntryCount - 1)) * (i / 2) + (fX);
                mTrans.pointValuesToPixel(positions);
                if (mViewPortHandler.isInBoundsX(positions[i])) {

                    String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis);

                    if (mXAxis.isAvoidFirstLastClippingEnabled()) {

                        // avoid clipping of the last mXAxis.mEntryCount-1为x轴标签数
                        if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) {
                            float width = Utils.calcTextWidth(mAxisLabelPaint, label);

                            if (width > mViewPortHandler.offsetRight() * 2
                                    && x + width > mViewPortHandler.getChartWidth())
                                positions[i] -= width / 2;
                            // avoid clipping of the first
                        } else if (i == 0) {

                            float width = Utils.calcTextWidth(mAxisLabelPaint, label);
                            positions[i] += width / 2;
                        }
                    }
                    drawLabel(c, label, positions[i], pos, anchor, labelRotationAngleDegrees);
                }
            }

        }

    }
3、创建一个MyLineChart

代码

    package com.mpandroidchartcsdn.mychart;

            import android.content.Context;
            import android.util.AttributeSet;

            import com.github.mikephil.charting.charts.LineChart;

    /**
     * Created by tujingwu on 2017/5/4 
     * . 
     */

    public class MyLineChart extends LineChart {

        public MyLineChart(Context context) {
            super(context);
        }

        public MyLineChart(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        @Override
        protected void init() {
            super.init();
            mXAxisRenderer = new MyXAxisRenderer(mViewPortHandler, mXAxis, mLeftAxisTransformer,this);
        }

    } 

最后运行效果如下gif图

这样一来上面存在的2个问题都得的解决后,得到下面最终gif图

总结:对于缩放最终得到解决,给我的一些思路还是来自 http://blog.csdn.net/qqyanjiang/article/details/51442120 这位兄弟的博客,虽然博客里没有怎么详细说,不过还是提供了一些思路,现在解决了上面我提到的2个问题,后面有时间会把联动和蜡烛图也写了。

标签: application

添加新评论