MPAndroidChart实现折线图不同区间范围的不同的颜色

废话少说先上图。(支持Y轴放大缩小)

不同范围彩色折线图

40~125一个颜色 125~147一个颜色 147~188一个颜色  188以上是一个颜色。自己测试随便订的范围,无实际意义。

开始撸代码、首先继承MPAndroidChart里的LineChart类

public class MyLineChart extends LineChart {
    public MyLineChart(Context context) {
        super(context);
    }

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

    public MyLineChart(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    @Override
    protected void init() {
        super.init();
        //mRenderer 渲染器 设置为自己定义的渲染器
        mRenderer = new MyLineChartRenderer(this, mAnimator, mViewPortHandler);
    }
    @Override
    public LineData getLineData() {
        return mData;
    }

    @Override
    protected void onDetachedFromWindow() {
        // releases the bitmap in the renderer to avoid oom error
        if (mRenderer != null && mRenderer instanceof MyLineChartRenderer) {
            ((MyLineChartRenderer) mRenderer).releaseBitmap();
        }
        super.onDetachedFromWindow();
    }
}

然后继承折线图的渲染器LineChartRenderer类进行重写。代码如下

public class MyLineChartRenderer extends LineChartRenderer {
    private Paint mHighlightCirclePaint;
    private boolean isHeart; 
    float[] pos; //颜色区间的位置
    int[] colors; //区间的颜色
    int[] range; //区间范围的值  如果不需要判断高亮小圆点的值  可以删除
    private ViewPortHandler viewPortHandler;

    public MyLineChartRenderer(LineDataProvider chart, ChartAnimator animator,
                               ViewPortHandler viewPortHandler) {
        super(chart, animator, viewPortHandler);
        mChart = chart;
        mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaintInner.setStyle(Paint.Style.FILL);
        mCirclePaintInner.setColor(Color.WHITE);
        mHighlightCirclePaint = new Paint();
        this.viewPortHandler = viewPortHandler;
    }

    private float[] mLineBuffer = new float[4];

    @Override
    protected void drawLinear(Canvas c, ILineDataSet dataSet) {

        int entryCount = dataSet.getEntryCount();

        final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled();
        final int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2;

        Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

        float phaseY = mAnimator.getPhaseY();

        mRenderPaint.setStyle(Paint.Style.STROKE);

        Canvas canvas = null;

        // if the data-set is dashed, draw on bitmap-canvas
        if (dataSet.isDashedLineEnabled()) {
            canvas = mBitmapCanvas;
        } else {
            canvas = c;
        }

        mXBounds.set(mChart, dataSet);

        // if drawing filled is enabled
        if (dataSet.isDrawFilledEnabled() && entryCount > 0) {
            drawLinearFill(c, dataSet, trans, mXBounds);
        }

        // more than 1 color
        if (dataSet.getColors().size() > 1) {

            if (mLineBuffer.length <= pointsPerEntryPair * 2)
                mLineBuffer = new float[pointsPerEntryPair * 4];

            for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) {

                Entry e = dataSet.getEntryForIndex(j);
                if (e == null) continue;
                if (e.getY() == 0) continue;
                mLineBuffer[0] = e.getX();
                mLineBuffer[1] = e.getY() * phaseY;

                if (j < mXBounds.max) {

                    e = dataSet.getEntryForIndex(j + 1);

                    if (e == null) break;
                    if (e.getY() == 0) break;
                    if (isDrawSteppedEnabled) {
                        mLineBuffer[2] = e.getX();
                        mLineBuffer[3] = mLineBuffer[1];
                        mLineBuffer[4] = mLineBuffer[2];
                        mLineBuffer[5] = mLineBuffer[3];
                        mLineBuffer[6] = e.getX();
                        mLineBuffer[7] = e.getY() * phaseY;
                    } else {
                        mLineBuffer[2] = e.getX();
                        mLineBuffer[3] = e.getY() * phaseY;
                    }

                } else {
                    mLineBuffer[2] = mLineBuffer[0];
                    mLineBuffer[3] = mLineBuffer[1];
                }

                trans.pointValuesToPixel(mLineBuffer);

                if (!mViewPortHandler.isInBoundsRight(mLineBuffer[0]))
                    break;

                // make sure the lines don't do shitty things outside
                // bounds
                if (!mViewPortHandler.isInBoundsLeft(mLineBuffer[2])
                        || (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler
                        .isInBoundsBottom(mLineBuffer[3])))
                    continue;

                // get the color that is set for this line-segment
                mRenderPaint.setColor(dataSet.getColor(j));

                canvas.drawLines(mLineBuffer, 0, pointsPerEntryPair * 2, mRenderPaint);

            }

        } else { // only one color per dataset

            if (mLineBuffer.length < Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2)
                mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 4];

