MPAndroidChart MarkerView的精確點選問題解決方案
阿新 • • 發佈:2019-02-07
說到在Android中畫圖,相信大家對MPAndroidChart不陌生,其功能強大,能畫出各種各樣非常絢麗的效果圖。但是近期公司的專案要求在markerview上新增一些按鈕,能夠直接在markerview上操作按鈕來直接對圖上的point進行操控。然後在lib裡找了半天,發現原生根本就不支援此功能,然後在其github MPAndroidChart的issue裡查詢相關問題,發現還真是有很多人提出了這個問題,讓作者解決這個問題,但是這個問題一直沒有得到很好的解決,記得作者在issue裡回答說”it is harder than what I thought…”,然後就沒有然後了。我直接懵逼…
不過這種方式需要改原始碼,不能讓你使用如下方式來整合進專案
compile ‘com.github.PhilJay:MPAndroidChart:v3.0.0-beta1’
所以,要解決這個問題,首先必須把MPAndroidChart從github上download下來,以compile library的方式來使用。
另外,這個只解決了,是否點選了markerview,如果點選了的話,相應其點選事件。如果此時,需要更為精確的控制,比如最開始提出的在markerview裡新增一些按鈕,對這些按鈕來響應不同的點選事件,該怎麼辦呢?我這裡已經實現了!在這裡把它分享出來,相信對有需要的朋友有所幫助!
- 在BarLineChartBase裡修改一下onTouchEvent方法,如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (mChartTouchListener == null || mData == null)
return false;
//這裡是新新增的判斷,讓markerView有機會相應點選事件!
if(mMarker !=null && MotionEvent.ACTION_UP == event .getAction() && mMarker.isTouch(event)){
mMarker.markViewClick();
return true;
}
// check if touch gestures are enabled
if (!mTouchEnabled)
return false;
else
return mChartTouchListener.onTouch(this, event);
}
- 在IMarker裡新增如下方法
boolean isTouch(MotionEvent event);
void markViewClick();
- 在MarkerView裡修改draw方法,實現isTouch,markViewClick方法,另外新增2個屬性用來記錄點選point在markerview裡的相對位置
/**
* relativeTouchPointX, relativeTouchPointY 是 touch 的點相對於 markerView 的 left, top 的座標
*/
private float relativeTouchPointX = 0;
private float relativeTouchPointY = 0;
public float getRelativeTouchPointX() {
return relativeTouchPointX;
}
public void setRelativeTouchPointX(float relativeTouchPointX) {
this.relativeTouchPointX = relativeTouchPointX;
}
public float getRelativeTouchPointY() {
return relativeTouchPointY;
}
public void setRelativeTouchPointY(float relativeTouchPointY) {
this.relativeTouchPointY = relativeTouchPointY;
}
......
/**
*
* @param canvas
* @param posX 畫圖的point X值
* @param posY 畫圖的point Y值
*/
@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);
mpPointF.x = posX;
mpPointF.y = posY;
}
@Override
public boolean isTouch(MotionEvent event) {
if(mpPointF.x == 0)
return false;
float left = mpPointF.x - getWidth() / 2;
float top = mpPointF.y - getHeight();
float right = mpPointF.x + getWidth() / 2;
float bottom = mpPointF.y;
boolean touchInMarkerView = new RectF(left, top, right, bottom).contains(event.getX(), event.getY());
if(touchInMarkerView){
setRelativeTouchPointX(event.getX() - left);
setRelativeTouchPointY(event.getY() - top);
}
mpPointF.x = 0;
mpPointF.y = 0;
return touchInMarkerView;
}
@Override
public void markViewClick() {
}
- 在自己的markerview實現類裡,得到不同按鈕的在markerview的相對位置,override getOffset方法,最後就可以override markViewClick方法,在裡面精確的控制點選區域,來控制不同的點選事件了!
public class DemoMarkerView extends MarkerView{
private Context context;
private TextView tv;
private Button minusBtn;
private Button addBtn;
private float minusBtnLeft, minusBtnTop, minusBtnRight, minusBtnBottom;
private float addBtnLeft, addBtnTop, addBtnRight, addBtnBottom;
public DemoMarkerView(Context context, int layoutResource) {
super(context, layoutResource);
this.context = context;
tv = (TextView) findViewById(R.id.tv);
minusBtn = (Button) findViewById(R.id.minus);
addBtn = (Button) findViewById(R.id.add);
}
@Override
public void refreshContent(Entry e, Highlight highlight) {
super.refreshContent(e, highlight);
tv.setText("x = " + e.getX() + ", y = " + e.getY());
minusBtnLeft = minusBtn.getLeft();
minusBtnTop = minusBtn.getTop();
minusBtnRight = minusBtn.getRight();
minusBtnBottom = minusBtn.getBottom();
addBtnLeft = addBtn.getLeft();
addBtnTop = addBtn.getTop();
addBtnRight = addBtn.getRight();
addBtnBottom = addBtn.getBottom();
Log.e("Demo", "minusBtn: left = " + minusBtn.getLeft() + ", top = " + minusBtn.getTop() + ", right = " + minusBtn.getRight() + ", bottom = " + minusBtn.getBottom());
}
@Override
public MPPointF getOffset() {
MPPointF mpPointF = new MPPointF();
mpPointF.x = - getWidth() / 2;
mpPointF.y = - getHeight();
return mpPointF;
}
@Override
public void markViewClick() {
Log.e("Demo", "onClick relativeTouchPointX = " + getRelativeTouchPointX() + ", relativeTouchPointY = " + getRelativeTouchPointY());
float x = getRelativeTouchPointX();
float y = getRelativeTouchPointY();
/**
* 最終的核心思想就是利用點選point在markerview中的相對位置,是否被包含在了對應的button區域內。因為此時button的(l,t,r,b)也是相對於markerviwe的座標,這樣2者在同一個座標系中,是可以判斷的。從而實現了點選不同的區域,就相當於點選不同的按鈕事件!
**/
if(new RectF(minusBtnLeft, minusBtnTop, minusBtnRight, minusBtnBottom).contains(x, y))
Toast.makeText(context, "onClick minusButton!", Toast.LENGTH_SHORT).show();
else if(new RectF(addBtnLeft, addBtnTop, addBtnRight, addBtnBottom).contains(x, y))
Toast.makeText(context, "onClick addButton!", Toast.LENGTH_SHORT).show();
else
Toast.makeText(context, "onClick markerView!", Toast.LENGTH_SHORT).show();
}
}
其中,markerview的layout.xml內容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/colorPrimary"
android:orientation="vertical"
android:layout_width="150dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv"
android:textColor="@android:color/white"
android:textSize="16sp"
android:layout_width="match_parent"
android:gravity="center"
android:layout_height="wrap_content" />
<Button
android:id="@+id/minus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="-"/>
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="+"/>
</LinearLayout>
很簡單,只有一個textview,和2個按鈕,點選minus和add按鈕toast不同的內容。
如果還有什麼不清楚的,歡迎留言!