How to show the DrawerLayout when sliding from left to right, no matter where?
Asked Answered
A

8

8

background

google has introduced the DrawerLayout, which shows a menu on the left area of the screen when you click on the "up" button of the action bar.

because the library isn't supported yet on actionBarSherlock, there is already a way to overcome it using this project .

it already has variants on many apps: currents, gmail, hangouts, youtube...

the question

on the "currents" app (and in youtube) , when the user slides the (most-left) page from left to right , the DrawerLayout appears, no matter where the finger has started the touch .

how can i achieve the same effect? maybe i should use onInterceptTouchEvent ?

there isn't much documentation and tutorials of what cool things can be done , other than this link (ok and this one too) . they say (in the part of "Give the user a quick peek") that about 20dp on the left is used for this functionality, but i can see that "currents" work with much larger area.

it seems the library is still not quite finished, and so the layout xml file cannot even be shown in the visual UI editor...


EDIT: it seems that the library is open sourced. code is available on :

.../android-sdk\sources\android-18\android\support\v4\widget\DrawerLayout.java
.../android-sdk\sources\android-18\android\support\v4\widget\SlidingPaneLayout.java
.../android-sdk\sources\android-18\android\support\v4\app\ActionBarDrawerToggle.java

now the question is how to make it work as i've written, so that it would work like on youtube , allowing us to customize how it looks and from where to allow scrolling it.

Alfano answered 17/7, 2013 at 12:33 Comment(14)
I'm not sure if I understood correctly, I am using new Navigation drawer also, and It works always without any extra setting. I'm working on a project with: ABS + Navigation Drawer + Fragments + GMaps and I've had no problems sliding right or left drawer from most-left or most-right side of the whole screen. Are you declaring the drawers in the xml as recommended?Westernize
i'm using what i've posted. are you saying i don't need to look at this project, and just use what google has? does it work fine with ABS , and does it allow me to customize how far it will trigger scrolling of the navigation drawer (for example from the middle of the screen) ? if so, how? the sample app that they have only allows scrolling when you start touching on the most-left side of the screen...Alfano
Well, Im absolutely sure ABS + new navigation drawer integrate without any problem, without any additional help like the project that you posted. As far as the area of screen that gets recognized when sliding for the drawer to appear, is quite thin, and I'm not sure if can be modified such as sliding library can (none, edge, whole screen). Example you give (youtube) is not valid, It does not use nav drawer, I would say they use sliding menu lib. Google+ does use the new nav drawer, and if you try, area of the screen to slide is same thin left border.Westernize
youtube does have a navigation drawer. maybe it doesn't look the same, but it works. maybe you should update the app. alternatively, you can try out "currents" by google. anyway, my question was how can i make it customizable, so that i can control where to trigger the navigation drawer scrolling?Alfano
Ok I've found the answer. unfortunately is not possible. Just 20dp from the edge: Give the user a quick peek If the user touches the very left edge of the screen (within 20 dp from the left), have the drawer peek out as soon as the finger makes contact with the display. This promotes accidental discovery and provides richer feedback. Look at here. In that case you need to have another way to determine the touch in the area you wnt and trigger the drawer by using mDrawerLayout.openDrawer(mDrawer)Westernize
@khale912 yes that's what i've written. anyway, if they say we should give this space, it means that it is customizable, no? isn't is part of the guidelines? to allow at least this width ? 20dp is so very thin, and i don't want to have a whole screen scrolling trigger...Alfano
i don't want to open the drawer. i want to scroll it with the touch of the user, so that only when fully scrolled to the right and stop touching the screen, it shows the entire drawer, but if you are in the middle of scrolling, show only a part of it. please, just try out "youtube" or "currents" if you can't understand what i mean.Alfano
I have checked both of them, they use whole screen to slide the left menu, and this menu is behind the view (you can tell by the shadow) and the content slides to the right. They perfectly can be using slidingmenu library. Look, if they were using new navigation drawer, the left menu(drawer) should be over the content, leaving a shadow between drawer and content and without sliding the content to the right, just as G+ left drawer does. I'd bet both currents and youtube use slidingmenu library and they slide using getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN)Westernize
so no customization even though the text imply about it? too bad. can you think of a way to overcome this? this was the whole point of my question and i thought that it is possible, and even gave some ideas. even the IDE itself can't show the layout right.Alfano
i also can't find which class has the function getSlidingMenu() ...Alfano
getSlidingMenu is from slidingmenu jfeinstein library (3rd party), btw, i already got the answer to customize the default 20dp, I'll post it in a couple of minutes while I try myself some details.Westernize
Seems to be like google does not want this 20dp to be changed that's why they don't give access to this property easily. But there's a way to change it, check the answer I just posted. Already tried, works.Westernize
the answer doesn't work as expected. plus i've noticed it doesn't have the same effect of scrolling as on youtube. on youtube, the right area is above the left drawer and as you scroll, it shows a part of the drawer. using the normal library, it's the opposite. i wonder why the navigation drawer isn't open source.Alfano
If you really want the youtube or currents effect, consider to implement jfeinstein library... It's awesome, I've tried and it works smoothly, only issue is that it's a bit tricky when you want to use maps as main content/fragment... that's why I changed to official nav drawerWesternize
D
7

