使用MPAndroid打造自定义Chart

记一次工作内容_打造自定义Chart

工作需求需要实现一个自定义操作方式的图表,感觉还行,效果如下:

效果
效果

需求如下:
1.基本的图表(使用了MPAndroid框架)
2.选择数据时有高亮提示,3秒不操作高亮消失
3.图表放大之后拖拽高亮则处理为,高亮被拖拽;拖拽其他区域处理为横坐标拖拽;
4.选择数据则显示提示框显示内容,提示框常驻高亮顶部

1.实现基本的图表

图表的实现就不多解释了,直接贴代码,注释详细:

public class CustomChartActivity extends AppCompatActivity {

    @BindView(R.id.chart_custom)
    LineChart mChartCustom;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_chart);
        ButterKnife.bind(this);

        //获取数据
        List<Entry> entries = loadData();

        LineDataSet dataSet = new LineDataSet(entries, "custom Data");
        //设置数据集合
        setDataset(dataSet);
        LineData lineData = new LineData(dataSet);
        //设置XY轴
        setXYAxis(mChartCustom);
        //设置chart
        setChart(mChartCustom);
        mChartCustom.setData(lineData);
        //设置chart拖拽逻辑
        setChartDragMode(mChartCustom);
        mChartCustom.invalidate();
    }

    /**
     * 设置chart
     * @param
     */
    private void setChart(LineChart mChartCustom) {
        MyMarkerView myMarkerView=new MyMarkerView(this, R.layout.item_markerview);
        myMarkerView.setChartView(mChartCustom);
        mChartCustom.setMarker(myMarkerView);
    }

    /**
     * 设置DataSet
     * @param dataSet
     */
    private void setDataset(LineDataSet dataSet) {
        dataSet.setMode(LineDataSet.Mode.LINEAR);
        dataSet.setDrawValues(false);   //不绘制数值
        dataSet.setDrawCircles(false);  //不画小圆圈
        dataSet.setColor(ContextCompat.getColor(this, R.color.colorAccent));    //设置数据颜色

        dataSet.setHighLightColor(Color.BLACK); //设置高亮颜色
        dataSet.setHighlightEnabled(true);  //打开高亮开关
        dataSet.setHighlightLineWidth(1f);  //设置高亮宽度
        dataSet.setDrawHighlightIndicators(true);   //绘制高亮
        dataSet.setDrawVerticalHighlightIndicator(true);    //绘制垂直高亮
        dataSet.setDrawHorizontalHighlightIndicator(false); //不绘制水平高亮

    }

    /**
     * 设置XY轴
     * @param mChartCustom
     */
    private static void setXYAxis(LineChart mChartCustom) {
        //设置又Y轴不显示
        YAxis axisRight = mChartCustom.getAxisRight();
        axisRight.setDrawLabels(false);
        axisRight.setDrawGridLines(false);

        //设置X轴
        XAxis xAxis = mChartCustom.getXAxis();
        xAxis.setDrawGridLines(false);  //不显示网格线
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);      //设置X轴label位置
        //设置Y轴
        YAxis axisLeft = mChartCustom.getAxisLeft();
        axisLeft.setDrawGridLines(false);   //不显示网格线
        axisLeft.setAxisMinimum(-2f);   //最小值为-2
        axisLeft.setAxisMaximum(2f);    //最大值为2
    }

    /**
     * 初始化数据
     * @return 数据
     */
    private List<Entry> loadData() {
        List<Entry> entries=new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            entries.add(new Entry(i, (float) Math.sin((i%80)*(2*Math.PI/80))));
        }
        return entries;
    }

}

设置数据显示框:

MyMarkerView myMarkerView=new MyMarkerView(this, R.layout.item_markerview);
myMarkerView.setChartView(mChartCustom);
mChartCustom.setMarker(myMarkerView);

我们看一下基本的MarkView设置:

public class MyMarkerView extends MarkerView {
    private final NumberFormat numberFormat;
    private TextView tvMarker;
    private MPPointF offset2=new MPPointF();


    /**
     * Constructor. Sets up the MarkerView with a custom layout resource.
     *
     * @param context
     * @param layoutResource the layout resource to use for the MarkerView
     */
    public MyMarkerView(Context context, int layoutResource) {
        super(context, layoutResource);
        tvMarker= (TextView) findViewById(R.id.tvMarker);
        numberFormat= java.text.NumberFormat.getNumberInstance();
        numberFormat.setMaximumFractionDigits(2);
        numberFormat.setMinimumFractionDigits(2);

    }