            Entry e1, e2;

            e1 = dataSet.getEntryForIndex(mXBounds.min);

            if (e1 != null) {

                int j = 0;
                for (int x = mXBounds.min; x <= mXBounds.range + mXBounds.min; x++) {

                    e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1));
                    e2 = dataSet.getEntryForIndex(x);

                  
                    if (e1.getY() == 0 || e2.getY() == 0) {
                        continue;
                    }

                  mLineBuffer[j++] = e1.getX();
                    mLineBuffer[j++] = e1.getY() * phaseY;

                    if (isDrawSteppedEnabled) {
                        mLineBuffer[j++] = e2.getX();
                        mLineBuffer[j++] = e1.getY() * phaseY;
                        mLineBuffer[j++] = e2.getX();
                        mLineBuffer[j++] = e1.getY() * phaseY;
                    }

                    mLineBuffer[j++] = e2.getX();
                    mLineBuffer[j++] = e2.getY() * phaseY;
                }

                if (j > 0) {
                    trans.pointValuesToPixel(mLineBuffer);

                    final int size = Math.max((mXBounds.range + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2;

                    mRenderPaint.setColor(dataSet.getColor());
                    if (isHeart) {
                        LinearGradient liner = new LinearGradient(0, mViewPortHandler.getContentRect().top,
                                0, mViewPortHandler.getContentRect().bottom, colors, pos, Shader.TileMode.CLAMP)
                        liner.setLocalMatrix(viewPortHandler.getMatrixTouch())
                        mRenderPaint.setShader(liner);
                    }
                    canvas.drawLines(mLineBuffer, 0, size, mRenderPaint);
                }
            }
        }