SlidingMenu is best sliding library I've ever found, It's very good library.
You can set getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN) enabling fling for all screen.

Dyestuff answered 23/7, 2013 at 16:32 Comment(9)
i wish to customize the area that is allowed. also, does this library support actionBarSherlock?Alfano
Yes! It's integrated with actionbarsherlock library, as i say in my answer your can `setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN), which controls whether the SlidingMenu can be opened with a swipe gestureDyestuff
Other customization option are mSlidingMenu.setShadowWidthRes() , mSlidingMenu.setShadowDrawable(), mSlidingMenu.setBehindWidthRes(), mSlidingMenu.setFadeDegree() an the like. I've tried almost all of sliding menu libraries on github. SlidingMenu is best by farDyestuff
so what can i do so that if (for example) i want that when the finger touches on the left area of the half the screen it would show the drawer, yet if it's on the right half of the screen, it won't?Alfano
i also wish to be able to customize the drawer to have any views i want. does it support it like the library i've shown?Alfano
what can i do in order to make it have the ability to slide from half the screen but not more than that ? also , is it possible to make it look as if the right area is on top and it just moves it aside and shows the drawer, like on youtube?Alfano
For half the screen, I think you should calculate screen size and DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); int width = metrics.widthPixels; and then mSlidingMenu..setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN); and mSlidingMenu.setTouchmodeMarginThreshold(width/2);Dyestuff
and if you don't want to move ActionBar set this: setSlidingActionBarEnabled(false);Dyestuff
i think i will use this library instead of the one that google provides, even though i think it is possible somehow to use theirs. it's too bad they don't have javaDocs (API) . thank you.Alfano
S
4

The problem is that DrawerLayout uses ViewDragHelper which has a default EDGE_SIZE of 20dp which is used to calculate the mEdgeSize like this:

mEdgeSize = (int) (EDGE_SIZE * density + 0.5f);

Here is a function which sets mEdgeSize to a percentage of the display width:

public static void setDrawerLeftEdgeSize(Activity activity, DrawerLayout drawerLayout, float displayWidthPercentage) {
    if (activity == null || drawerLayout == null)
        return;

    try {
        // find ViewDragHelper and set it accessible
        Field leftDraggerField = drawerLayout.getClass().getDeclaredField("mLeftDragger");
        leftDraggerField.setAccessible(true);
        ViewDragHelper leftDragger = (ViewDragHelper) leftDraggerField.get(drawerLayout);
        // find edgesize and set is accessible
        Field edgeSizeField = leftDragger.getClass().getDeclaredField("mEdgeSize");
        edgeSizeField.setAccessible(true);
        int edgeSize = edgeSizeField.getInt(leftDragger);
        // set new edgesize
        Point displaySize = new Point();
        activity.getWindowManager().getDefaultDisplay().getSize(displaySize);
        edgeSizeField.setInt(leftDragger, Math.max(edgeSize, (int) (displaySize.x * displayWidthPercentage)));
    } catch (NoSuchFieldException e) {
        // ignore
    } catch (IllegalArgumentException e) {
        // ignore
    } catch (IllegalAccessException e) {
        // ignore
    }
}

So, let's say you want 30% of your left edge to react to slide events and open the Navigation Drawer, then simply call:

mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
setDrawerLeftEdgeSize(this, mDrawerLayout, 0.3f);
Steib answered 14/11, 2013 at 18:38 Comment(4)
it still works in a wacky way. try to set the size to 0.5f , and then only long click on the left side. it will still trigger showing the drawer, and it shows it in a very fast way. BTW, you've used a too-high API for just getting the width of the screen. instead, you can use : activity.getResources().getDisplayMetrics().widthPixelsAlfano
I tried with 0.7f here and the long click. The animation speed is still "fine" for me (I'm on an S3). But, I guess the duration of the animation is fixed, and therefore to animate the drawer that much more in the same matter of time - it will simply have to move faster. Note: I used the 'getSize(..)' on purpose as I'm on a recent API level.Steib
long clicking should not trigger showing the sliding drawer. the movement of the drawer moves in a weird way, and it's hard for me to explain. I also have SGS3 . about getSize(), you should enable Lint checks on save, so that it will warn you about using too-new functions/classes . the targetSdk in the manifest should be the latest (19 by now) yet the minSdk can be whatever you wish to support (8 or 9 covers nearly 100% ).Alfano
Yes long click should peek out the drawer - it is mentioned here for example plus.google.com/+AdamWPowell/posts/8j2GVw72i1E. Or, do you mean it should only peek when clicking on the edge? And yes, I've set 'minSdkVersion=14' on purpose. These days setting API 8 (or 9) generates too much compatibility pain compared to the market share IMO.Steib
L
3

I tried changing default EDGE_SIZE solution, and encountered the long clicking problem too. Finally, Ifound override dispatchTouchEvent and open drawer depending the sliding direction by calculating X offset.

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            startX = ev.getX();
            startY = ev.getY();
            break;
        case MotionEvent.ACTION_UP:
            endX = ev.getX();
            endY = ev.getY();

            float sensitivity = 5;
            // From left to right
            if (endX - startX >= sensitivity) {
                if (mDrawerLayout.isDrawerOpen(Gravity.RIGHT)) {
                    mDrawerLayout.closeDrawer(Gravity.RIGHT);
                } else {
                    mDrawerLayout.openDrawer(Gravity.LEFT);
                }
            }

            // From right to left
            if (startX - endX >= sensitivity) {
                if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
                    mDrawerLayout.closeDrawer(Gravity.LEFT);
                } else {
                    mDrawerLayout.openDrawer(Gravity.RIGHT);
                }
            }

            break;
    }
Losse answered 19/12, 2014 at 7:17 Comment(0)
W
2

By using others' help: (first) (second)

I found that using reflections it's achievable to modify the defult 20dp-screen-edge for the drawer menu to slide

After declaring content and drawers, you can do this:

