I'm trying to implement the navigation drawer pattern based on my app. I downloaded the sample code from here and i ran it and 90 % of the times the drawer works ok, but sometimes the drawer gets stuck when i try to open it. I have a way of replicating the situation but it doesn't always work. What i do is:
1- Run the sample code as is.
2- Put your finger on the left edge to get the drawer peek
3- Let go of the finger and press it on the main fragment
4- Try to open the drawer as usual
Sometimes the drawer gets stuck on the peek mode no matter how much you swipe your finger to the right to open the drawer more. Has anyone had / fixed this issue?
I faced a similar issue as mentioned by you. I had a list view inside a relative layout (FILL_PARENT). Whenever the content in the list view is less and when I dragged in the area outside the list view, the navigation drawer got struck. Setting android:clickable="true" for the relative layout resolved the problem. Hope this may help.
To clarify on Viji's answer, if you are using something like the navigation drawer example provided:
<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- As the main content view, the view below consumes the entire
space available using match_parent in both dimensions. -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- android:layout_gravity="start" tells DrawerLayout to treat
this as a sliding drawer on the left side for left-to-right
languages and on the right side for right-to-left languages.
The drawer is given a fixed width in dp and extends the full height of
the container. A solid background is used for contrast
with the content view. -->
<ListView
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#111"/>
</android.support.v4.widget.DrawerLayout>
Adding android:clickable="true" to the FrameLayout seems to fix the problem.
Two soluctions.
setting android:clickable
copy the source code of drawerlayout and delete peekDrawer function to disabled this feature
Let me explain why the reason of this bug.
DrawerLayout three states
STATE_IDLE, STATE_DRAGGING, STATE_SETTLING
ACTION_DOWN-> onEdgeTouched will be triggered if it is on the edge, DrawerLayout will trigger peekDrawer after 120ms
PeekDrawer actually changes the state of DrawerLayout to STATE_SETTLING, and then lets the Drawer scroll to the specified location. Then set the state to IDLE.
ACTION_MOVE->
If the current state is DRAGGING, drag captureView
If the current state is not DRAGGING, it will try to execute tryCaptureViewForDrag to reset the state to DRAGGING.
And at the same time, it will also determine whether a new edge gesture is triggered (emphasis !!)
If a new edge gesture was dete, it will invoke onEdgeDragStared
and DrawerLayout will go to captureView to captrue the drawer
How the recuurent this bug?
First tap the edge to invoke a 120ms delay function peekDrawer
Before the peekDrawer was invoked, trigger onEdgeDragStared let the drawer layout capture the drawer, the dragState will be seted to STATE_DRAGGING
After 120ms, If your finger are still in the area that drawerLayout want to peek to , the dragState will be set to SETTLING
Before the drawer peek to the destination, move your finger fast out of that area before the peek end, the drawer will not follow your finger because the state is SETTLING, after SETTLING the state will be set to IDLE
Now your finger is totally out of the drawer and the state is IDLE , so you can’t drag the view anymore
So, the solution is to stop the peekDrawer
actually, the drawerlayout has fixed this problem.
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (action) {
case MotionEvent.ACTION_MOVE: {
// If we cross the touch slop, don't perform the delayed peek for an edge touch.
if (mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL)) {
mLeftCallback.removeCallbacks();
mRightCallback.removeCallbacks();
}
break;
}
}
return interceptForDrag || interceptForTap || hasPeekingDrawer() || mChildrenCanceledTouch;
}
If the child is clickable, the child will consume the event, the onInterceptTouchEvent will invoke many times. and remove the peekDraw while move.
Related
I have a bottom sheet with its height and width set to match_parent. So when on button click I set the behavior to STATE_EXPANDED like this:
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
My Bottomsheet is defined as below:
<FrameLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:elevation="#dimen/design_appbar_elevation"
app:behavior_hideable="true"
app:layout_behavior="#string/bottom_sheet_behavior">
<include
android:id="#+id/bottom_sheet_content"
layout="#layout/bottomsheet_layout" />
</FrameLayout>
I am monitoring states with the BottomSheet Callbacks.
I click on a button and bottom sheet expanded to full screen.
Its current State is STATE_EXPANDED
I quickly swipe down on the bottom sheet. (Not fully drag till it closed, simple swipe down like scrolling)
It stops at the middle and its state is logged as STATE_COLLAPSED
If I swipe again it is all gone and its state is STATE_HIDDEN
I don't understand why it stops in the middle. How can I make it hidden with a single swipe.
I tried that by setting peek_height to 0dp. By this, it never encounters the STATE_HIDDEN. When hidden, its state becomes STATE_COLLAPSED. I just don't understand this states.
How to achieve STATE_HIDDEN with a single swipe down?
Kinda late but I just stumbled upon this while searching for something similar.
This is how you can skip the collapsed state:
In XML by adding app:behavior_skipCollapsed="true" to the BottomSheet view.
OR
Programmatically with setSkipCollapsed(boolean).
I have implemented a SwipeRefreshLayout on my ListView, and it is working, but not how I want it to.
First of all, the refresh triggers before I lift my finger, which doesn't feel right because it moves the content back to the top even though my finger is still sitting in swiped down position. Also the distance to trigger is really short and setDistanceToTriggerSync from the docs is not available to me for some reason.
second, I'd like some sort of view the be displayed in the gap when my list view is pulled down, like a text that tells the user "swipe down to refresh" or a an animation (like the dancing ghost in snap chat). How do I set this View
Here's what I have
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dashboardRootLayout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/lvDashboard"
android:background="#ffffff"
/>
Why isn't the setDistanceToTriggerSync available?
Anyway the default behavior of the SwipeRefreshLayout does not have that extra view, it just has a special progress bar and a listener for refreshing and showing or not the progress of refresh. Also it can change the look of the actionbar with the refresh message.
Possible solution:
If you want that special view, of the top of my head I can think of having the list being moved down with animation and creating or putting VISIBLE the view you want to show on the top of the SwipeRefreshLayout.
SwipeRefreshLayout swipeRefreshLayout=(SwipeRefreshLayout)findViewById(R.id.dashboardRootLayout);
swipeRefreshLayout.setOnRefreshListener(new OnRefreshListener()
{
#Override
public void onRefresh()
{
// do animation
// set the view you want to show VISIBLE
}
});
You could actually implement all of this by yourself without the SwipeRefreshLayout since you don't want the default behavior.
}
I need to do a Sliding Up View wich should slide up from the bottom of the screen when I click a button. It has to show on the bottom of the screen and I need to slide/drag it to the center of the screen. The images below explain it better.
almost like the AndroidSlidingUpPanel from "umano" which you can find here:
The problem is that I want the first child (The content of my View - an image for example) to fill all the screen and also I want the second child(the actual bottom bar) to be showed when I click a button. The images below explain it better. If there is not possible to do this by changing the AndroidSlidingUpPanel, how can I do that? I have never worked with views like this. I would really appreciate any tip or help. Thank you very much.
To hide or show panel, you can use
showPanel()
method.
To hide it try this:
SlidingUpPanelLayout slidingPanel = (SlidingUpPanelLayout) findViewById(R.id.sliding_panel);
slidingPanel.hidePanel();
To make it appe
SlidingUpPanelLayout slidingPanel = (SlidingUpPanelLayout) findViewById(R.id.sliding_panel);
slidingPanel.showPanel();
This is available only in v 2.0 of AndroidSlidingUpPanel (https://github.com/umano/AndroidSlidingUpPanel). As I know, it's included in android support library v13 now, but not sure if there is latest version.
You can check this library for dragging content from all four edges of the screen https://github.com/SimonVT/android-menudrawer
You can make a custom layout inside this menu drawer to get your expected result.
You can do it with AndroidSlidingUpPanel, just set visibility:
android:visibility="GONE"
on the 2° child of the view (the panel) and use .showPane() and .hidePane() on SlidingUpPanelLayout to show/hide the panel when you click the button.
The following library do it as well
https://github.com/Paroca72/sc-widgets
Inside you will find a widget named ScSlidingPanel.
This widget work different from the other and can be use and customize very easily.
You put it inside a RelativeLayout give an alignment and it will open from that side.. Left, Right, Top and Bottom or mixed..
In your specific case your must align your panel at bottom of the container and it will sliding from the bottom.
<!-- Define the container -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- Sliding from top -->
<scapps.com.library.ScSlidingPanel
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<!-- HERE THE YOUR CONTENT -->
<!-- or you can load by setLayout method -->
</scapps.com.library.ScSlidingPanel>
</RelativeLayout>
Another important property that you can use right for your case is the handle size.
You can define an handle and define the beavior of it.. as your image above you used a button.. you can unsing an image and setting setToggleOnTouch() to true for open/close the panel touching on handle.
I had implement DrawerLayout in my application. Now if suppose I click on screen then the DrawerLayout is getting close. But now I want to implement some operations on onTouch.
My question is how can I prevent the DrawerLayout to close onTouch. I want to implement this like suppose I dismiss the onTouch event of DrawerLayout.
If any suggestion please share with me.
Thanks in advance.
Its not actually a proper solution but is a workaround increase the drawer width so that it covers majority of the screen.Doing this your drawer will get most of the touch events and therefore wont close on touch.
<ListView
android:id="#+id/drawerContentList"
style="#style/Theme.AppCompat.Light.DarkActionBar"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#111"
android:choiceMode="singleChoice" >
</ListView>
increase android:layout_width to say around 320dp
I have successfully implemented a NavigationDrawer for my application.
My app displays a drawer that opens on the left of the screen.
My problem is I need to add a button on the left. That button might be clicked or swiped to open the left drawer. That I can do.
But the button is supposed to look like it's a part of the drawer that would overflow into the screen.
That means the button should slide simultaneously as the drawer opens and closes.
CLOSED STATE :
OPENING STATE
I tried adding the button into the left drawer's layout, but it seems you can't make stuff appear outside of its boundaries, and the drawer will always get completely hidden when you close it.
Now I'm trying adding it to add a button to the main DrawerLayout and make it align to the right of the left drawer... But no luck... It looks like a DrawerLayout can't have more than two children...
Any help will be appreciated.
I'm using the Support library (v4)
[EDIT]
And I am supporting API level 8... So can't use ImageView.setTranslationX or View.OnAttachStateChangeListener
It's quite tricky.
I think this is similar to what is done to the drawer handle in new Google Maps app. Don't take my word, not sure. :)
I have written toggle that stays on the edge of the Activity content view.
When the DrawerLayout is dragged, I translated the view on the x-axis by the amount of the minimal child (which is DrawerLayout content view) minus the shadow (if any). Since the shadow casted + content view of the DrawerLayout gives the full measured width of the entire drawer.
I quickly multiply the slided offset and the minimal child and find the x translation.
[Edit: Code has been removed for readability and it has been moved to the link provided below]
In your activity:
mDrawerToggle = new DrawerLayoutEdgeToggle(
this,
mDrawerLayout,
R.drawable.ic_launcher,
R.drawable.ic_launcher,
Gravity.LEFT,
true) {
#Override
public void onDrawerClosed(View view) {
super.onDrawerClosed(view); //must call super
}
#Override
public void onDrawerOpened(View view) {
super.onDrawerOpened(view); //must call super
}
#Override
public void onDrawerSlide(View view, float slideOffset) {
super.onDrawerSlide(view, slideOffset); //must call super
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
Creativity boost:
You can add more cosmetics like distance from ActionBar which you can set as margin to the handle.
Also you can mimic "single-zipper effect" by moving the handle up/down along left/right just by translating on the Y axis. :)
Edit: Its available on my GitHub here
Edit 2: For those that can't get the handle appear at the beginning just use mDrawerToggle.setVerticalPostionOffset(0)
I found a way of doing this pretty easily thanks to the Aniqroid library written by Mobistry (SO pseudo)
I use the SlidingTray class which is a copy of android SlidingDrawer but lets you position the drawer to any corner of the screen.
I declared the element via xml
<com.sileria.android.view.SlidingTray
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer"
android:layout_width="match_parent"
android:layout_height="270dp"
android:content="#+id/content"
android:handle="#+id/handle" >
<ImageView
android:id="#+id/handle"
android:layout_width="320dp"
android:layout_height="24dp"
android:background="#drawable/tray_btn" />
<LinearLayout
android:id="#+id/content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="fill_vertical"
android:orientation="vertical" >
</LinearLayout>
</com.sileria.android.view.SlidingTray>
And just had to call
Kit.init(this);
in my main Activity before inflating the layout.
I thank aniqroid devs!
https://code.google.com/p/aniqroid/