Mpandroidchart: Make MarkerView clickable

Created on 10 Oct 2014  路  13Comments  路  Source: PhilJay/MPAndroidChart

I want add a Listener to the MarkerView,but it don't work

mv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.v("LineChartActivity","click");
Toast.makeText(getBaseContext(),"click",Toast.LENGTH_LONG).show();
}
});

the logcat didn't show the log, also the toast not show

enhancement

Most helpful comment

I have finished my app with clickable marker view. My solution is that we'll create a subclass of LineChart (or other chart), then let override onTouchEvent and detect the touch location.

public class MyChart extends LineChart {

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean handled = true;
        // if there is no marker view or drawing marker is disabled
        if (isShowingMarker() && this.getMarker() instanceof ChartInfoMarkerView){
            ChartInfoMarkerView markerView = (ChartInfoMarkerView) this.getMarker();
            Rect rect = new Rect((int)markerView.drawingPosX,(int)markerView.drawingPosY,(int)markerView.drawingPosX + markerView.getWidth(), (int)markerView.drawingPosY + markerView.getHeight());
            if (rect.contains((int) event.getX(),(int) event.getY())) {
                // touch on marker -> dispatch touch event in to marker
                markerView.dispatchTouchEvent(event);
            }else{
                handled = super.onTouchEvent(event);
            }
        }else{
            handled = super.onTouchEvent(event);
        }
        return handled;
    }

    private boolean isShowingMarker(){
        return mMarker != null && isDrawMarkersEnabled() && valuesToHighlight();
    }
}

public class ChartInfoMarkerView extends MarkerView {
    @BindView(R.id.markerContainerView)
    LinearLayout markerContainerView;

    protected float drawingPosX;
    protected float drawingPosY;
    private static final int MAX_CLICK_DURATION = 500;
    private long startClickTime;

    /**
     * The constructor
     *
     * @param context
     * @param layoutResource
     */
    public ChartInfoMarkerView(Context context, int layoutResource) {
        super(context, layoutResource);
        ButterKnife.bind(this);
        markerContainerView.setClickable(true);
        markerContainerView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("MARKER","click");
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                startClickTime = Calendar.getInstance().getTimeInMillis();
                break;
            }
            case MotionEvent.ACTION_UP: {
                long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
                if(clickDuration < MAX_CLICK_DURATION) {
                    markerContainerView.performClick();
                }
            }
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void draw(Canvas canvas, float posX, float posY) {
        super.draw(canvas, posX, posY);
        MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
        this.drawingPosX = posX + offset.x;
        this.drawingPosY = posY + offset.y;
    }
}

All 13 comments

You are right thats not possible at the moment.
It might have something to do with the various touch-events and gestures interacting with each other. I will look into it as soon as I get the time.

In the meantime, you will have to wait or try and fix it yourself.

thank you for answering.
I will try to fix it .

Did you make any progress on this?
It seems harder than I originally thought O:

sorry,I've failed.... It also harder than I originally thought O:

I actually managed in a hacky way : you can set an "onClick" on the chart without interfering with the touch functions. You know when a marker is shown as refreshContent is called, then when you click the chart, the marker has been shown, and not valuesToHighlight(), then the marker has just been closed.

My test code with static status and text :
barChart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(MyMarkerView.showing && !barChart.valuesToHighlight()) {
Log.i("barChart","closed "+MyMarkerView.barString);
MyMarkerView.showing = false;
}
}
});

It is not perfectly an "onclick", as clicking on the bar also closes the marker, and you have to check for reloadings or other events that may hide the marker.

Still not possible?

Could we at least have setMarkerView accepting a view and not just resource?

Hey!
You can do something like place Chart and your View in RelativeLayout, set ChartValueSelectedListener, fill your View and translate it's position like in getMarkerViewPosition() from Chart. Handle scroll events etc.

Sincerely,
Roman

has this question bean soluted now?

I have finished my app with clickable marker view. My solution is that we'll create a subclass of LineChart (or other chart), then let override onTouchEvent and detect the touch location.

public class MyChart extends LineChart {

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean handled = true;
        // if there is no marker view or drawing marker is disabled
        if (isShowingMarker() && this.getMarker() instanceof ChartInfoMarkerView){
            ChartInfoMarkerView markerView = (ChartInfoMarkerView) this.getMarker();
            Rect rect = new Rect((int)markerView.drawingPosX,(int)markerView.drawingPosY,(int)markerView.drawingPosX + markerView.getWidth(), (int)markerView.drawingPosY + markerView.getHeight());
            if (rect.contains((int) event.getX(),(int) event.getY())) {
                // touch on marker -> dispatch touch event in to marker
                markerView.dispatchTouchEvent(event);
            }else{
                handled = super.onTouchEvent(event);
            }
        }else{
            handled = super.onTouchEvent(event);
        }
        return handled;
    }

    private boolean isShowingMarker(){
        return mMarker != null && isDrawMarkersEnabled() && valuesToHighlight();
    }
}

public class ChartInfoMarkerView extends MarkerView {
    @BindView(R.id.markerContainerView)
    LinearLayout markerContainerView;

    protected float drawingPosX;
    protected float drawingPosY;
    private static final int MAX_CLICK_DURATION = 500;
    private long startClickTime;