        mRenderPaint.setPathEffect(null);
    }

    /**
     * cache for the circle bitmaps of all datasets
     */
    private HashMap<IDataSet, DataSetImageCache> mImageCaches = new HashMap<>();
    private float[] mCirclesBuffer = new float[2];

    @Override
    protected void drawCircles(Canvas c) {
        mRenderPaint.setStyle(Paint.Style.FILL);

        float phaseY = mAnimator.getPhaseY();

        mCirclesBuffer[0] = 0;
        mCirclesBuffer[1] = 0;

        List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();

        for (int i = 0; i < dataSets.size(); i++) {

            ILineDataSet dataSet = dataSets.get(i);

            if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() ||
                    dataSet.getEntryCount() == 0)
                continue;

            mCirclePaintInner.setColor(dataSet.getCircleHoleColor());

            Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

            mXBounds.set(mChart, dataSet);

            float circleRadius = dataSet.getCircleRadius();
            float circleHoleRadius = dataSet.getCircleHoleRadius();
            boolean drawCircleHole = dataSet.isDrawCircleHoleEnabled() &&
                    circleHoleRadius < circleRadius &&
                    circleHoleRadius > 0.f;
            boolean drawTransparentCircleHole = drawCircleHole &&
                    dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE;

            DataSetImageCache imageCache;

            if (mImageCaches.containsKey(dataSet)) {
                imageCache = mImageCaches.get(dataSet);
            } else {
                imageCache = new DataSetImageCache();
                mImageCaches.put(dataSet, imageCache);
            }

            boolean changeRequired = imageCache.init(dataSet);

            // only fill the cache with new bitmaps if a change is required
            if (changeRequired) {
                imageCache.fill(dataSet, drawCircleHole, drawTransparentCircleHole);
            }

            int boundsRangeCount = mXBounds.range + mXBounds.min;

            for (int j = mXBounds.min; j <= boundsRangeCount; j++) {

                Entry e = dataSet.getEntryForIndex(j);

                if (e == null) break;
                if (e.getY() == 0) continue;
                mCirclesBuffer[0] = e.getX();
                mCirclesBuffer[1] = e.getY() * phaseY;

                trans.pointValuesToPixel(mCirclesBuffer);

                if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0]))
                    break;

                if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) ||
                        !mViewPortHandler.isInBoundsY(mCirclesBuffer[1]))
                    continue;

                Bitmap circleBitmap = imageCache.getBitmap(j);

                if (circleBitmap != null) {
                    c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, null);
                }
            }
        }
    }

    private class DataSetImageCache {

        private Path mCirclePathBuffer = new Path();

        private Bitmap[] circleBitmaps;

        /**
         * Sets up the cache, returns true if a change of cache was required.
         *
         * @param set
         * @return
         */
        protected boolean init(ILineDataSet set) {

            int size = set.getCircleColorCount();
            boolean changeRequired = false;

            if (circleBitmaps == null) {
                circleBitmaps = new Bitmap[size];
                changeRequired = true;
            } else if (circleBitmaps.length != size) {
                circleBitmaps = new Bitmap[size];
                changeRequired = true;
            }

            return changeRequired;
        }

        /**
         * Fills the cache with bitmaps for the given dataset.
         *
         * @param set
         * @param drawCircleHole
         * @param drawTransparentCircleHole
         */
        protected void fill(ILineDataSet set, boolean drawCircleHole, boolean drawTransparentCircleHole) {

            int colorCount = set.getCircleColorCount();
            float circleRadius = set.getCircleRadius();
            float circleHoleRadius = set.getCircleHoleRadius();

            for (int i = 0; i < colorCount; i++) {

                Bitmap.Config conf = Bitmap.Config.ARGB_4444;
                Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf);

                Canvas canvas = new Canvas(circleBitmap);
                circleBitmaps[i] = circleBitmap;
                mRenderPaint.setColor(set.getCircleColor(i));

                if (drawTransparentCircleHole) {
                    // Begin path for circle with hole
                    mCirclePathBuffer.reset();

                    mCirclePathBuffer.addCircle(
                            circleRadius,
                            circleRadius,
                            circleRadius,
                            Path.Direction.CW);

                    // Cut hole in path
                    mCirclePathBuffer.addCircle(
                            circleRadius,
                            circleRadius,
                            circleHoleRadius,
                            Path.Direction.CCW);

                    // Fill in-between
                    canvas.drawPath(mCirclePathBuffer, mRenderPaint);
                } else {

                    canvas.drawCircle(
                            circleRadius,
                            circleRadius,
                            circleRadius,
                            mRenderPaint);

                    if (drawCircleHole) {
                        canvas.drawCircle(
                                circleRadius,
                                circleRadius,
                                circleHoleRadius,
                                mCirclePaintInner);
                    }
                }
            }
        }

        /**
         * Returns the cached Bitmap at the given index.
         *
         * @param index
         * @return
         */
        protected Bitmap getBitmap(int index) {
            return circleBitmaps[index % circleBitmaps.length];
        }
    }
    /***
     * 对高亮的值进行显示小圆点  如果没有此需要可删除
     * */
    @Override
    public void drawHighlighted(Canvas c, Highlight[] indices) {
        super.drawHighlighted(c, indices);

        float phaseY = mAnimator.getPhaseY();
        ILineDataSet lineData = mChart.getLineData().getDataSetByIndex(0);
        Transformer trans = mChart.getTransformer(lineData.getAxisDependency());
        mCirclesBuffer[0] = 0;
        mCirclesBuffer[1] = 0;

        for (Highlight high : indices) {
            Entry e = lineData.getEntryForXValue(high.getX(), high.getY());
            mCirclesBuffer[0] = e.getX();
            mCirclesBuffer[1] = e.getY() * phaseY;
            trans.pointValuesToPixel(mCirclesBuffer);
            mHighlightCirclePaint.setColor(lineData.getHighLightColor());
            //根据不同的区间显示小圆点的颜色
            if (isHeart) {
                if (e.getY() >= range[0]) {
                    mHighlightCirclePaint.setColor(colors[0]);
                } else if (e.getY() < range[0] && e.getY() >= range[1]) {
                    mHighlightCirclePaint.setColor(colors[2]);
                } else if (e.getY() >= range[2] && e.getY() < range[1]) {
                    mHighlightCirclePaint.setColor(colors[4]);
                } else {
                    mHighlightCirclePaint.setColor(colors[6]);
                }
            }
            c.drawCircle(mCirclesBuffer[0], mCirclesBuffer[1], 10, mHighlightCirclePaint);
            mHighlightCirclePaint.setColor(Color.WHITE);
            c.drawCircle(mCirclesBuffer[0], mCirclesBuffer[1], 5, mHighlightCirclePaint);
        }
    }

    @Override
    public void drawHorizontalBezier(ILineDataSet dataSet) {

        float phaseY = mAnimator.getPhaseY();

        Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

        mXBounds.set(mChart, dataSet);

        cubicPath.reset();

        if (mXBounds.range >= 1) {

            Entry prev = dataSet.getEntryForIndex(mXBounds.min);
            Entry cur = prev;

            // let the spline start
            cubicPath.moveTo(cur.getX(), cur.getY() * phaseY);

            for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) {

                prev = cur;
                cur = dataSet.getEntryForIndex(j);

                final float cpx = (prev.getX())
                        + (cur.getX() - prev.getX()) / 2.0f;

                cubicPath.cubicTo(
                        cpx, prev.getY() * phaseY,
                        cpx, cur.getY() * phaseY,
                        cur.getX(), cur.getY() * phaseY);
            }
        }

        // if filled is enabled, close the path
        if (dataSet.isDrawFilledEnabled()) {

            cubicFillPath.reset();
            cubicFillPath.addPath(cubicPath);
            // create a new path, this is bad for performance
            drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds);
        }

        mRenderPaint.setColor(dataSet.getColor());

        mRenderPaint.setStyle(Paint.Style.STROKE);

        trans.pathValueToPixel(cubicPath);
       if (isHeart) {
          LinearGradient liner = new LinearGradient(0,mViewPortHandler.getContentRect().top,0,mViewPortHandler.getContentRect().bottom, colors, pos, Shader.TileMode.CLAMP)
          liner.setLocalMatrix(viewPortHandler.getMatrixTouch())
          mRenderPaint.setShader(liner);
        }


        mBitmapCanvas.drawPath(cubicPath, mRenderPaint);

        mRenderPaint.setPathEffect(null);
    }
    /***
     * @param isHeart true 开启分区间显示的颜色
     * @param medium 不同层级的判断条件 
     * @param colors 不同区间的颜色值,从上到下的颜色 我这里是3个值  那么分成四段 colors数组长度就为4              
     * */
    public void setHeartLine(boolean isHeart, int medium, int larger, int limit, int[] colors) {
        this.isHeart = isHeart;
        range = new int[3];
        range[0] = limit;
        range[1] = larger;
        range[2] = medium;
        float[] pos = new float[4];
        float Ymax = ((LineChart) mChart).getAxisLeft().getAxisMaximum();
        float Ymin = ((LineChart) mChart).getAxisLeft().getAxisMinimum();
        pos[0] = (Ymax - limit) / (Ymax - Ymin);
        pos[1] = (limit - larger) / (Ymax - Ymin) + pos[0];
        pos[2] = (larger - medium) / (Ymax - Ymin) + pos[1];
        pos[3] = 1f;

        this.pos = new float[pos.length * 2];
        this.colors = new int[colors.length * 2];
        int index = 0;
        for (int i = 0; i < pos.length; i++) {
            this.colors[index] = colors[i];
            this.colors[index + 1] = colors[i];
            if (i == 0) {
                this.pos[index] = 0f;
                this.pos[index + 1] = pos[i];
            } else {
                this.pos[index] = pos[i - 1];
                this.pos[index + 1] = pos[i];
            }

            index += 2;
        }
    }
}

