记一次工作内容_打造自定义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()
方法来实现一个定时器的作用;