    @Override
    public void refreshContent(Entry e, Highlight highlight) {
        tvMarker.setTextColor(ContextCompat.getColor(getContext(),R.color.colorPrimaryDark));
        tvMarker.setText(numberFormat.format(e.getY()));
        super.refreshContent(e, highlight);
    }

    @Override
    public MPPointF getOffset() {
        return new MPPointF(0,0);
    }
}

2.设置置顶的MarkView(数据显示框)

我们在这里重写MarkView的MPPointF getOffsetForDrawingAtPoint(float posX, float posY)方法,这个方法会返回一个MPPointF类,这个返回值表示的就是View绘制左上角的偏移量:

/**
 * 设置绘制的左上角的偏移坐标
 * @param posX value点的X
 * @param posY value点的Y
 * @return  偏移坐标
 */
@Override
public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
    offset2.x=getOffset().getX();
    if(posX>getChartView().getRight()/2){
        //如果右边界超过chart右边界,设置MarkView固定于左上角
        offset2.x=-getWidth();
    }else if (posX<getChartView().getRight()/2){
        offset2.x=0f;
    }
    offset2.y=-posY+getChartView().getViewPortHandler().offsetTop();
    return offset2;
}

这里我们可以看到首先我们判断一下value的横坐标,如果大于chart一半,那么设置偏移坐标-getWidth(),也就是提示框在高亮线左侧,如果小于chart一半,那么就是设置偏移量为0,也就是提示框在高亮先右侧;然后我们设置偏移坐标恒为-posY+getChartView().getViewPortHandler().offsetTop(),以达到无论value点X坐标在哪里,都偏移到提示框处于chart顶部;

3.设置拖拽逻辑

其实根据需求,可以想到主要需要重写的就是chart的OnTouchListener:

//设置拖拽模式
mChartCustom.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Highlight[] highlights = mChartCustom.getHighlighted();     //获取highLight
                if (highlights==null){
                    break;
                }
                float highlightPx = highlights[0].getXPx();     //获取highlight横坐标
                if (Math.abs(highlightPx-event.getX())<100){    //如果触摸距离在highlight范围内
                    //设置chart无法拖拽,highLight可以被拖拽
                    mChartCustom.setDragEnabled(false);
                    mChartCustom.setHighlightPerDragEnabled(true);
                }else {     //如果不在highLight范围内那么就设置为chart拖拽,highLight无法被拖拽
                    mChartCustom.setDragEnabled(true);
                    stopHighLight(mChartCustom);    //取消高亮
                }
                break;
        }
        return false;
    }
});

这里有几个方法需要了解:

  • Highlight[] getHighlighted():返回当前所有的高亮集合,通过Highlight.getXPx()方法可以获取到高亮点的X坐标;
  • setDragEnabled():设置视口可以被拖拽;
  • setHighlightPerDragEnabled():设置高亮线可以被拖拽;

4.设置高亮线以及提示框不操作3s后消失

Handler handler=new Handler();
private Runnable valueChooseRunnable;

valueChooseRunnable = new Runnable() {
    @Override
    public void run() {
        stopHighLight(mChartCustom);
    }
  };

//设置数据选择样式
mChartCustom.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
    @Override
    public void onValueSelected(Entry e, Highlight h) {
      //选中数据高亮之后5秒消失
      if (handler!=null){
          //刷新定时器
          handler.removeCallbacks(valueChooseRunnable);
          handler.postDelayed(valueChooseRunnable,3000);
      }
    }
    @Override
    public void onNothingSelected() {
    }
});

这里使用了chart.setOnChartValueSelectedListener()来实现对图标value选择的监听;这里使用Handler.postDelayed()方法来实现一个定时器的作用;

5.源码传送门

我是一只咸鱼,不想承认,也不能否认,不要同情我笨,又夸我天真,还梦想着翻身...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,527评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,033评论 4 62
  • 近日,气温陡增,重庆40度,成都37度,泸州36度,整个西部地区都陷入巨大的热海之中,走出家门,除了太阳的炙热,就...
    落水有声阅读 261评论 2 5
  • 感觉之前的回答都不够全面,MOOC死忠粉的我终于鼓起勇气整理了一堆网络课程资源,具体大家就看下面的吧,如果觉得不错...
    周小米同学阅读 1,619评论 0 9
  • 自然醒来满阳光,安静享受自由王。 更忆风雨上班时,早出晚归蝼蚁忙。
    老槐树阅读 185评论 0 4