开始使用,布局中引入

<你的类路径.MyLineChart
        android:id="@+id/line_chart"
        android:layout_width="match_parent"
        android:layout_height="200dp"/>

 然后设置chart的XY轴样式等等。设置值之前需要设置你的区间颜色等等信息代码如下

 if (line_chart.getRenderer() instanceof MyLineChartRenderer) {
            MyLineChartRenderer renderer = (MyLineChartRenderer) line_chart.getRenderer();

            int medium = 125;
            int larger = 147;
            int limit = 188;


            int[] colors = new int[4];
            colors[0] = Color.parseColor("#fa7069");
            colors[1] = Color.parseColor("#faa369");
            colors[2] = Color.parseColor("#facd69");
            colors[3] = Color.parseColor("#d3d8dc");

            renderer.setHeartLine(true, medium,larger,limit, colors);
        }

实现思路:

LinearGradient线性渐变类。对Y轴不同区间占比进行计算,设置渐变色的位置pos数组。进行渐变。两个相同的颜色渐变不会有任何变化。所以设置4个颜色,实际设置进去的渐变色有8个。

核心代码(之前代码不支持Y轴放大缩小,现在支持Y轴放大和缩小)

if (isHeart) {
      LinearGradient liner = new LinearGradient(0, mViewPortHandler.getContentRect().top,0, mViewPortHandler.getContentRect().bottom, colors, pos, Shader.TileMode.CLAMP)
      liner.setLocalMatrix(viewPortHandler.getMatrixTouch())
      mRenderPaint.setShader(liner);
}

 Y轴支持放大缩小原理是获取到chart的根据收拾放大的触摸矩阵(Matrix)

