精华内容
下载资源
问答
  • MarkerView

    千次阅读 2018-05-08 10:14:59
    过了一个愉快的五一后,我们又开始上班了,完成了本分的工作,抽点时间来和说说MarkerView咯。给我的印象,MarkerView的扩展性很强。它可以自定义自己想要的U样式. MarkerView源码 /** * View that can be ...

    前言:

    过了一个愉快的五一后,我们又开始上班了,完成了本分的工作,抽点时间来和说说MarkerView咯。给我的印象,MarkerView的扩展性很强。它可以自定义自己想要的U样式.

    MarkerView源码

    /**
     * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your
     * markers.
     *
     * @author Philipp Jahoda
     */
    public class MarkerView extends RelativeLayout implements IMarker {
    
        private MPPointF mOffset = new MPPointF();
        private MPPointF mOffset2 = new MPPointF();
        private WeakReference<Chart> mWeakChart;
    
        /**
         * Constructor. Sets up the MarkerView with a custom layout resource.
         *
         * @param context
         * @param layoutResource the layout resource to use for the MarkerView
         */
        public MarkerView(Context context, int layoutResource) {
            super(context);
            setupLayoutResource(layoutResource);
        }
    
        /**
         * Sets the layout resource for a custom MarkerView.
         *
         * @param layoutResource
         */
        private void setupLayoutResource(int layoutResource) {
    
            View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this);
    
            inflated.setLayoutParams(new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
            inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    
            // measure(getWidth(), getHeight());
            inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight());
        }
    
        public void setOffset(MPPointF offset) {
            mOffset = offset;
    
            if (mOffset == null) {
                mOffset = new MPPointF();
            }
        }
    
        public void setOffset(float offsetX, float offsetY) {
            mOffset.x = offsetX;
            mOffset.y = offsetY;
        }
    
        @Override
        public MPPointF getOffset() {
            return mOffset;
        }
    
        public void setChartView(Chart chart) {
            mWeakChart = new WeakReference<>(chart);
        }
    
        public Chart getChartView() {
            return mWeakChart == null ? null : mWeakChart.get();
        }
    
        @Override
        public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
    
            MPPointF offset = getOffset();
            mOffset2.x = offset.x;
            mOffset2.y = offset.y;
    
            Chart chart = getChartView();
    
            float width = getWidth();
            float height = getHeight();
    
            if (posX + mOffset2.x < 0) {
                mOffset2.x = - posX;
            } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
                mOffset2.x = chart.getWidth() - posX - width;
            }
    
            if (posY + mOffset2.y < 0) {
                mOffset2.y = - posY;
            } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) {
                mOffset2.y = chart.getHeight() - posY - height;
            }
    
            return mOffset2;
        }
    
        @Override
        public void refreshContent(Entry e, Highlight highlight) {
    
            measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
    
        }
    
        @Override
        public void draw(Canvas canvas, float posX, float posY) {
    
            MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
    
            int saveId = canvas.save();
            // translate to the correct position and draw
            canvas.translate(posX + offset.x, posY + offset.y);
            draw(canvas);
            canvas.restoreToCount(saveId);
        }
    }

    MarkerView继承了RelativeLayout,所以说它相当于一个View容器,你只管往里面放View就好了。至于啥样子,就不管了,由你来掌控。它还实现了IMarker接口,IMarker接口中的方法有如下几个:

        MPPointF getOffset();
    
        MPPointF getOffsetForDrawingAtPoint(float posX, float posY);
    
        void refreshContent(Entry e, Highlight highlight);
    
        void draw(Canvas canvas, float posX, float posY);

    draw方法,就是去画你自定义的MarkerView的内容,posX,posY,就是画在指定的点。
    refreshContent方法,顾名思义,就是刷新MarkerView的内容。传了两个参数,一个是Entry,一个是Highlight,可以知道,这个与Highlight有密切的关系。也就是说,只有在选择了一个Entry高亮显示了,才会画出这个刷新画出MarkerView。
    getOffsetForDrawingAtPoint方法,与画的点的位置有关系,一个偏移量
    getOffset方法,就是偏移量咯。

    实际使用

    在MarkerView中还有一个方法被我们忽视了,setupLayoutResource(int layoutResource)。这个方法就是加载你自定义的xml文件。我们来看下demo中自定义的MarkerView。

    public class MyMarkerView extends MarkerView {
    
        private TextView tvContent;
    
        public MyMarkerView(Context context, int layoutResource) {
            super(context, layoutResource);
    
            tvContent = (TextView) findViewById(R.id.tvContent);
        }
    
        // callbacks everytime the MarkerView is redrawn, can be used to update the
        // content (user-interface)
        @Override
        public void refreshContent(Entry e, Highlight highlight) {
    
            if (e instanceof CandleEntry) {
    
                CandleEntry ce = (CandleEntry) e;
    
                tvContent.setText("" + Utils.formatNumber(ce.getHigh(), 0, true));
            } else {
    
                tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true));
            }
    
            super.refreshContent(e, highlight);
        }
    
        @Override
        public MPPointF getOffset() {
            return new MPPointF(-(getWidth() / 2), -getHeight());
        }
    }

    重写了refreshContent方法 , getOffset()方法。如何使用的呢?

     MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view);
            mv.setChartView(mChart); // For bounds control
            mChart.setMarker(mv); // Set the marker to the chart

    先new 一个MyMarkerView,然后设置到Chart中去。在绘制的时候就可以绘制你自定义的xml了。在Chart中有一个drawMarkers方法:

    protected void drawMarkers(Canvas canvas) {
    
            // if there is no marker view or drawing marker is disabled
            if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight())
                return;
    
            for (int i = 0; i < mIndicesToHighlight.length; i++) {
    
                Highlight highlight = mIndicesToHighlight[i];
    
                IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());
    
                Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
                int entryIndex = set.getEntryIndex(e);
    
                // make sure entry not null
                if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                    continue;
    
                float[] pos = getMarkerPosition(highlight);
    
                // check bounds
                if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                    continue;
    
                // callbacks to update the content
                mMarker.refreshContent(e, highlight);
    
                // draw the marker
                mMarker.draw(canvas, pos[0], pos[1]);
            }
        }

    最主要的就是最后两行代码,先刷新UI,再去画。以上就是对MarkerView的介绍了。下次再会!

    展开全文
  • MarkerView 顾名思义就是标记视图,点击图表上的数值时一般都需要一个提示框用于显示数值,MPAndroidChart 也提供了对应的接口,setMarker(MarkerView)。 效果图如下: 这边画的比较简陋,布局可以自定义: 一、...

    MarkerView 顾名思义就是标记视图,点击图表上的数值时一般都需要一个提示框用于显示数值,MPAndroidChart 也提供了对应的接口,setMarker(MarkerView)
    效果图如下:
    在这里插入图片描述
    这边画的比较简陋,布局可以自定义:

    一、绘制图表

    堆叠柱状图的实现可以参考我的另外一篇文章,MPAndroidChart实现堆叠柱状图

    二、自定义 MarkerView

    public class MyMarkView extends MarkerView {
      private TextView tv1;
      private TextView tv2;
    
      public MyMarkView(Context context) {
        super(context, R.layout.mark_view);
        initView();
      }
    
      private void initView() {
        tv1 = findViewById(R.id.tv1);
        tv2 = findViewById(R.id.tv2);
      }
    
      @Override
      public void refreshContent(Entry e, Highlight highlight) {
    	//更新显示数据,由于是柱状图,所以可以强制转换为BarEntry
        BarEntry barEntry = (BarEntry) e;
        //获取Y值列表
        float[] values = barEntry.getYVals();
    
        tv1.setText(String.format(Locale.US, "数据1: %.2f", values[0]));
        tv2.setText(String.format(Locale.US, "数据2: %.2f", values[1]));
        super.refreshContent(e, highlight);
      }
    
      private MPPointF mOffset;
    
      @Override
      public MPPointF getOffset() {
      	//设置MarkerView的偏移量,就是提示框显示的位置
        if (mOffset == null) {
          // center the marker horizontally and vertically
          mOffset = new MPPointF(-(getWidth() / 2), -getHeight());
        }
    
        return mOffset;
      }
    }
    

    三、添加 MarkerView

    初始化图表时,增加以下代码:

        MyMarkView myMarkView = new MyMarkView(this);
        //必须设置,否则MarkerView会超出图表
        myMarkView.setChartView(barChart);
        barChart.setMarker(myMarkView);
    

    四、MarkerView 显示位置

    MarkerView 显示位置主要在 getOffsetForDrawingAtPoint 方法中进行计算的,具体源码如下:

        @Override
        public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
    		//获取我们设置的偏移量
            MPPointF offset = getOffset();
            mOffset2.x = offset.x;
            mOffset2.y = offset.y;
    		//获取图表对象,如果没有调用setChartView方法,该chart就为空
            Chart chart = getChartView();
    		//获取 MarkerView 的宽和高
            float width = getWidth();
            float height = getHeight();
    
    		//计算最终位置
            if (posX + mOffset2.x < 0) {
                mOffset2.x = - posX;
            } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
                mOffset2.x = chart.getWidth() - posX - width;
            }
    
            if (posY + mOffset2.y < 0) {
                mOffset2.y = - posY;
            } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) {
                mOffset2.y = chart.getHeight() - posY - height;
            }
    
            return mOffset2;
        }
    
    

    如果想自定义显示位置,直接重写该方法即可。

    展开全文
  • MPAndroidChart项目实战——MarkerView显示问题解决

    千次阅读 热门讨论 2017-02-24 15:37:13
    在运用三方开源库时常会遇到不满足需求的情况,比喻MPAndroidChart的MarkerView显示问题。 二丶解决后效果图展示 说明,未修改前MarkerView会出现显示不全问题(弹框位置在图表外不显示) 三丶关键代码 思路:当...

    Demo补充中(UpDating):https://github.com/JinBoy23520/MPAndroidChartDemoByJin

    本文出自:http://blog.csdn.net/dt235201314/article/details/56841839

    一丶慨述

    在运用三方开源库时常会遇到不满足需求的情况,比喻MPAndroidChart的MarkerView显示问题。

    二丶解决后效果图展示


    说明,未修改前MarkerView会出现显示不全问题(弹框位置在图表外不显示)

    三丶关键代码

    思路:当弹框显示位置大于布局位置,做判断重写MarkerView类

    重写MarkerView类:

    public class NewMarkerView extends MarkerView {
    
        private TextView tvContent;
        private CallBack mCallBack;
    
        public NewMarkerView(Context context, int layoutResource) {
            super(context, layoutResource);
    
            tvContent = (TextView) findViewById(R.id.tvContent);
        }
    
        // callbacks everytime the MarkerView is redrawn, can be used to update the
        // content (user-interface)
        @Override
        public void refreshContent(Entry e, Highlight highlight) {
    
            String values;
            if (e instanceof CandleEntry) {
                CandleEntry ce = (CandleEntry) e;
                values = "" + Utils.formatNumber(ce.getHigh(), 0, true);
            } else {
                values = "" + Utils.formatNumber(e.getY(), 0, true);
            }
    
            if (mCallBack != null) {
                mCallBack.onCallBack(e.getX(), values);
            }
            super.refreshContent(e, highlight);
        }
    
        @Override
        public MPPointF getOffset() {
            tvContent.setBackgroundResource(R.drawable.chart_popu);
            return new MPPointF(0, -getHeight());
        }
    
        @Override
        public MPPointF getOffsetRight() {
            tvContent.setBackgroundResource(R.drawable.chart_popu_right);
            return new MPPointF(-getWidth(), -getHeight());
        }
    
        public void setCallBack (CallBack callBack) {
            this.mCallBack = callBack;
        }
        public interface CallBack {
            void onCallBack(float x, String value);
        }
    
        public TextView getTvContent() {
            return tvContent;
        }
    }
    这里方法NewMarkerView()用来绑定布局

    方法refreshContent()用来获取最点击坐标点的值

    下面的方法则是设置弹框布局显示位置

    MarkerView类添加判断回调markview的方法,直接上修改后代码

    public class MarkerView extends RelativeLayout implements IMarker {
    
        private MPPointF mOffset = new MPPointF();
        private MPPointF mOffset2 = new MPPointF();
        private WeakReference<Chart> mWeakChart;
    
        /**
         * Constructor. Sets up the MarkerView with a custom layout resource.
         *
         * @param context
         * @param layoutResource the layout resource to use for the MarkerView
         */
        public MarkerView(Context context, int layoutResource) {
            super(context);
            setupLayoutResource(layoutResource);
        }
    
        /**
         * Sets the layout resource for a custom MarkerView.
         *
         * @param layoutResource
         */
        private void setupLayoutResource(int layoutResource) {
    
            View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this);
    
            inflated.setLayoutParams(new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
            inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    
            // measure(getWidth(), getHeight());
            inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight());
        }
    
        public void setOffset(MPPointF offset) {
            mOffset = offset;
    
            if (mOffset == null) {
                mOffset = new MPPointF();
            }
        }
    
        public void setOffset(float offsetX, float offsetY) {
            mOffset.x = offsetX;
            mOffset.y = offsetY;
        }
    
        @Override
        public MPPointF getOffset() {
            return mOffset;
        }
    
        public MPPointF getOffsetRight() {
            return mOffset;
        }
    
        public void setChartView(Chart chart) {
            mWeakChart = new WeakReference<>(chart);
        }
    
        public Chart getChartView() {
            return mWeakChart == null ? null : mWeakChart.get();
        }
    
        @Override
        public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
    
            Chart chart = getChartView();
            MPPointF offset = getOffset();
            float width = getWidth();
            float height = getHeight();
    
            mOffset2.x = offset.x;
    
            if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
                offset = getOffsetRight();
                mOffset2.x = offset.x;
            }
    
            mOffset2.y = offset.y;
    
            if (posX + mOffset2.x < 0) {
                mOffset2.x = - posX;
            } /*else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
                mOffset2.x = chart.getWidth() - posX - width;
            }*/
    
            if (posY + mOffset2.y < 0) {
                mOffset2.y = - posY;
            } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) {
                mOffset2.y = chart.getHeight() - posY - height;
            }
    
            return mOffset2;
        }
    
        @Override
        public void refreshContent(Entry e, Highlight highlight) {
    
            measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
    
        }
    
        @Override
        public void draw(Canvas canvas, float posX, float posY) {
    
            MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
    
            int saveId = canvas.save();
            // translate to the correct position and draw
            canvas.translate(posX + offset.x, posY + offset.y);
            draw(canvas);
            canvas.restoreToCount(saveId);
        }
    }
    

    主要修改位置:



    详细用法参照博客:

     MPAndroidChart项目实战(二)——双平滑曲线(双折线图)和MarkView实现



    展开全文
  • 前言 Android里面只要用过图表的应该都...下面就是给大家分享一下自己写的自适应MarkerView。 先上图 正文 要实现这样的效果,大家先想想怎么做? 这里的逻辑步骤分为: 1、创建新类继承自MarkerView 2、inflate

    前言

    Android里面只要用过图表的应该都知道MPAndroidChart这个库。这个库在iOS里面也有对应Charts,所以一般移动端做图表,Android和iOS两端都要实现同样的效果,他们是不错的一个选择。
    但是,对于图表这种包含的情况非常复杂的东西,很难满足大家各种各样的需求,所以很多都需要自定义。下面就是给大家分享一下自己写的自适应MarkerView。

    先上图

    正常居中显示

    超过左边界

    超过上边界

    超过右边界

    超过上边界和右边界

    正文

    要实现这样的效果,大家先想想怎么做?
    这里的逻辑步骤分为:
    1、创建新类继承自MarkerView
    2、inflate layout进去
    3、重写getOffsetForDrawingAtPoint ,分别处理各种边界情况的偏移
    4、重写draw,绘制底色,根据不同的情况,绘制带箭头的对话框

    直接上代码
    public class XYMarkerView extends MarkerView {
        public static final int ARROW_SIZE = 40; // 箭头的大小
        private static final float CIRCLE_OFFSET = 10;//因为我这里的折点是圆圈,所以要偏移,防止直接指向了圆心
        private static final float STOKE_WIDTH = 5;//这里对于stroke_width的宽度也要做一定偏移
        private final TextView tvContent;
        private final RoundImageView avatar;
        private final TextView name;
        private final List<StepListModel> stepListModels;
        private int index;
        private int oldIndex = -1;
    
        public XYMarkerView(Context context, List<StepListModel> stepListModels) {
            super(context, R.layout.custom_marker_view);
            tvContent = (TextView) findViewById(R.id.tvContent);
            avatar = (RoundImageView) findViewById(R.id.avatar);
            name = (TextView) findViewById(R.id.name);
            this.stepListModels = stepListModels;
        }
    
        @Override
        public void refreshContent(Entry e, Highlight highlight) {
            super.refreshContent(e, highlight);
            index = highlight.getDataSetIndex();//这个方法用于获得折线是哪根
            tvContent.setText((int) e.getY() + "");
    //            StepListModel stepListModel = stepListModels.get(highlight.getDataSetIndex() % Constants.battleUsersCount);
    //            name.setText(stepListModel.getNickNm());
    //            Glide.with(getContext())
    //                    .load(GlideUtil.getGlideUrl(stepListModel.getIconUrl()))
    //                    .into(avatar);
            Glide.with(getContext())
                    .asBitmap()
                    .load(avatars[index % avatars.length])
                    .listener(new RequestListener<Bitmap>() {
                        @Override
                        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
                            return false;
                        }
    
                        @Override
                        public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
                            if (resource != null) {
                                if (oldIndex != index) {
                                    XYMarkerView.this.getChartView().invalidate();
                                    oldIndex = index;
                                }
                                avatar.setImageBitmap(resource);
                            }
                            return false;
                        }
                    })
                    .into(avatar);
            name.setText(highlight.getDataSetIndex() + "");
            tvContent.setTextColor(getResources().getColor(ColorUtil.colors[highlight.getDataSetIndex() % ColorUtil.colors.length]));
            LogUtil.m("getDataSetIndex" + highlight.getDataSetIndex());
    
        }
    
        @Override
        public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
            MPPointF offset = getOffset();
            Chart chart = getChartView();
            float width = getWidth();
            float height = getHeight();
    // posY \posX 指的是markerView左上角点在图表上面的位置
    //处理Y方向
            if (posY <= height + ARROW_SIZE) {// 如果点y坐标小于markerView的高度,如果不处理会超出上边界,处理了之后这时候箭头是向上的,我们需要把图标下移一个箭头的大小
                offset.y = ARROW_SIZE;
            } else {//否则属于正常情况,因为我们默认是箭头朝下,然后正常偏移就是,需要向上偏移markerView高度和arrow size,再加一个stroke的宽度,因为你需要看到对话框的上面的边框
                offset.y = -height - ARROW_SIZE - STOKE_WIDTH; // 40 arrow height   5 stroke width
            }
    //处理X方向,分为3种情况,1、在图表左边 2、在图表中间 3、在图表右边
    //
            if (posX > chart.getWidth() - width) {//如果超过右边界,则向左偏移markerView的宽度
                offset.x = -width;
            } else {//默认情况,不偏移(因为是点是在左上角)
                offset.x = 0;
                if (posX > width / 2) {//如果大于markerView的一半,说明箭头在中间,所以向右偏移一半宽度
                    offset.x = -(width / 2);
                }
            }
            return offset;
        }
    
        @Override
        public void draw(Canvas canvas, float posX, float posY) {
            Paint paint = new Paint();//绘制边框的画笔
            paint.setStrokeWidth(STOKE_WIDTH);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setColor(getResources().getColor(ColorUtil.colors[index % ColorUtil.colors.length]));
    
            Paint whitePaint = new Paint();//绘制底色白色的画笔
            whitePaint.setStyle(Paint.Style.FILL);
            whitePaint.setColor(Color.WHITE);
    
            Chart chart = getChartView();
            float width = getWidth();
            float height = getHeight();
    
            MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
            int saveId = canvas.save();
    
            Path path = new Path();
            if (posY < height + ARROW_SIZE) {//处理超过上边界
                path = new Path();
                path.moveTo(0, 0);
                if (posX > chart.getWidth() - width) {//超过右边界
                    path.lineTo(width - ARROW_SIZE, 0);
                    path.lineTo(width, -ARROW_SIZE + CIRCLE_OFFSET);
                    path.lineTo(width, 0);
                } else {
                    if (posX > width / 2) {//在图表中间
                        path.lineTo(width / 2 - ARROW_SIZE / 2, 0);
                        path.lineTo(width / 2, -ARROW_SIZE + CIRCLE_OFFSET);
                        path.lineTo(width / 2 + ARROW_SIZE / 2, 0);
                    } else {//超过左边界
                        path.lineTo(0, -ARROW_SIZE + CIRCLE_OFFSET);
                        path.lineTo(0 + ARROW_SIZE, 0);
                    }
                }
                path.lineTo(0 + width, 0);
                path.lineTo(0 + width, 0 + height);
                path.lineTo(0, 0 + height);
                path.lineTo(0, 0);
                path.offset(posX + offset.x, posY + offset.y);
            } else {//没有超过上边界
                path = new Path();
                path.moveTo(0, 0);
                path.lineTo(0 + width, 0);
                path.lineTo(0 + width, 0 + height);
                if (posX > chart.getWidth() - width) {
                    path.lineTo(width, height + ARROW_SIZE - CIRCLE_OFFSET);
                    path.lineTo(width - ARROW_SIZE, 0 + height);
                    path.lineTo(0, 0 + height);
                } else {
                    if (posX > width / 2) {
                        path.lineTo(width / 2 + ARROW_SIZE / 2, 0 + height);
                        path.lineTo(width / 2, height + ARROW_SIZE - CIRCLE_OFFSET);
                        path.lineTo(width / 2 - ARROW_SIZE / 2, 0 + height);
                        path.lineTo(0, 0 + height);
                    } else {
                        path.lineTo(0 + ARROW_SIZE, 0 + height);
                        path.lineTo(0, height + ARROW_SIZE - CIRCLE_OFFSET);
                        path.lineTo(0, 0 + height);
                    }
                }
                path.lineTo(0, 0);
                path.offset(posX + offset.x, posY + offset.y);
            }
    
            // translate to the correct position and draw
            canvas.drawPath(path, whitePaint);
            canvas.drawPath(path, paint);
            canvas.translate(posX + offset.x, posY + offset.y);
            draw(canvas);
            canvas.restoreToCount(saveId);
        }
    }
    

    详细

    不同颜色、头像等处理

    我这里的需求是根据不同的折线,显示不同的人物头像、名字和对应的值,并且对话框的颜色、字体都要对应折线的颜色。
    所以,我这里根据highlight.getDataSetIndex()就能处理不同颜色、头像等信息的情况。

    //类似这个
     name.setText(highlight.getDataSetIndex() + "");
     tvContent.setTextColor(getResources().getColor(ColorUtil.colors[highlight.getDataSetIndex() % ColorUtil.colors.length]));
    

    注意:
    这里有个地方要注意一下,这里如果你使用Glide来加载图片到ImageView里去,会出现第一次加载不出来头像,第二次点击的时候就加载出来了。
    原因 :是因为其实glide已经加载出来了,然后只是异步加载出来之后,已经是在layout绘制完成之后了,并没有进行invalidate的刷新。所以直到第二次的时候,其实glide已经加载过了,有缓存,所以直接就显示了,发生在draw方法之前,因为refreshContent就在draw之前调用

    
                // callbacks to update the content
                mMarker.refreshContent(e, highlight);
    
                // draw the marker
                mMarker.draw(canvas, pos[0], pos[1]);
    

    我这里是直接在glide的onResourceReady里面调用chart的invalidate,但是这样的话会一直循环刷新了,卡死!
    所以,这里使用了一个oldIndex是否等于index来判断是否是已经invalidate,这样的话就只有点击不同的折线的时候会invalidate一次,之后就不会了。

      private int index;
      private int oldIndex = -1;
      ...
      Glide.with(getContext())
                    .asBitmap()
                    .load(avatars[index % avatars.length])
                    .listener(new RequestListener<Bitmap>() {
                        @Override
                        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
                            return false;
                        }
    
                        @Override
                        public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
                            if (resource != null) {
                                if (oldIndex != index) {
                                    XYMarkerView.this.getChartView().invalidate();
                                    oldIndex = index;
                                }
                                avatar.setImageBitmap(resource);
                            }
                            return false;
                        }
                    })
                    .into(avatar);
    
    边界情况的处理

    MarkerView的draw方法,里面有posX、posY,这个点坐标代表的是markerView的左上角的坐标(如果不偏移的情况下,官方的默认也是做了偏移处理的,但是还有不够完善)。

    默认是在左上角的,像这样
    getOffsetForDrawingAtPoint就是处理偏移的方法,最终应用是在draw里面,进行canvas的translate变换

     @Override
        public void draw(Canvas canvas, float posX, float posY) {
    
            MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
    
            int saveId = canvas.save();
            // translate to the correct position and draw
            canvas.translate(posX + offset.x, posY + offset.y);
            draw(canvas);
            canvas.restoreToCount(saveId);
        }
    

    下面是我对各种边界情况的处理

         @Override
        public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
            MPPointF offset = getOffset();
            Chart chart = getChartView();
            float width = getWidth();
            float height = getHeight();
    // posY \posX 指的是markerView左上角点在图表上面的位置
    //处理Y方向
            if (posY <= height + ARROW_SIZE) {// 如果点y坐标小于markerView的高度,如果不处理会超出上边界,处理了之后这时候箭头是向上的,我们需要把图标下移一个箭头的大小
                offset.y = ARROW_SIZE;
            } else {//否则属于正常情况,因为我们默认是箭头朝下,然后正常偏移就是,需要向上偏移markerView高度和arrow size,再加一个stroke的宽度,因为你需要看到对话框的上面的边框
                offset.y = -height - ARROW_SIZE - STOKE_WIDTH; // 40 arrow height   5 stroke width
            }
    //处理X方向,分为3种情况,1、在图表左边 2、在图表中间 3、在图表右边
    //
            if (posX > chart.getWidth() - width) {//如果超过右边界,则向左偏移markerView的宽度
                offset.x = -width;
            } else {//默认情况,不偏移(因为是点是在左上角)
                offset.x = 0;
                if (posX > width / 2) {//如果大于markerView的一半,说明箭头在中间,所以向右偏移一半宽度
                    offset.x = -(width / 2);
                }
            }
            return offset;
        }
    

    处理左边界

    处理一般情况

    处理上边界

    处理右边界

    对于各个箭头的处理

    实现这个对话框,主要是通过绘制path来实现的,箭头的处理也是根据不同的情况,进行不同的path绘制实现的。

     @Override
        public void draw(Canvas canvas, float posX, float posY) {
            Paint paint = new Paint();//绘制边框的画笔
            paint.setStrokeWidth(STOKE_WIDTH);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setColor(getResources().getColor(ColorUtil.colors[index % ColorUtil.colors.length]));
    
            Paint whitePaint = new Paint();//绘制底色白色的画笔
            whitePaint.setStyle(Paint.Style.FILL);
            whitePaint.setColor(Color.WHITE);
    
            Chart chart = getChartView();
            float width = getWidth();
            float height = getHeight();
    
            MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
            int saveId = canvas.save();
    
            Path path = new Path();
            if (posY < height + ARROW_SIZE) {//处理超过上边界
                path = new Path();
                path.moveTo(0, 0);
                if (posX > chart.getWidth() - width) {//超过右边界
                    path.lineTo(width - ARROW_SIZE, 0);
                    path.lineTo(width, -ARROW_SIZE + CIRCLE_OFFSET);
                    path.lineTo(width, 0);
                } else {
                    if (posX > width / 2) {//在图表中间
                        path.lineTo(width / 2 - ARROW_SIZE / 2, 0);
                        path.lineTo(width / 2, -ARROW_SIZE + CIRCLE_OFFSET);
                        path.lineTo(width / 2 + ARROW_SIZE / 2, 0);
                    } else {//超过左边界
                        path.lineTo(0, -ARROW_SIZE + CIRCLE_OFFSET);
                        path.lineTo(0 + ARROW_SIZE, 0);
                    }
                }
                path.lineTo(0 + width, 0);
                path.lineTo(0 + width, 0 + height);
                path.lineTo(0, 0 + height);
                path.lineTo(0, 0);
                path.offset(posX + offset.x, posY + offset.y);
            } else {//没有超过上边界
                path = new Path();
                path.moveTo(0, 0);
                path.lineTo(0 + width, 0);
                path.lineTo(0 + width, 0 + height);
                if (posX > chart.getWidth() - width) {
                    path.lineTo(width, height + ARROW_SIZE - CIRCLE_OFFSET);
                    path.lineTo(width - ARROW_SIZE, 0 + height);
                    path.lineTo(0, 0 + height);
                } else {
                    if (posX > width / 2) {
                        path.lineTo(width / 2 + ARROW_SIZE / 2, 0 + height);
                        path.lineTo(width / 2, height + ARROW_SIZE - CIRCLE_OFFSET);
                        path.lineTo(width / 2 - ARROW_SIZE / 2, 0 + height);
                        path.lineTo(0, 0 + height);
                    } else {
                        path.lineTo(0 + ARROW_SIZE, 0 + height);
                        path.lineTo(0, height + ARROW_SIZE - CIRCLE_OFFSET);
                        path.lineTo(0, 0 + height);
                    }
                }
                path.lineTo(0, 0);
                path.offset(posX + offset.x, posY + offset.y);
            }
    
            // translate to the correct position and draw
            canvas.drawPath(path, whitePaint);
            canvas.drawPath(path, paint);
            canvas.translate(posX + offset.x, posY + offset.y);
            draw(canvas);
            canvas.restoreToCount(saveId);
        }
    

    绘制MarkerView分为两部分,一部分是绘制对话框,一部分是绘制传入的R.layout.xx的view。

    类似这样

    这里有一个问题,就是需要填充对话框白色,并且还要有边框。
    一个画笔是不够的,所以这里有两个画笔,一个是填充的,一个是画边框的。
    并且,是必须要先绘制对话框的底色,绘制对话框,再绘制传入的view

       Paint paint = new Paint();//绘制边框的画笔
            paint.setStrokeWidth(STOKE_WIDTH);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setColor(getResources().getColor(ColorUtil.colors[index % ColorUtil.colors.length]));
    
            Paint whitePaint = new Paint();//绘制底色白色的画笔
            whitePaint.setStyle(Paint.Style.FILL);
            whitePaint.setColor(Color.WHITE);
    
    // 第一步        canvas.drawPath(path, whitePaint); 
    // 第二步        canvas.drawPath(path, paint); 
                    canvas.translate(posX + offset.x, posY + offset.y); 
    // 第三步        draw(canvas);
    

    因为我们的markerView偏移是对于canvas的偏移,但是我们的对话框的path并没有偏移,所以我们也要对path进行同样的偏移处理。很简单,直接获取偏移值,然后偏移就好。

       MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
      path.offset(posX + offset.x, posY + offset.y);
    

    最后

    这样的最终效果就是传入的layout 被对话框包裹在里面了。

    箭头的绘制处理也比较简单,就是根据不同的情况来进行绘制就好,看代码吧。

    展开全文
  • <div><p>I am using <code>MarkerView</code> and add it to my map but my problem is whenever I move map or change zoom ,inflated view (<code>MarkerView) is moving. <p>PS : mapbox-android-sdk version is...
  • 一直以来,图标类UI都用MPAndroidChart这个库,但是今天今天感觉不好了,想实现一个标签,并可点击,我以为这玩意肯定支持,结果在库里找了好久,发现没有,然后我的第一...Make MarkerView Click 不出我所料。 ...
  • 如何在表格创建的时候在默认在最后一个坐标点绘制一个markerview
  • Android MPAndroidChart的MarkerView设置了点击事件没有反应,试了很久没实现,有实现的吗?
  • MPAndroidChart的详细使用——MarkerView

    千次阅读 2020-03-11 12:38:30
    iMarker接口可以实现自定义高亮显示标记的视图 上一篇:MPAndroidChart的详细使用——动画 下一篇:MPAndroidChart的详细使用——...public class YourMarkerView extends MarkerView { private TextView tvConten...
  • MPAndroidChart之LineChart(2)MarkerView

    千次阅读 热门讨论 2017-05-10 16:25:59
    MarkerView 对于MPAndroidChart的基本属性和一些常见的设置前面的一篇博客MPAndroidChart之LineChart(1)大部分说到了,这篇将要实现MarkerView这个东西,我理解是提示覆盖物,类似百度地图的覆盖物同一个意思,至于...
  • MPAndroidChart 教程:MarkerView(十一)

    万次阅读 多人点赞 2015-12-30 13:15:15
    一、什么是MarkerView效果图: 如上图所示,当点击折线图上的点时,会弹出一个View,这就是 Markerview 。 上面的左图是一个 TextView 布局和一个椭圆黑色的 background 右图是一个 TextView 布局和一个 bitmap ...
  • MPAndroidChart实战——MarkerView第一次点击内容未显示问题 项目中自定义了MarkerView,但第一次点击只显示了背景,内容并未显示...//实例自定义的MarkerView val markerView = MyChartMarkerView( getContext()!!,
  • MPAndroidChart——LineChart,配置 MarkerView。 想要运用好本篇博客,读者至少需要掌握 Android 开发有关 Activity 的部分,并且对 MPAndroidChart 有简单的了解。本文是傻瓜式教程,更多需求请查看官方文档。
  • 但是近期公司的项目要求在markerview上添加一些按钮,能够直接在markerview上操作按钮来直接对图上的point进行操控。然后在lib里找了半天,发现原生根本就不支持此功能,然后在其github MPAndroidChart的issue里查找...
  • 最近安排的事情忙完了,自己发现MarkerView实现的标签,有偏移问题。实在是看不下去了今天就来解决一下子。 首先,我在查找了很多文章之后,了解到MarkerView中存在偏移量的问题。 在代码中设置MarkerView: ...
  • Android图表库MPAndroidChart(五)——自定义MarkerView实现选中高亮 建议看这篇之前先去看我的前几篇的文章 相信看了上篇的同学会奇怪这个效果 这个高亮显示是怎么来的,这里也提及一下,和百度的覆盖物类似,都...
  • 遇到这个很是头疼的问题,花了我很长时间琢磨,最后等效的实现了markerview的点击事件 解决办法如下: 在他的点击事件中,有如下两个方法,第一个是当你点击统计图时执行的方法; 第二个方法是在你的统计图再次...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 433
精华内容 173
关键字:

markerview