使用MPAndroid打造自定义Chart

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

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

《使用MPAndroid打造自定义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.源码传送门

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

    原文作者:我爱小白小白爱大开
    原文地址: https://www.jianshu.com/p/6a0c82498561
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