Tobias Moe Thorstensen
Tobias Moe Thorstensen

Reputation: 8981

How to combine OnClickListener and OnTouchListener for an ImageButton

I need to do something when the user clicks the ImageButton I've tried to create a static class that implements both OnClickListener and OnTouchListener

static class ClickListenerForScrolling implements OnClickListener, OnTouchListener 

that has the following methods:

@Override
public void onClick(View v)

and

@Override
public boolean onTouch(View arg0, MotionEvent arg1)

The whole idea behind this is to change the image source of the ImageButton when the user touches it, and perform a task when the user clicks this button. Can anybody give me a hint on how to do this?

Upvotes: 26

Views: 28800

Answers (6)

Praveen Singh
Praveen Singh

Reputation: 2569

Rather than distance / time diff based approaches, You can make use of GestureDetector in combination with setOnTouchListener to achieve this. GestureDetector would detect the click while you can use OnTouchListener for other touch based events, e.g detecting drag.

Here is a sample code for reference:

Class MyCustomView() {

    fun addClickAndTouchListener() {
        val gestureDetector = GestureDetector(
            context,
            object : GestureDetector.SimpleOnGestureListener() {
                override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
                    // Add your onclick logic here
                    return true
                }
            }
        )

        setOnTouchListener { view, event ->
            when {
                gestureDetector.onTouchEvent(event) -> {
                    // Your onclick logic would be triggered through SimpleOnGestureListener
                    return@setOnTouchListener true
                }
                event.action == MotionEvent.ACTION_DOWN -> {
                    // Handle touch event
                    return@setOnTouchListener true
                }
                event.action == MotionEvent.ACTION_MOVE -> {
                    // Handle drag
                    return@setOnTouchListener true
                }
                event.action == MotionEvent.ACTION_UP -> {
                    // Handle Drag over
                    return@setOnTouchListener true
                }
                else -> return@setOnTouchListener false
            }
        }

    }
}

Upvotes: 2

Mohammad Elsayed
Mohammad Elsayed

Reputation: 2066

There are many solutions for this problem, the best one for me is to eliminate totally the setOnClickListener and inside the onTouchListener do check if the action is ACTION_UP and then inside that check if the last before that is ACTION_DOWN:

case MotionEvent.ACTION_UP:
    if (lastAction == MotionEvent.ACTION_DOWN) {
        //code for click   
        .
        .
        lastAction = event.getAction();
    }

lastAction is int and I update it at the end of each ACTION

Upvotes: 0

stannums
stannums

Reputation: 342

Use return false instead of return true

Upvotes: 8

Sven Menschner
Sven Menschner

Reputation: 603

Since both actions consist of the gestures "put finger on screen - lift finger from screen" you can't determine if it was touch action or a click action. So if you implement both listeners on this image button, a touch/click will change the picture AND press the button. Not sure if there is a determined order of these events...

However, if you want to separate these events, you will either need to define to a different gesture to one of the actions (like wiping to change picture), or create different areas who handle the events, for example the image doesn't fit the whole button and the free area serves as button click area.

HTH

Update:

I figured out, that a TouchEvent is more general than a ClickEvent thus it is called first.

public abstract boolean onTouch (View v, MotionEvent event)

This returns true, if the listener has consumed the event, false otherwise. So you can decide in your implementation if the Event should also be handled by OnClickListener, then just return false.

Upvotes: 36

siarhei sin
siarhei sin

Reputation: 111

Use this code:

public class GestureHelper implements OnTouchListener {

private final GestureDetector mGestureDetector;

public GestureHelper(Context context) {
    mGestureDetector = new GestureDetector(context, new GestureListener(this));
}

public void onSwipeRight() {
};

public void onSwipeLeft() {
};

public void onSwipeTop() {
};

public void onSwipeBottom() {
};

public void onDoubleTap() {
};

public void onClick() {
};

@Override
public boolean onTouch(View v, MotionEvent event) {
    return mGestureDetector.onTouchEvent(event);
}

private static final class GestureListener extends SimpleOnGestureListener {

    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;
    private GestureHelper mHelper;

    public GestureListener(GestureHelper helper) {
        mHelper = helper;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        mHelper.onClick();
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        mHelper.onDoubleTap();
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        boolean result = false;
        try {
            float diffY = e2.getY() - e1.getY();
            float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        mHelper.onSwipeRight();
                    } else {
                        mHelper.onSwipeLeft();
                    }
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        mHelper.onSwipeBottom();
                    } else {
                        mHelper.onSwipeTop();
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }
}

}

Extend this class and use like this...

view.setOnTouchListener(new SomeYourGestureHelper(context, someParameters));

Upvotes: 11

SUARPI - Oscar G.
SUARPI - Oscar G.

Reputation: 139

The onTouchListener is able to handle both movements.

Switch between event.action() values to obtain MotionEvent.

case MotionEvent.ACTION_DOWN: is the first finger impact.
case MotionEvent.ACTION_UP: is when the finger goes away.

You'll have to set the impact point on ACTION_DOWN.

TheImpactPoint=event.getX();

And then obtain the distance with ACTION_UP

float distance =TheImpactPoint-event.getX();

If distance = 0 then there is a click, otherwise it would be more or less than zero depending on gesture.

So this is the way to use the click event without a real click event and using only the onTouchListener.

Hope will be useful.

Upvotes: 13

Related Questions