Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OnTouchListener prevents visual feedback

I have a ListView which has an OnItemClickListener. On top of that, each row (CardView) has an OnTouchListener in order to implement a swiping gesture.

The OnTouchListener reacts to ACTION_DOWN, ACTION_CANCEL, ACTION_MOVE and ACTION_UP. I use performItemClick() when ACTION_UP happens, so that the OnItemClickListener is called when required.

If I return true in the OnTouchListener, the swiping movement I implemented via ACTION_MOVE works perfectly, clicking the item works as well. However, there is zero visual feedback. Usually, there would be a ripple on Lollipop or a background change on ICS.

If I return False (meaning I don't want to intercept the event), then there is a visual feedback and the clicking works... But my OnTouchListener never intercepts any ACTION_MOVE event. This prevents any swiping.

I tried a variety of solutions, such as using v.setpressed() but it had no effect.

I'm curious to know how I could preserve the ripple (or visual feedback in general) that would happen if my OnTouchListener wasn't intercepting the event.

Here's my OnTouchListener, if you're curious.

private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
    float mDownX;
    private int mSwipeSlop = -1;
    private boolean mItemPressed;
    private VelocityTracker mVelocityTracker = null;
    private HashMap<Long, Integer> mItemIdTopMap = new HashMap<>();

    @Override
    public boolean onTouch(final View v, MotionEvent event) {
        int index = event.getActionIndex();
        int pointerId = event.getPointerId(index);

        if (mSwipeSlop < 0) {
            mSwipeSlop = ViewConfiguration.get(getActivity())
                    .getScaledTouchSlop();
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mItemPressed) {
                    // Multi-item swipes not handled
                    return false;
                }
                mItemPressed = true;
                mDownX = event.getX();
                if (mVelocityTracker == null) {
                    // Retrieve a new VelocityTracker object to watch the velocity of a motion.
                    mVelocityTracker = VelocityTracker.obtain();
                } else {
                    // Reset the velocity tracker back to its initial state.
                    mVelocityTracker.clear();
                }
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_CANCEL:
                v.setAlpha(1);
                v.setTranslationX(0);
                mItemPressed = false;
                mVelocityTracker.recycle();
                mVelocityTracker = null;
                break;
            case MotionEvent.ACTION_MOVE: {
                mVelocityTracker.addMovement(event);
                float x = event.getX() + v.getTranslationX();
                float deltaX = x - mDownX;
                float deltaXAbs = Math.abs(deltaX);
                if (!mSwiping) {
                    if (deltaXAbs > mSwipeSlop) {
                        mSwiping = true;
                        getListView().requestDisallowInterceptTouchEvent(true);
                        mBackgroundContainer.showBackground(v.getTop(), v.getHeight());
                    }
                }
                if (mSwiping) {
                    v.setTranslationX((x - mDownX));
                    v.setAlpha(1 - deltaXAbs / v.getWidth());
                }
            }
            break;
            case MotionEvent.ACTION_UP: {
                // User let go - figure out whether to animate the view out, or back into place
                if (mSwiping) {
                    float x = event.getX() + v.getTranslationX();
                    float deltaX = x - mDownX;
                    float deltaXAbs = Math.abs(deltaX);
                    float fractionCovered;
                    float endX;
                    float endAlpha;
                    final boolean remove;
                    mVelocityTracker.computeCurrentVelocity(1000);
                    float velocityX = Math.abs(VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId));
                    if (velocityX > 700 || deltaXAbs > v.getWidth() / 4) {
                        // fixme
                        fractionCovered = deltaXAbs / v.getWidth();
                        endX = deltaX < 0 ? -v.getWidth() : v.getWidth();
                        endAlpha = 0;
                        remove = true;
                    } else {
                        // Not far enough - animate it back
                        fractionCovered = 1 - (deltaXAbs / v.getWidth());
                        endX = 0;
                        endAlpha = 1;
                        remove = false;
                    }
                    mVelocityTracker.clear();

                    int SWIPE_DURATION = 600;
                    long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);
                    getListView().setEnabled(false);
                    v.animate().setDuration(duration).
                            alpha(endAlpha).translationX(endX).
                            withEndAction(new Runnable() { // fixme replace with AnimationListener
                                @Override
                                public void run() {
                                    // Restore animated values
                                    v.setAlpha(1);
                                    v.setTranslationX(0);
                                    if (remove) {
                                        animateRemoval(getListView(), v);
                                    } else {
                                        mBackgroundContainer.hideBackground();
                                        mSwiping = false;
                                        getListView().setEnabled(true);
                                    }
                                }
                            });
                } else {
                    int position = getListView().getPositionForView(v);
                    if (position != ListView.INVALID_POSITION)
                        getListView().performItemClick(v, position, getListView().getItemIdAtPosition(position));
                }
            }
            mItemPressed = false;
            break;
            default:
                return false;
        }
        return true;
    }

    private void animateRemoval(final ListView listview, View viewToRemove) {
        // irrelevant
    }
};

It's based on Chet Haase's demo from a couple of years back.

I really can't figure it out. Help?

Thanks!

like image 831
geecko Avatar asked Jul 26 '15 14:07

geecko


1 Answers

to me, v.setPressed did the trick, where v is the view receiving the touch event. Meaning that on ACTION_DOWN I invoked v.setPressed(true) and on ACTION_UP v.setPressed(false)

like image 102
Antonio Pet Avatar answered Nov 08 '22 17:11

Antonio Pet