Matrix自行百度,我就不过多解释了。

 public void setHeartLine(boolean isHeart, int medium, int larger, int limit, int[] colors) {
        this.isHeart = isHeart;
        range = new int[3];
        range[0] = limit;
        range[1] = larger;
        range[2] = medium;
        float[] pos = new float[4];
        float Ymax = ((LineChart) mChart).getAxisLeft().getAxisMaximum();
        float Ymin = ((LineChart) mChart).getAxisLeft().getAxisMinimum();
        pos[0] = (Ymax - limit) / (Ymax - Ymin);
        pos[1] = (limit - larger) / (Ymax - Ymin) + pos[0];
        pos[2] = (larger - medium) / (Ymax - Ymin) + pos[1];
        pos[3] = 1f;

        this.pos = new float[pos.length * 2];
        this.colors = new int[colors.length * 2];
        int index = 0;
        for (int i = 0; i < pos.length; i++) {
            this.colors[index] = colors[i];
            this.colors[index + 1] = colors[i];
            if (i == 0) {
                this.pos[index] = 0f;
                this.pos[index + 1] = pos[i];
            } else {
                this.pos[index] = pos[i - 1];
                this.pos[index + 1] = pos[i];
            }

            index += 2;
        }
    }

最终效果:最终效果

三条红线是LimitLine分界线 删除即可

源代码下载

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
你可以使用 `LimitLine` 和 `YAxis` 的 `addLimitLine()` 方法来实现根据不同的值范围填充不同颜色。下面是一个示例代码: ```java // 创建 LimitLine 并设置它的位置和值 LimitLine limitLine1 = new LimitLine(50f, "Limit 1"); limitLine1.setLineColor(Color.RED); limitLine1.setLineWidth(2f); limitLine1.enableDashedLine(10f, 10f, 0f); LimitLine limitLine2 = new LimitLine(100f, "Limit 2"); limitLine2.setLineColor(Color.GREEN); limitLine2.setLineWidth(2f); limitLine2.enableDashedLine(10f, 10f, 0f); // 将 LimitLine 添加到 YAxis 上 YAxis yAxisLeft = lineChart.getAxisLeft(); yAxisLeft.addLimitLine(limitLine1); yAxisLeft.addLimitLine(limitLine2); // 设置填充色 Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red); lineChart.getRenderer().setFillFormatter(new MyFillFormatter(drawable)); ``` 在上面的代码中,我们首先创建了两个 `LimitLine` 对象,并设置它们的位置和样式。然后,将这两个 `LimitLine` 添加到 `YAxis` 的实例上。最后,我们设置了填充色,并将其传递给自定义的 `MyFillFormatter` 对象,这个对象负责根据图表的值范围来填充不同颜色。 ```java public class MyFillFormatter implements IFillFormatter { private Drawable drawable; public MyFillFormatter(Drawable drawable) { this.drawable = drawable; } @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { return 0; } @Override public Drawable getFillDrawable(ILineDataSet dataSet) { return drawable; } } ``` 在自定义的 `MyFillFormatter` 类中,我们实现了 `IFillFormatter` 接口,重写了其中的两个方法。其中,`getFillLinePosition()` 方法用于返回填充线的位置,这里我们返回了 0,表示使用默认的填充线位置。`getFillDrawable()` 方法用于返回填充色的 `Drawable` 对象,这里我们直接返回了构造函数中传递的 `Drawable` 对象。 这样,当你的折线图的值范围跨越了 `LimitLine` 对象所指定的值时,就会自动填充设置的颜色了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值