Search code examples
androidandroid-viewpagerswipeontouchlistenerontouch

Would it be possible to intercept touch event and transfer to ListView if user swipes left or right in ViewPager


I have a ViewPager that contains two fragments, which each contains listviews with swipeable list items. To make it easier for the user to swipe, I would like it so that if the user swipes left in the first fragment, the swipe is picked up by the listview. If the user swipes right in the second fragment, the swipe is picked up by the listview. Since there is only two fragments, it makes sense for the swipes to be picked up by the listview, and not the viewpager since there are no more fragments to scroll to after these two.

So, would it be possible to intercept the touch, and pass it to the listview based on the viewpager page being shown the and the direction of the swipe?

Here is the swipable listview library I am using: https://github.com/timroes/EnhancedListView


Solution

  • I think you can customize the ViewPager and control the behavior of ListView swipe in canScroll method of the ViewPager.

    I tried a sample and it seems to be working fine. You can use this Custom ViewPager.

    public class CustomViewPager extends ViewPager {
    
        public CustomViewPager(Context context) {
            super(context);
        }
    
        public CustomViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
            if (v instanceof ViewPager) {
                if (getChildAt(getCurrentItem()) != null) {
                    //get the ListView of current Fragment
                    EnhancedListView enhancedListView = (EnhancedListView) getChildAt(getCurrentItem()).findViewById(R.id.list);
                    //If the user is in first page and tries to swipe left, enable the ListView swipe
                    if (getCurrentItem() == 0 && dx > 0) {
                        enhancedListView.enableSwipeToDismiss();
                    } 
                    //If the user is in second page and tries to swipe right, enable the ListView swipe
                    else if (getCurrentItem() == 1 && dx < 0) {
                        enhancedListView.enableSwipeToDismiss();
                    } 
                    //Block the ListView swipe there by enabling the parent ViewPager swiping
                    else {
                        enhancedListView.disableSwipeToDismiss();
                    }
                }
            }
            return super.canScroll(v, checkV, dx, x, y);
        }
    
    }
    

    Also you have to do some changes in EnhancedListView library as well. Because canScroll method is called after ACTION_DOWN event, if we enable the swiping in canScroll method - it skips the logic defined forACTION_DOWN event and it might results in unexpected behavior. So block the swipe only if touch event is ACTION_MOVE. These are the changes in onTouchEvent of EnhancedListView library.

    //EnhancedListView class
    @Override
        public boolean onTouchEvent(MotionEvent ev) {
    
            if (!mSwipeEnabled && (ev.getAction() == MotionEvent.ACTION_MOVE)) {
                return super.onTouchEvent(ev);
            }
    
         .....
    

    I am not sure if this is the perfect solution for the problem but it is working just fine.

    Here are some screenshots of the sample app if the question or the answer is not clear.

    • App consists of ViewPager with two Fragment pages. Each page has an EnhanedListView (Which provides Swipe To Dismiss/Delete feature). Since both parent ViewPager and child List item can be swiped, it causes a conflict. By default swipe is picked up by the child ListItem which prevents the parent ViewPager from swiping.

      enter image description here

    Required solution:

    • If the user is in first page and swipes right then list item should be swiped.

      enter image description here

    • If the user is in second page and swipes left then list item should be swiped.

      enter image description here

    • In other cases the ViewPager should be swiped.

    update : To fix the bug with SwipeRefreshLayout, here are the slight changes in custom ViewPager code.

     public class ScrollLock extends ViewPager {
    
    
            public ScrollLock(Context context) {
                super(context);
            }
    
            public ScrollLock(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
    
            @Override
            protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
                if (v instanceof ViewPager) {
                    if (getChildAt(getCurrentItem()) != null) {
                        //set it so it does not swipe to refresh while swiping away a list item
                        SwipeRefreshLayout swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe);
    
    //get the ListView of current Fragment
                        EnhancedListView enhancedListView = (EnhancedListView) getChildAt(getCurrentItem()).findViewById(R.id.listView1);
                        //If the user is in first page and tries to swipe left, enable the ListView swipe
                        if (getCurrentItem() == 0 && dx > 0) {
                            enhancedListView.enableSwipeToDismiss();
                            swipeLayout.setEnabled(false);
                            return true;
                        } 
                        //If the user is in second page and tries to swipe right, enable the ListView swipe
                        else if (getCurrentItem() == 1 && dx < 0) {
                            enhancedListView.enableSwipeToDismiss();
                            swipeLayout.setEnabled(false);
                            return true;
                        } 
                        //Block the ListView swipe there by enabling the parent ViewPager swiping
                        else {
                            enhancedListView.disableSwipeToDismiss();
                        }
                    }
                }
                return super.canScroll(v, checkV, dx, x, y);
            }
    
        }