重写MPAndroidChart显示标记

欢迎Follow我的GitHub, 关注我的简书.

效果

本文的合集已经编著成书,高级Android开发强化实战,欢迎各位读友的建议和指导。在京东即可购买:https://item.jd.com/12385680.html

Android

MPAndroidChart是实现图表功能的优秀控件, 可以完成大多数绘制需求. 对于修改第三方库而言, 优秀的架构是继承开发, 而不是把源码拆分出去. MP在显示标记控件(MarkView)时, 会有异常, 导致标志在图表边缘显示不全, 则需要重写控件解决问题.

继承LineChart, 提取高亮位置坐标getHighLightPos, 重绘标记drawMarkers.

/**
 * 数据中心的图表折线图, 继承MPChart的折线图
 * <p>
 * Created by wangchenlong on 15/10/13.
 */
public class CYDataLineChart extends LineChart {

    @SuppressWarnings("unused")
    private static final String TAG = "DEBUG-WCL: " + CYDataLineChart.class.getSimpleName();

    // 默认构造器
    public CYDataLineChart(Context context) {
        super(context);
    }

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

    public CYDataLineChart(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    // 获取高亮点坐标
    public float[] getHighLightPos(Entry e, Highlight highlight) {
        return getMarkerPosition(e, highlight);
    }

    // 重写这个方法, 修复Bug
    @Override
    protected void drawMarkers(Canvas canvas) {

        // if there is no marker view or drawing marker is disabled
        if (mMarkerView == null || !mDrawMarkerViews || !valuesToHighlight())
            return;

        Rect newRect = canvas.getClipBounds();
        newRect.inset(-80, 0);  //make the rect larger

        canvas.clipRect(newRect, Region.Op.REPLACE);

        //noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];
            int xIndex = highlight.getXIndex();

            if (xIndex <= mDeltaX && xIndex <= mDeltaX * mAnimator.getPhaseX()) {

                Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);

                // make sure entry not null
                if (e == null || e.getXIndex() != mIndicesToHighlight[i].getXIndex())
                    continue;

                float[] pos = getMarkerPosition(e, highlight);

                // Marker偏移
                float tmpY = pos[1] - 8 * AppUtils.getPerDp();

                Paint paint = new Paint();
                paint.setStyle(Paint.Style.FILL);
                paint.setAntiAlias(true);
                paint.setStrokeWidth(5);

                // noinspection deprecation
                paint.setColor(getResources().getColor(R.color.chart_circle));
                canvas.drawCircle(pos[0], pos[1], 2 * AppUtils.getPerDp(), paint);

                // check bounds
                if (!mViewPortHandler.isInBounds(pos[0], tmpY))
                    continue;

                mMarkerView.refreshContent(e, highlight);

                mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(),
                        mMarkerView.getMeasuredHeight());

                if (tmpY - mMarkerView.getHeight() <= 0) {
                    float y = mMarkerView.getHeight() - tmpY;
                    mMarkerView.draw(canvas, pos[0], tmpY + y);
                } else {
                    mMarkerView.draw(canvas, pos[0], tmpY);
                }
            }
        }
    }
}

getMarkerPosition是LineChart类中的protected方法, 继承类, 使用public方法导出.
float tmpY = pos[1] - 8 * AppUtils.getPerDp();, 重新计算Y坐标, 偏离原始画布.

但是这样做有一个问题, 在移动MarkView时, 父控件会有残留. 如何解决呢? 办法就是在移动时, 重绘父控件的canvas, 使用invalidate()函数.

        // 设置图表点击事件, 监听高亮位置
        mLcChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
            @Override
            public void onValueSelected(Entry e, int dataSetIndex, Highlight h) {
                int index = e.getXIndex();
                Log.e(TAG, "index = " + index);
                setChartIndex(index);
                mCallback.setCurIndex(index);
                mIndex = index;

                float[] pos = mLcChart.getHighLightPos(e, h);
                Log.e(TAG, "x: " + pos[0] + ", y: " + pos[1]);

                mLlContainer.invalidate(); // 重绘父控件, 避免残留
            }

            @Override
            public void onNothingSelected() {
                // 再次点击时调用这个, 要不非高亮
                mLcChart.highlightValue(mIndex, 0);
            }
        });
    // 外部设置图表高亮
    private void setChartHighlight(int index) {
        if (mLcChart.getData() == null) return;

        mMarkerView.setDateText(mMarkers.get(index));
        mLcChart.highlightValue(index, 0);

        mLlContainer.invalidate(); // 重绘父控件, 避免残留
    }

在图表控件中, 内部外部都会触发高亮位置.

动画

OK, Enjoy It!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,192评论 4 61
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,909评论 25 708
  • Swift版本点击这里欢迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh阅读 25,522评论 7 249
  • 儿子是个重情之人,每年虽只能与表弟相聚数日,却是念念不忘,端午节跟我回去探亲,俩娃在一起有说不完的话。一起划船,一...
    Flora1975阅读 131评论 0 0
  • 做微商到现在已经有一个月的时间了,在这一个月里,我经历了很多事情,我觉得我忘记了很多以前的经历了,这些都是以前在快...
    圆润的桃子阅读 526评论 0 0