1

I have the following view Hierarchy: View Hierarchy with my programmatically added MyCoordinatorLayout

So Let's say I have the following:

I programmatically inflate my MyCoordinatorLayout as a child of the LinearLayout. So somewhere this happens:

linearLayout.addView(myCoordinatorLayout, ..., ...);

Now I have the following code:

public class MyCoordinatorLayout extends CoordinatorLayout{
   public void inflate() {
       LayoutInflater.from(getContext()).inflate(R.layout.merge_header_custom_layout, this, true);
       WebView newWebView = new WebView(getContext());
       addView(newWebView, 0, new ViewGroup.LayoutParams.MATCH_PARENT, MATCH_PARENT);
   }

}

public class MyCustomLayout extends LinearLayout{
     @Override
     public void onAttachedToWindow() {
         Log.v(TAG, "onAttachedToWindow() called, now adding swipe behavior"); //This is called as expected!
         addSwipeBehavior();
     }

     private void addSwipeBehavior() {
        final SwipeDismissBehavior<View> swipe = new SwipeDismissBehavior<View>();
        swipe.setSwipeDirection(SWIPE_DIRECTION_ANY);
        swipe.setListener(new SwipeDismissBehavior.OnDismissListener() {
           @Override
           public void onDismiss(View view) {
              Log.v(TAG, "onDismiss called!");
           }
           @Override
           public void onDragStateChanged(int i) {
               Log.v(TAG, "onDragStateChanged()");
           }
        }
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) get LayoutParams();
        params.setBehavior(swipe);
        Log.v(TAG, "behavior=" + params.getBehavior); //This shows its set.
     }
}

Here is the problem. The swipe to dismiss does not work at all. In fact, those log statements I added are never printed out. Is it probably because my CoordinatorLayout was added programmatically, not existing in the XML beforehand like the examples I have seen?

I would really like my behavior to work, but it is just not being triggered. Also, I checked the MyCustomLayout and saw that its onTouchEvent(MotionEvent e) is triggered.... However, the behavior is still never triggered. I also did a getLayoutParams() and getBehavior() and it returns the correct behavior so I know the CoordinatorLayout has the correct Behavior, but the behavior isn't just working at all... Not sure if I missed something! Would be great to get some help or point me in the right direction!

ngoctranfire
  • 793
  • 9
  • 15

2 Answers2

0

I think the first issue is that the SwipeDismissBehavior needs to be associated with a swippable view and the behavior associated of how you want to swipe to dismiss, eg, side to side behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END); and then propagate the touch appropriately, or if in a RecyclerView use something like an ItemTouchHelper.

This SO post does a much better job of explaining how to properly set up the swipe to dismiss: How to use SwipeDismissBehavior.OnDismissListener on RecyclerView

Hope this helps.

Community
  • 1
  • 1
AllDayAmazing
  • 2,383
  • 1
  • 24
  • 25
  • Can you explain what you mean by it needs to be associated with a scrollable view? I'm guessing you are talking about the NestedScrollView? I tried to make my MyCustomLayout extends the NestedScrollView, but that didn't do anything either. Also, there's no toolbars here. – ngoctranfire Jan 09 '16 at 10:13
  • CoordinatorLayout isn't scrollable, it's just a ViewGroup - WebView, however, is. My though is this is why you aren't seeing the logging. You need to set it so that there is a scrollable something to listen to being scrolled. – AllDayAmazing Jan 11 '16 at 00:45
  • I don't actually understand what you mean... How exactly do I associate my parent CoordinatorLayout with a scrollable view...? I'm trying to have the SwipeDismissBehavior for all the children of the Coordinator Layout as I drew in my picture above. – ngoctranfire Jan 11 '16 at 02:46
  • Apologies - I completely misread your question the first time through. Updated the answer accordingly which should point you in the right direction. – AllDayAmazing Jan 11 '16 at 07:47
  • I've actually already tried that and I have that exact code too. It just didn't work as expected. I'm not sure what the deal is. – ngoctranfire Jan 11 '16 at 11:02
  • Also, `MyCustomLayout extends LinearLayout` yet you are calling `CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) getLayoutParams();` when `getLayoutParams()` should return the `LayoutParams` for the `LinearLayout` - might be an issue as well – AllDayAmazing Jan 12 '16 at 01:43
  • The layout for the getLayoutParams() is always pointing the parent I believe... I still can't get this to work... Not sure what the reason is. It might be that my view is on top of a view pager. – ngoctranfire Jan 12 '16 at 06:40
0

I was able to figure out why my touch interaction with the SwipeDismissBehavior was never called. The reason was quite simple. In my diagram, I forgot that at the top of the view hierarchy, and on top of the linear layout was a View Pager. Let me redraw the diagram. Detailed image showing the view hierarchy.

What was actually happening was that the ViewPager was intercepting my touch and never letting it reach the child layouts. There was a fix. What I needed to do was the following:

public class MySwipeDismissBehavior extends SwipeDismissBehavior<View> {
     private float mLastX; 

     @Override
     public boolean onInterceptTouchEvent(CoordinatorLayout parent, FrameLayout child, MotionEvent event) {
         if (parent.isPountInChildBounds(child, event.getX(), event.getY())) {
             ViewPager vp = findViewPager(child);
             switch(event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
                     disableViewPager(vp, true);
                     break;
                 case MotionEvent.ACTION_MOVE:
                     boolean swipingLeftToRight = (x-mLastX) > 0; //Allow the view pager to handle it if I swiping from left to right.
                     if (swipingLeftToRight) {
                         disableViewPager(vp, false);
                     } else { // if swiping from right to left, let the swipe dismiss behavior handle it.
                         disableViewPager(vp, true);
                     }
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: //As soon as it 
                     disableViewPager(vp, true);
                     break;

             }
         }

     }

     private void disableViewPager(ViewPager vp, boolean enable) {
         if (vp != null) {
            vp.requestDisallowInterceptTouchEvent(enable)
         }
     }

     private ViewPager findViewPager(View child) {
          ViewPager parent = child.getParent();
          while (parent != null) {
               parent = child.getParent();
               if (parent instanceof ViewPager) {
                   return (ViewPager) parent;
               } else if(!(parent instanceof View)) {
                   return null;
               } else {
                   child = (View) parent;
               }

          }
          return null;

     }
}

After I added this new behavior to my code, my swipe dismiss behavior works. I hope this is helpful to others. I got help and inspiration for my solution from here: Snack bar dismiss not working. It wasn't exactly the same problem, but it pointed me towards the right direction

Community
  • 1
  • 1
ngoctranfire
  • 793
  • 9
  • 15