Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwipeListView only one item opened at a time

This question refers to the SwipeListView component found here: https://github.com/47deg/android-swipelistview

After trying out several implementations and fixes I found on the web I decided to modify the sources a little.

I will post this here since i know it's a known issue and all the versions I found proved to have some issues eventually.

SwipeListViewTouchListener.java has suffered the following changes:

...
/**
     * Create reveal animation
     *
     * @param view      affected view
     * @param swap      If will change state. If "false" returns to the original
     *                  position
     * @param swapRight If swap is true, this parameter tells if movement is toward
     *                  right or left
     * @param position  list position
     */
    private void generateRevealAnimate(final View view, final boolean swap, final boolean swapRight, final int position) {
        int moveTo = 0;
        if (opened.get(position)) {
            if (!swap) {
                moveTo = openedRight.get(position) ? (int) (viewWidth - rightOffset) : (int) (-viewWidth + leftOffset);
            }
        } else {
            if (swap) {
                moveTo = swapRight ? (int) (viewWidth - rightOffset) : (int) (-viewWidth + leftOffset);
            }
        }
        final boolean aux = !opened.get(position);
        if(swap) {
            opened.set(position, aux);
            openedRight.set(position, swapRight);
        }

        animate(view).translationX(moveTo).setDuration(animationTime).setListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                swipeListView.resetScrolling();

                if (swap) {
                    if (aux) {
                        swipeListView.onOpened(position, swapRight);
                    } else {
                        swipeListView.onClosed(position, openedRight.get(position));
                    }
                }
                // if (aux || !swap) {
                // resetCell();
                // }
            }
        });
    }
...

/**
     * Close all opened items
     */

    void closeOtherOpenedItems() {
        if (opened != null && downPosition != SwipeListView.INVALID_POSITION) {
            int start = swipeListView.getFirstVisiblePosition();
            int end = swipeListView.getLastVisiblePosition();
            for (int i = start; i <= end; i++) {
                if (opened.get(i) && i != downPosition) {
                    closeAnimate(swipeListView.getChildAt(i - start).findViewById(swipeFrontView), i);
                }
            }
        }

    }
...

/**
     * @see View.OnTouchListener#onTouch(android.view.View,
     * android.view.MotionEvent)
     */
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
...
closeOtherOpenedItems();
view.onTouchEvent(motionEvent);
return true;
}

The rest of the code not mentioned is the same.

Any comments highly appreciated, this changes prevent you from having to implement the SwipeListViewOnTouchListener in the activity which inflates the list.

like image 630
Adrian Olar Avatar asked Mar 24 '14 08:03

Adrian Olar


3 Answers

Cons: doesn't close the row opened by openAnimate()

   BaseSwipeListViewListener swipeListViewListener = new BaseSwipeListViewListener() {
    int openItem = -1;

    @Override
    public void onStartOpen(int position, int action, boolean right) {
        super.onStartOpen(position, action, right);

        if (openItem > -1)
            swipeListView.closeAnimate(openItem);

        openItem = position;
    }
   }

Or better way:

 @Override
    public void onStartOpen(int position, int action, boolean right) {
        super.onStartOpen(position, action, right);
        swipeListView.closeOpenedItems();
    }

And set the listener to the listView:

   swipeListView.setSwipeListViewListener(swipeListViewListener);
like image 131
lordmegamax Avatar answered Nov 18 '22 11:11

lordmegamax


If you're going to modify the swipelistview library itself I have a simpler solution.

Add the following if block to SwipeListViewTouchListener.java in the onTouch method right at the beginning of case MotionEvent.ACTION_DOWN:

if(lastOpenedPosition != downPosition && opened.get(lastOpenedPosition)) {
    closeAnimate(lastOpenedPosition);
    return false;
}

Create an int lastOpenedPosition field and initialize it to 0, and in the generateRevealAnimate method inside the if (aux) block add:

lastOpenedPosition = position;

I would also add config variable (in res/values/swipelistview_attrs.xml) to SwipeListView and add it to the onTouch if block, to add the ability to turn this feature off and on. This basically results in if the list is touched while a row is open, than the row will close. Which, imho, is better functionality than the row closing only after you finished opening another row.

like image 1
Leo K Avatar answered Nov 18 '22 10:11

Leo K


Your fix worked, but there is a way to do it without affecting the original code:

swipeListView.setSwipeListViewListener(new BaseSwipeListViewListener() {
    int openItem = -1;
    int lastOpenedItem = -1;
    int lastClosedItem = -1;

    @Override
    public void onOpened(int position, boolean toRight) {
        lastOpenedItem = position;
        if (openItem > -1 && lastOpenedItem != lastClosedItem) {
            swipeListView.closeAnimate(openItem);
        }
        openItem = position;
    }

    @Override
    public void onStartClose(int position, boolean right) {
        Log.d("swipe", String.format("onStartClose %d", position));
        lastClosedItem = position;
    }
}

You should however, send a pull request to apply your code as that would fix the bug.

Source: https://github.com/47deg/android-swipelistview/issues/46

like image 2
spacebiker Avatar answered Nov 18 '22 12:11

spacebiker