Reputation: 5440
This is my old question. However this time I have provided my code as well.
I have a ListView with different types of rows. The row may contain text, image, video or something else. If I click on ImageView (inside the row) I will go to another activity to show the image in full screen, If I click on Video (inside the row) I will go to another activity to play he video.
I have implemented right to left swipe listener on my ListView. If I start the ListView swipe from an empty space in ListView, the swipe works (first and second row in below image). However if I start the ListView swipe from the ListView row's item, then the swipe doesn't work (third and fourth row in below image). If I remove the click events from ImageView and Video then the swipe works even if I start the swipe from the ListView row's item i.e. in this case the swipe works on whole ListView, no matter on which row I do the swipe.
How can I get rid on this problem? I think this can be achieved if I disable all the click events on the all the items inside ListView. How can do so? Is there any other way?
I want both swipe on ListView and click on ListView's item.
SwipeDetector.java - For detecting swipes on the ListView
public class SwipeDetector implements View.OnTouchListener {
private SwipeListener swipeListener;
private ListView mListView;
private int hundred;
private boolean motionInterceptDisallowed = false;
public static enum Action {
LR, // Left to right
RL, // Right to left
TB, // Top to bottom
BT, // Bottom to top
None // Action not found
}
private static final int HORIZONTAL_MIN_DISTANCE = 30; // The minimum
// distance for
// horizontal swipe
private static final int VERTICAL_MIN_DISTANCE = 80; // The minimum distance
// for vertical
// swipe
private float downX, downY, upX, upY; // Coordinates
private Action mSwipeDetected = Action.None; // Last action
public SwipeDetector(Context context, ListView listView) {
hundred = (int) context.getResources().getDimension(R.dimen.hundred);
mListView = listView;
}
public boolean swipeDetected() {
return mSwipeDetected != Action.None;
}
public Action getAction() {
return mSwipeDetected;
}
/**
* Swipe detection
*/@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
{
downX = event.getX();
downY = event.getY();
mSwipeDetected = Action.None;
return false; // allow other events like Click to be processed
}
case MotionEvent.ACTION_MOVE:
{
upX = event.getX();
upY = event.getY();
float deltaX = downX - upX;
float deltaY = downY - upY;
float absX = Math.abs(deltaX);
float absY = Math.abs(deltaY);
if((absX >= (3 * absY)) && absX > HORIZONTAL_MIN_DISTANCE && mListView != null && !motionInterceptDisallowed) {
mListView.requestDisallowInterceptTouchEvent(true);
motionInterceptDisallowed = true;
}
if((absX >= (3 * absY)) && absX <= hundred) {
if (deltaX > 0) {
mSwipeDetected = Action.RL;
swipeListener.onSwipe(MotionEvent.ACTION_MOVE, Action.RL, absX);
}
}
return false;
}
case MotionEvent.ACTION_UP:
swipeListener.onSwipe(MotionEvent.ACTION_UP, Action.BT, 0);
if (mListView != null) {
mListView.requestDisallowInterceptTouchEvent(false);
motionInterceptDisallowed = false;
}
return false;
case MotionEvent.ACTION_CANCEL:
return true;
}
return false;
}
/**
* Set chat send listener
* @param listener
*/
public void setSwipeListener(SwipeListener listener) {
swipeListener = listener;
}
public interface SwipeListener {
void onSwipe(int event, Action action, float x);
}
}
This is how I am setting SwipeDetector on my ListView
final SwipeDetector swipeDetector = new SwipeDetector(SwipeActivity.this, mListView);
swipeDetector.setSwipeListener(mSwipeListener);
mListView.setOnTouchListener(swipeDetector);
My Activity implements SwipeDetector.SwipeListener
Below is overridden onSwipe() method
@Override
public void onSwipe(int event, SwipeDetector.Action action, float x) {
switch (event) {
case MotionEvent.ACTION_MOVE:
System.out.println("list move");
break;
case MotionEvent.ACTION_UP:
System.out.println("list up");
break;
}
}
In my adapter I am setting onClickListener() on my TextView, ImageView and VideoView
holder.mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("item textview click");
}
});
holder.mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("item imageview click");
}
});
holder.mVideoView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("item videoview click");
}
});
When I start the swipe from the empty area of ListView, the ListView's swipe listener captures it. However, when I start the swipe from the TextView/ImageView/VideoView then that View's onClickListener is fired instead of parent's (ListView's) onTouchListener().
How can I implement the same? I want that when I click on ListView's item then that item's onClick should get fired and when I swipe on the ListView's item then ListView's onSwipe should get fired.
Upvotes: 4
Views: 3585
Reputation: 691
I know this is an old question but still for future search here is a better solution:
Try to use RecyclerView instead of ListView and add OnItemTouchListener of recyclerview's item.
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(
this,
new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
// Your code here for item on click event...
}
},
runnableSwipeLeftToRight,
runnableSwipeRightToLeft)
);
Here are two methods which will execute on appropriate swipes:
Runnable runnableSwipeRightToLeft = new Runnable() {
public void run() {
// Your Code here...
}
};
Runnable runnableSwipeLeftToRight = new Runnable() {
public void run() {
// Your code here...
}
};
And here is class code for touch and swipe detection:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener, RecyclerView.OnLongClickListener {
private OnItemClickListener mListener;
GestureDetector gestureDetector;
Runnable runnableSwipeLeftToRight, runnableSwipeRightToLeft;
private static final String TAG = "RecyclerItemClickListen";
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_MAX_OFF_PATH = 200;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
@Override
public boolean onLongClick(View view) {
return false;
}
public interface OnItemClickListener {
public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener, Runnable runnableSwipeLeftToRight, Runnable runnableSwipeRightToLeft) {
this.mListener = listener;
this.runnableSwipeLeftToRight = runnableSwipeLeftToRight;
this.runnableSwipeRightToLeft = runnableSwipeRightToLeft;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
gestureDetector = new GestureDetector(context, new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) { }
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) { return false; }
@Override
public void onLongPress(MotionEvent motionEvent) { }
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float v1) {
try {
float diffAbs = Math.abs(e1.getY() - e2.getY());
float diff = e1.getX() - e2.getX();
if (diffAbs > SWIPE_MAX_OFF_PATH) {
return false;
}
// Left swipe
if (diff > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
onLeftSwipe();
// Right swipe
} else if (-diff > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
onRightSwipe();
}
} catch (Exception e) {
Log.e("YourActivity", "Error on gestures");
}
return false;
}
});
}
private void onLeftSwipe() {
Log.d(TAG, "onLeftSwipe: ");
runnableSwipeRightToLeft.run();
}
private void onRightSwipe() {
Log.d(TAG, "onRightSwipe:");
runnableSwipeLeftToRight.run();
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
}
return gestureDetector.onTouchEvent(e);
}
@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
Hope this will help you.
Upvotes: 0
Reputation: 12953
Thats Not Possible with both onClickListener
for listitem
and SwipeListener
for List
,because it gets Ambiguity between which View to Consider on touch
You Use OnSwipeTouchListener
which implements onTouchListener
OnSwipeTouchListener.java
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener (Context ctx){
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
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) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
result = true;
}
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
result = true;
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
public void onSwipeTop() {
}
public void onSwipeBottom() {
}
}
And use OnSwipeTouchListener
on listitem
listitem.setOnTouchListener(new OnSwipeTouchListener() {
public void onSwipeLeft() {
//stuff to do list view left swipe
Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show();
}
public boolean onTouch(View v, MotionEvent event) {
//stuff to do on list item click
return gestureDetector.onTouchEvent(event);
}
});
}); //to get click events
Upvotes: 1