public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mDrawerList = (ListView) findViewById(R.id.left_drawer);

    // set a custom shadow that overlays the main content when the drawer opens
    mDrawerLayout.setDrawerShadow(R.drawable.your_drawer_shadow, GravityCompat.START);
    // set up the drawer's list view with items and click listener
    mDrawerList.setAdapter(new ArrayAdapter<String>(this,
            R.layout.your_drawer_list, yourItems));
    mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

    // enable ActionBar app icon to behave as action to toggle nav drawer
    getActionBar().setDisplayHomeAsUpEnabled(true);
    getActionBar().setHomeButtonEnabled(true);

    Field mDragger = null;
    try {
        mDragger = mDrawerLayout.getClass().getDeclaredField(
                "mLeftDragger"); //mRightDragger for right obviously
    } catch (NoSuchFieldException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    mDragger.setAccessible(true);
    ViewDragHelper draggerObj = null;
    try {
        draggerObj = (ViewDragHelper) mDragger
                .get(mDrawerLayout);
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    Field mEdgeSize = null;
    try {
        mEdgeSize = draggerObj.getClass().getDeclaredField(
                "mEdgeSize");
    } catch (NoSuchFieldException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    mEdgeSize.setAccessible(true);
    int edge = 0;
    try {
        edge = mEdgeSize.getInt(draggerObj);
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    try {
        mEdgeSize.setInt(draggerObj, edge * 5); //optimal value as for me, you may set any constant in dp
        //You can set it even to the value you want like mEdgeSize.setInt(draggerObj, 150); for 150dp
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    // ActionBarDrawerToggle ties together the the proper interactions
    // between the sliding drawer and the action bar app icon
    mDrawerToggle = new ActionBarDrawerToggle(
            this,                  /* host Activity */
            mDrawerLayout,         /* DrawerLayout object */
            R.drawable.ic_drawer,  /* nav drawer image to replace 'Up' caret */
            R.string.drawer_open,  /* "open drawer" description for accessibility */
            R.string.drawer_close  /* "close drawer" description for accessibility */
            ) {
        public void onDrawerClosed(View view) {

        }

        public void onDrawerOpened(View drawerView) {

        }
    };
    mDrawerLayout.setDrawerListener(mDrawerToggle);

}
}

Well, It works for me man!

Westernize answered 29/7, 2013 at 20:13 Comment(4)
good job , but: 1.isn't using reflection a bit risky? is it impossible to extend the class and use what is already there? maybe just copy its code (where is the code anyway?)? 2. it doesn't work well as it stops scrolling way before the content is fully shown. 3.you could put the entire new code in a single try-catch, since if one failed, the rest will also failAlfano
well, 1. yes it's risky as long as you don't catch properly all the exceptions, in case something doesn't work as expected, you need to be prepared with a B plan (that would be default 20-dp in this case, or just the home button). It would be awesome to extend and override methods, issue is, we don't have full acces to it, not opensource. :( 2. I noticed that as well, It shows some extra withe space after all scrolling 3. You could try that, but you would be losing assigment space in memory initializing all variables you are going to use inside of the try{} part, not a huge part though.Westernize
1. actually it is open source. i've updated the question to include the path to the files. 2. there must be a better way. 3. in this small sample, you don't need the variables in case it failed in any of the steps.Alfano
This sample code jumps straight to the NoSuchFieldException on my kitkat HTC One.Delegacy
N
2

You can use this with Navigation Drawer

DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
Field mDragger = mDrawerLayout.getClass().getDeclaredField(
    "mLeftDragger");//mRightDragger or mLeftDragger based on Drawer Gravity
mDragger.setAccessible(true);
ViewDragHelper draggerObj = (ViewDragHelper) mDragger
    .get(mDrawerLayout);

Field mEdgeSize = draggerObj.getClass().getDeclaredField(
    "mEdgeSize");
mEdgeSize.setAccessible(true);
int edge = mEdgeSize.getInt(draggerObj);

mEdgeSize.setInt(draggerObj, edge * 3); 
Naples answered 4/11, 2013 at 9:21 Comment(2)
it works , but it's wacky . if i change it to 30 instead of 3 , it works really weird.Alfano
Thanks for this hint! I improved it a bit in a separate answer below.Steib
B
1

Firstly I am not sure what you mean about ActionBarSherlock not supporting DrawerLayout. You can work with the DrawerLayout and ActionBarSherlock quite happily, I have apps that are using this combination. You will need to change the support jar version but I have found that ActionbarSherlock does not have a problem with this.

Implement DrawerLayout in the usual manner. Then in your fragment implement onTouchEvent or interceptTouchEvent and when the type is a 'move' type event sliding from left to right you can call the openDrawer method on the DrawerLayout. You will need to add handling for the distance the finger travels to make sure the call to open the drawer is not too sensitive.

Bayreuth answered 25/7, 2013 at 9:37 Comment(3)
please show a sample code of how to achieve it. do note that i don't wish to just open the drawer. i wish to move it like on the apps i've mentioned, and if the user stop touching, it decides if it should scrll back or scroll to show the drawer.Alfano
This functionality is different to the functionality of the DrawerLayout. Is there a particular reason for not wanting to use the Drawer Layout? You are going to make life a lot more difficult for yourself trying to implement your own custom drawer. For some information regarding how to approach this take a look at this series of posts from Cyril Mottier on how he implemented the drawer in Prixing. cyrilmottier.com/2012/05/22/…Bayreuth
i didn't say i don't wish to use it. in fact, i asked how to use it AND have this functionality of choosing where to start the dragging.Alfano
A
1

When we override the default edge side our screen of that portion on which it overlaps stops working, I have used the above code in viewpager but now when i scroll to the left from right instead of coming next view page it remains constant. What will be the solution for it? I have used setDrawerLeftEdgeSize(this,mDrawerLayout,distance );

public static void setDrawerLeftEdgeSize(Activity activity,
            DrawerLayout drawerLayout, float displayWidthPercentage) {
        if (activity == null || drawerLayout == null)
            return;

        try {
            // find ViewDragHelper and set it accessible
            Field leftDraggerField = drawerLayout.getClass().getDeclaredField(
                    "mLeftDragger");
            leftDraggerField.setAccessible(true);
            ViewDragHelper leftDragger = (ViewDragHelper) leftDraggerField
                    .get(drawerLayout);
            // find edgesize and set is accessible
            Field edgeSizeField = leftDragger.getClass().getDeclaredField(
                    "mEdgeSize");
            edgeSizeField.setAccessible(true);
            int edgeSize = edgeSizeField.getInt(leftDragger);
            // set new edgesize
            Point displaySize = new Point();
            activity.getWindowManager().getDefaultDisplay()
                    .getSize(displaySize);
            edgeSizeField.setInt(leftDragger, Math.max(edgeSize,
                    (int) (displaySize.x * displayWidthPercentage)));
        } catch (NoSuchFieldException e) {
            // ignore
        } catch (IllegalArgumentException e) {
            // ignore
        } catch (IllegalAccessException e) {
            // ignore
        }
    }

same as above..

Ackack answered 6/1, 2015 at 8:5 Comment(0)
D
0

Using this project SlidingMenu you can achieve that effect and integrate with ActionBarSherlock.

To slide the menu from left to right just customize the menu when creating it and add:

slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);

Hope this helps.

Drollery answered 17/7, 2013 at 12:49 Comment(3)
i don't understand. where can i set here the area size that will allow to drag the DrawerLayout ? also, i think it's a totally different library.Alfano
The area is the margin of the screen, just swipe near the edge or from outside of screen and it works like a charm. It is indeed another library, I have been using this one since before google has introduced the DrawerLayout and it is very good. You said your library is still not quite finished, so I only provided you with an alternative.Drollery
i want to customize it, for example like on current and youtube.Alfano

© 2022 - 2024 — McMap. All rights reserved.