6

my question is simple. Can I use an HorizontalScrollView inside the content menu of a DrawerLayout? My DrawerLayout looks like this:

<android.support.v4.widget.DrawerLayout
    android:id="@+id/pnlMenu"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- Main content view -->

    <ListView
        android:id="@+id/lst"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true"
        android:fastScrollEnabled="true"
        android:focusable="true"
        android:focusableInTouchMode="true"
        tools:listitem="@layout/item_layout" >
    </ListView>

    <!-- Content of menu -->

    <FrameLayout
        android:id="@+id/drawerFrame"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:clickable="true"
        android:background="@color/black" >

        <fragment
            android:id="@+id/frag"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            class="com.test.TestFragment" />
    </FrameLayout>
</android.support.v4.widget.DrawerLayout>

Inside the fragment I have the HorizontalScrollView but when I try to touch it nothing happen because the drawer layout follow my finger. I think that disabling the touch events inside the content menu and make DrawerLayout closable only when main content view is clicked will solve my problem. Is that true? If not, can someone tell me what can I do?

Thank you.

David Martinelli
  • 233
  • 6
  • 16

3 Answers3

6

Based on this solution: https://stackoverflow.com/a/7258579/452486

I've been able to make HorizontalScrollView scrollable. Create a class extends DrawerLayout:

public class AllowChildInterceptTouchEventDrawerLayout extends DrawerLayout {

    private int mInterceptTouchEventChildId;

    public void setInterceptTouchEventChildId(int id) {
       this.mInterceptTouchEventChildId = id;
    }

    public AllowChildInterceptTouchEventDrawerLayout(Context context) {
    super(context);
    }

    public AllowChildInterceptTouchEventDrawerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        if (mInterceptTouchEventChildId > 0) {
            View scroll = findViewById(mInterceptTouchEventChildId);
            if (scroll != null) {
                Rect rect = new Rect();
                scroll.getHitRect(rect);
                if (rect.contains((int) ev.getX(), (int) ev.getY())) {
                    return false;
                }
            }
        }
        return super.onInterceptTouchEvent(ev);

        }
    }

And add the child id which you want to intercept the touch event of drawerlayout

AllowChildInterceptTouchEventDrawerLayout drawerLayout = (AllowChildInterceptTouchEventDrawerLayout) findViewById(R.id.layoutdrawer_id);
drawerLayout.setInterceptTouchEventChildId(R.id.horizontalscrollview_id);
Community
  • 1
  • 1
Arst
  • 3,098
  • 1
  • 35
  • 42
1

It's better to set requestDisallowInterceptTouchEvent(true) flag instead of returning false. When We have a HorizontalScrollView and below it there's another view (eg. ListView with some menu items) and we make a dynamic gesture from HorizontalScrollView area to the aforementioned ListView the app will crash with a NPE. This case happens when you open the app for the first time and go to a DrawerLayout through the hamburger icon. Use this:

if (rect.contains((int) ev.getX(), (int) ev.getY())) {     
    this.requestDisallowInterceptTouchEvent(true);
}
buczek
  • 2,011
  • 7
  • 29
  • 40
0

If you want to have HorizontalScrollView inside your DrawerLayout an alternative strategy to all the answers so far would be to lock the drawer when opened. That means user won't be able to close the drawer with a swipe but the scrolling inside HorizontalScrollView will work as expected.

The locking is achieved by calling

mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);

A convenient place for this call would be in onDrawerOpened.

Unfortunately the locking also prevents drawer from closing when user taps the scrim (the dimmed part of the screen not occupied by drawer). You'll need to catch that tap and close it yourself, with something like this:

mDrawerLayout.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int x = (int) event.getX();
        int drawerWidth = (int)getResources().getDimension(R.dimen.your_drawer_width);
        if (x > drawerWidth) {
            // inside scrim
            mDrawerLayout.closeDrawer();
            return true;
        }
        return false;
    }
}); 
Nemanja Kovacevic
  • 3,510
  • 2
  • 30
  • 45
  • Simple solution. Additionally, you need to set drawer lock mode to undefined before closing drawer by `mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED)`. I have found that without this the drawer does not open once closed – rakex Feb 28 '17 at 18:01