    /**
     * The constructor
     *
     * @param context
     * @param layoutResource
     */
    public ChartInfoMarkerView(Context context, int layoutResource) {
        super(context, layoutResource);
        ButterKnife.bind(this);
        markerContainerView.setClickable(true);
        markerContainerView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("MARKER","click");
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                startClickTime = Calendar.getInstance().getTimeInMillis();
                break;
            }
            case MotionEvent.ACTION_UP: {
                long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
                if(clickDuration < MAX_CLICK_DURATION) {
                    markerContainerView.performClick();
                }
            }
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void draw(Canvas canvas, float posX, float posY) {
        super.draw(canvas, posX, posY);
        MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
        this.drawingPosX = posX + offset.x;
        this.drawingPosY = posY + offset.y;
    }
}

Thanks, @thanhzusu your answer helped to handle markerview onclick event.
If there is a button inside the marker view, I was wondering if there was any way to handle its onclick event. I think we need to get the coordinates of that button inside markerview.

Let's say for example we have marker_view_with_button.xml as below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="45dp"
    android:background="@drawable/marker_background"
    android:gravity="center"
    android:orientation="horizontal"
    android:padding="5dp">


    <TextView
        android:id="@+id/tvContent"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_marginStart="5dp"
        android:layout_marginEnd="10dp"
        android:layout_marginTop="-5dp"
        android:ellipsize="end"
        android:gravity="center"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:textColor="@android:color/white"
        android:textSize="12sp" />

    <ImageButton
        android:id="@+id/viewDetailsButton"
        style="@style/filterButton"
        android:layout_width="40dp"
        android:layout_height="30dp"
        android:layout_gravity="center_vertical|end"
        android:layout_marginEnd="5dp"
        android:layout_marginTop="-5dp"
        android:tint="@color/icons"
        android:src="@drawable/ic_next"
        android:contentDescription="view details" />

</LinearLayout>

And we would like to handle the onClick event of that imageButton. How would we update the CustomBarChartWithButton.java file which is currently as follows?

public class CustomBarChartWithButton extends BarChart {

    public CustomBarChartWithButton(Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {


        boolean handled = true;
        // if there is no marker view or drawing marker is disabled
        if (isShowingMarker() && this.getMarker() instanceof CustomBarGraphInit.ButtonMarkerView) {
            CustomBarGraphInit.ButtonMarkerView markerView = (CustomBarGraphInit.ButtonMarkerView) this.getMarker();

            Rect rect = new Rect((int) markerView.drawingPosX, (int) markerView.drawingPosY, (int) markerView.drawingPosX + markerView.getWidth(), (int) markerView.drawingPosY + markerView.getHeight());
            if (rect.contains((int) event.getX(), (int) event.getY())) {
                // touch on marker -> dispatch touch event in to marker
                markerView.dispatchTouchEvent(event);
            } else {
                handled = super.onTouchEvent(event);
            }
        } else {
            handled = super.onTouchEvent(event);
        }
        return handled;


    }

    private boolean isShowingMarker() {
        return mMarker != null && isDrawMarkersEnabled() && valuesToHighlight();
    }


}

And the ButtonMarkerView.java being as follows:

public class ButtonMarkerView extends MarkerView {

        protected float drawingPosX;
        protected float drawingPosY;
        private static final int MAX_CLICK_DURATION = 500;
        private long startClickTime;

        private TextView tvContent;
        private ImageButton viewDetailsButton;
        private MPPointF mOffset;

        /**
         * Constructor. Sets up the MarkerView with a custom layout resource.
         *
         * @param context
         * @param layoutResource the layout resource to use for the MarkerView
         */
        public ButtonMarkerView(Context context, int layoutResource) {
            super(context, layoutResource);
            Log.e(TAG, "ButtonMarkerView: here");
            // find your layout components
            tvContent = findViewById(R.id.tvContent);
            viewDetailsButton = findViewById(R.id.viewDetailsButton);
            viewDetailsButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.e(TAG, "onClick: ");
                }
            });

        }

        @Override
        public void refreshContent(Entry e, Highlight highlight) {
            tvContent.setText("Test text");
            // this will perform necessary layouting
            super.refreshContent(e, highlight);
        }


        @Override
        public MPPointF getOffset() {

            if (mOffset == null) {
                // center the marker horizontally and vertically
                mOffset = new MPPointF(-(getWidth() / 2), -getHeight());
            }

            return mOffset;
        }


        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    startClickTime = Calendar.getInstance().getTimeInMillis();
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
                    if (clickDuration < MAX_CLICK_DURATION) {
                        viewDetailsButton.performClick();
                    }
                }
            }
            return super.onTouchEvent(event);
        }


        @Override
        public void draw(Canvas canvas, float posX, float posY) {
            super.draw(canvas, posX, posY);
            MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
            this.drawingPosX = posX + offset.x;
            this.drawingPosY = posY + offset.y;
        }
    }

hi @JainamJhaveri
You can get the coordinates from MotionEvent and determine whether it's inside viewDetailsButton's frame or not.

case MotionEvent.ACTION_UP: {
  long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
  if (clickDuration < MAX_CLICK_DURATION) {
       // TODO: check the coordinates from event inside viewDetailsButton's frame.
       viewDetailsButton.performClick();
   }
}

I am unable to figure out how to check the coordinates from inside viewDetailsButton, keeping in mind the orientation change as well

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vishvendu picture vishvendu  路  3Comments

AiTheAnswer picture AiTheAnswer  路  3Comments

galex picture galex  路  3Comments

ChenZeFengHi picture ChenZeFengHi  路  3Comments

andreyfel picture andreyfel  路  3Comments