Make fragment clickable when navigation drawer is opened
Asked Answered
H

3

7

My problem is as follows: I lock the navigation drawer menu setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN) in the landscape mode of the tablet, but I need the fragment from the right to be active, so I can click it with navigation always opened. But I dont know how to do it. Please help.

Screenshot

Hardan answered 12/2, 2015 at 19:43 Comment(1)
Possible duplicate of Navigation Drawer: set as always opened on tabletsLeilanileininger
S
12

There are a few things you need to do:

  1. Disable the layout fading by setting a transparent color:

    drawer.setScrimColor(Color.TRANSPARENT);
    
  2. Lock the drawer

    drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
    
  3. Create a custom drawer class which allows clicking through when in locked mode:

    public class CustomDrawer extends DrawerLayout {
    
        public CustomDrawer(Context context) {
            super(context);
        }
    
        public CustomDrawer(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public CustomDrawer(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            View drawer = getChildAt(1);
    
            if (getDrawerLockMode(drawer) == LOCK_MODE_LOCKED_OPEN && ev.getRawX() > drawer.getWidth()) {
                return false;
            } else {
                return super.onInterceptTouchEvent(ev);
            }
        }
    
    }
    
  4. Use this class in xml:

    <com.example.myapplication.CustomDrawer
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!-- The main content view -->
        </FrameLayout>
        <ListView android:layout_width="100dp"
                  android:layout_height="match_parent"
                  android:layout_gravity="start"
                  android:background="#111"/>
    </com.example.myapplication.CustomDrawer>
    
Simulator answered 12/2, 2015 at 20:6 Comment(3)
Can you still click on the items from your drawer since it returns false for the whole screen?Pulsatile
@Pulsatile for that you'll need to check if the touch even is performed on the ListView or not. Updated my code accordingly.Simulator
I think this will only work for left drawers, right drawers need something like getRawX() < getWidth() - drawer.getWidth(), even then RTL start/end may mess this up!Skidproof
P
2

This is a tricky one. When the drawer is open, it intercepts your touch events which trigger the close of the drawer. In order to prevent that, you need to subclass your DrawerLayout and override the onInterceptTouchEvent method:

public class CustomDrawerLayout extends DrawerLayout
{
    private View rightView;
    private int mTouchSlop;

    public CustomDrawerLayout (Context context)
    {
        this(context, null);
    }

    public CustomDrawerLayout (Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public CustomDrawerLayout (Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(context));
    }

    public void setRightView (View v)
    {
        this.rightView = v;
    }

    @Override
    public boolean onInterceptTouchEvent (MotionEvent ev)
    {
        boolean result = super.onInterceptTouchEvent(ev);

        if (rightView != null && isDrawerOpen(rightView))
        {
            DrawerLayout.LayoutParams layoutParams = (DrawerLayout.LayoutParams) rightView.getLayoutParams();

            if (layoutParams.gravity == Gravity.END)
            {
                // This is true when the position.x of the event is happening on the left of the drawer (with gravity END)
                if (ev.getX() < rightView.getX() && ev.getX() > mTouchSlop)
                {
                    result = false;
                }
            }
        }

        return result;
    }
}

This is my code working with a right drawer. I'm sure you can adapt this for your left drawer. You might also want to disable the shadow:

mDrawerLayout.setScrimColor(Color.TRANSPARENT);
Pulsatile answered 12/2, 2015 at 19:56 Comment(0)
F
0

Thanks for @Simas's solution!

I found if an item which user click is quite near drawerView, use ev.rawX is not appropriate. Furthermore, I add other gravity check to determinate interception.

class ContentTouchableDrawer @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : DrawerLayout(context, attrs) {

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        val drawer: View = getChildAt(1)
        logt("drawer : $drawer")
        logt("drawer : width = ${drawer.width}")
        logt("drawer : x = ${drawer.x}")
        logt("drawer : eventRawX = ${ev.rawX}")
        logt("drawer : eventX = ${ev.x}")


        val drawerGravity = (drawer.layoutParams as LayoutParams).gravity
        val result = when(drawerGravity){
            Gravity.RIGHT, GravityCompat.END -> ev.x < drawer.x
            Gravity.LEFT, GravityCompat.START -> ev.x > drawer.width
            //Gravity.NO_GRAVITY
            else -> false
        }

        return if (getDrawerLockMode(drawer) == LOCK_MODE_LOCKED_OPEN && result) {
            false
        } else {
            super.onInterceptTouchEvent(ev)
        }
    }


}
Frustrate answered 26/10, 2022 at 6:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.