I have a view that need to process onTouch gestures and onClick events. What is the proper way to achieve this?
I have an onTouchListener
and
if you use onTouchListener
, you don't have to use onClickListener
. in onClickListener
what it does is it get the touch event and check event actions and detect the click. so if you want to do some work when onClick
. you can do it in the onTouchListener
by filtering the action.
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//this is touch
}
if (event.getAction() == MotionEvent.ACTION_UP) {
//this is click
}
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
layOutParams.x = initialX + (int) (event.getRawX() - initialTouchX);
layOutParams.y = initialY + (int) (event.getRawY() - initialTouchY);
break;
case MotionEvent.ACTION_DOWN:
initialX = layOutParams.x;
initialY = layOutParams.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
if (initialTouchX == event.getRawX() && initialTouchY == event.getRawY()) {
return false;// to handle Click
}
break;
case MotionEvent.ACTION_UP:
if (initialTouchX == event.getRawX() && initialTouchY == event.getRawY()) {
return false;// to handle Click
}
break;
}
return true;
}
};
In you GestureDetector, you can call callOnClick() directly. Note the View.callOnClick API requires API level 15. Just have a try.
// Create a Gesturedetector
GestureDetector mGestureDetector = new GestureDetector(context, new MyGestureDetector());
// Add a OnTouchListener into view
m_myViewer.setOnTouchListener(new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
return mGestureDetector.onTouchEvent(event);
}
});
private class MyGestureDetector extends GestureDetector.SimpleOnGestureListener
{
public boolean onSingleTapUp(MotionEvent e) {
// ---Call it directly---
callOnClick();
return false;
}
public void onLongPress(MotionEvent e) {
}
public boolean onDoubleTap(MotionEvent e) {
return false;
}
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
public void onShowPress(MotionEvent e) {
LogUtil.d(TAG, "onShowPress");
}
public boolean onDown(MotionEvent e) {
// Must return true to get matching events for this down event.
return true;
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, final float distanceX, float distanceY) {
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// do something
return super.onFling(e1, e2, velocityX, velocityY);
}
}
I agree with jdx, if you use onTouchlistener you dont need to use onclicklistener and viceversa, if you want to trigger an event on button,image,or textview click, then just fire onClicklistener. if you want some animations like dragging and spinning then use ontouchlistener to get the coordinates of the surface touched
I've been able to implement OnClick and OnTouch together in a custom keyboard I'm building. You can take a look at this code and modify it according to your context since you didn't include a code sample to work from.
If you post a sample of your code I can modify this sample of mine to accomplish the same outcome for your use. Context is everything when it comes to Android Development. Take a look at this code snippet and see if it helps. Otherwise post a sample of your own and I'll make the modifications and reply back.
View DefaultphaseLay() {
LinearLayout mViewFirstPhase = (LinearLayout) getLayoutInflater().inflate(R.layout.layout_default_phase, parent);
ksOne_btn_LiLay = (LinearLayout) mViewFirstPhase.findViewById(R.id.ksOne_btn_LiLay);
ksOne_btn = (Button) mViewFirstPhase.findViewById(R.id.ksOne_btn);
ksOne_btn.setOnClickListener(mCorkyListener);
ksOne_btn.setFocusableInTouchMode(true);
ksOne_btn.setFocusable(true);
ksOne_btn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
playClick(keyCode);
//v.startAnimation(animScale);
//key_sound.start();
xkeys_lay.setVisibility(View.GONE);
numkeys_lay.setVisibility(View.GONE);
if (Constants.HapticFeedbackConstant) {
myVib.vibrate(Constants.vibration_duration);
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
ksOne_btn.setFocusable(false); //Check This Added This In Attempt to Kill Selector on Action_Up
//playClick(-100);
//key_sound.pause();
}
return false;
}
});
ChangeKeyBackgroundMehods.initChange_Key_Background(ksOne_btn_LiLay);
ChangeFontStyle.initChange_fontStyle(ksOne_btn, getApplicationContext());
ChangeFont_size.initChange_fontSize(ksOne_btn, getApplicationContext());
This works for the context of type alpha or numerical output, if the event is looking to send or consume some other context it would need to be modified.
isMove = false;
case MotionEvent.ACTION_DOWN:
//Your stuff
isMove = false;
case MotionEvent.ACTION_UP:
if (!isMove || (Xdiff < 10 && Ydiff < 10 ) {
view.performClick; //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little
even when you just click it
}
case MotionEvent.ACTION_MOVE:
isMove = true;
Another way is to use threads. That is on Action_Down start a thread to increment a counter. In case of Action_UP : stop/interrupt the thread. look for counter if it is less than 2 (say or threshold as per your application) or !isMove then invoke click function