Pergunta

I am trying to implement a custom list view that allows for both swiping and clicking buttons, similar to the implementation that the boomerang e-mail client has achieved (or Mailbox for iOS).

If I just want to click on the list item itself, I can create a new OnItemClickListener and ensure that there are no focusable or clickable descendants, which does not consume the touch event, and allows the custom list view to detect swipes. However, if I make anything within the list item clickable, it will consume the touch event, not call OnItemClickListener, and disable the swiping functionality of the custom list itself.

I've tried creating custom implementations of onTouchListener, or using onInterceptTouchEvent for a custom viewgroup. For all these implementations, onTouchListener must return true on Action.DOWN (and thus consumes the touch event) otherwise it will cease listening to the rest of the touch event.

Why is it that OnItemClickListener can detect the touch without consuming the touch event, and how do I replicate that for my own custom implementation? Any help or pointers appreciated!

Boomerang swipable and clickable list functionality

Here's the OnItemClickListener which works and does not consume the touch event:

 private AdapterView.OnItemClickListener onClick = new AdapterView.OnItemClickListener() 
    {
         @Override
         public void onItemClick(AdapterView<?> adapterView, View v, int position,
                 long arg3) {

             mAction = mAdapter.getItem(position);
             updateListToShowCurrentAction();   
             return;
         }
    };

Here's the functionality that I would like to be able to implement for a view within the list item while still passing the touch event up to the custom listview:

cancelButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                    final int position = mListView.getPositionForView((View) v.getParent());
                    mReordCtrl.removeAction(mAdapter, position);        
            }
        });

Here's the layout snippet within the list_item:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:tag="list_item" >

    <LinearLayout
            android:id="@+id/back"
            android:layout_width="match_parent"
            android:layout_height="fill_parent"
            android:layout_gravity="center_vertical"
            android:orientation="horizontal"
            android:tag="back" >

            <ImageView
                android:id="@+id/cancel_button"
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:src="@drawable/ic_action_cancel"
                android:tag="cancel_button" />
            </LinearLayout>
Foi útil?

Solução

If you've already played around with onInterceptTouchEvent, try overriding onTouchEvent(MotionEvent e). Here's an example implementation that you can adapt to your needs, that integrates a SimpleOnGestureListener detector and a custom interface viewListener that can be used for callbacks to a parent class. You probably don't need all of this if you just want to detect clicks; you might be able to just get away with the gesture detector. Also, here's a link I found helpful when I was researching touch event handling.

@Override public boolean onTouchEvent(MotionEvent ev) {
    //See if the gesture detector has detected an event.
    boolean eventConsumed = detector.onTouchEvent(ev);
    if (!eventConsumed) {

        //If no gesture detected, and the motion is finished, do something
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            //DO SOMETHING, such as scroll

            //Send event to listener.
            if (viewListener != null) {
                this.viewListener.onUp(ev.getX());
            }
            return true; //Consume the event, so nothing else fires.
        } else {
            //If no event is detected, and the action hasn't finished, do the default behavior
            return super.onTouchEvent(ev);
        }
    } else {
        return true; //Consume the event, so nothing else fires.  You can change this to false if you'd like to not consume the event.  
    }

}

and, a custom vertical scroll view class that captures downward swipes, but passes all other gestures to the children.

public class VerticalScrollView extends ScrollView {
private GestureDetector mGestureDetector;
View.OnTouchListener mGestureListener;

public VerticalScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mGestureDetector = new GestureDetector(context, new YScrollDetector());
    setFadingEdgeLength(0);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean superBool = super.onInterceptTouchEvent(ev);
    boolean intercept =  mGestureDetector.onTouchEvent(ev);
    return superBool && intercept ;
}

// Return false if we're scrolling more in the x direction than in the y direction, so we don't claim the action.
class YScrollDetector extends SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return Math.abs(distanceY) > Math.abs(distanceX);
    }
}

}

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top