I am using a android.support.v4.widget.DrawerLayout in my activity.
As long as I use the swipe gesture to open it, it works fine.
However, when I want to open it through a button click by calling drawer.openDrawer(Gravity.LEFT), it does not work.
But, if I open it once using the swipe gesture, after that it works normally with button click.
Any idea how I could solve or work around this?
I had the same issue and I've just found out that for some reason the FrameLayout that represents the drawer have the visibility set to "gone", that probably goes to "visible" during the first slideGesture.
So, open your layout xml file, locate your FrameLayout that represents the drawer and simply erase the visibility setting. My opening tag is now as follows:
<FrameLayout
android:layout_width="305dp"
android:layout_height="match_parent"
android:layout_gravity="start">
That worked for me.
Cheers
In my case the visibility on 'NavigationView' was set to gone in the layout. Changing it to visible solved the issue
If you want to open it from the Top Left Toggle you should implement onOptionsItemSelected(MenuItem item)
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// The action bar home/up action should open or close the drawer.
// ActionBarDrawerToggle will take care of this.
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
case android.R.id.home:
return true;
}
return super.onOptionsItemSelected(item);
}
Please use clearFocus() method for DrawerLayout object.
Encountered the same problem and was able to fix it by specifying a width for the drawer content element inside the layout.
Without layout_width attribute the drawer did not open on the first openDrawer() call,
with the value it does.
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<FrameLayout
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="right"
android:background="#ffffff"
android:id="#+id/drawer_content">
</FrameLayout>
Too stupid, i did set an empty layout,
problem was just as described above, when the drawer was manually dragged the first time, after that, the button worked, but without dragging it first, the navigation drawer did never open.
Don't set an empty layout.
Removing my EmptyLayout from the ListView which represented the NavigationDrawerContent made it work perfect again.
DONT
drawerContentListView.setEmptyView(getLayoutInflater().inflate(R.layout.navigation_drawer_empty_layout, null));
**** costed me more than an hour.
You can put this code inside your DrawerLayout:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
Select your NavigationView > Set visibility properties to visible
Related
I started a project with a Navigation Drawer from the basic template of Android Studio. The only modification I made was to display it as permanent in order to have a tablet/TV layout.
To achieve this, the only modification I made was in the xml layout. This allow the NavigationView to be always visible.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Content will come here" />
</LinearLayout>
</LinearLayout>
I also put the project on Github, so anyone can test it.
PROJECT DEMO ON GITHUB
https://github.com/ChristopheVersieux/NavFocus
WHAT IS HAPPENING
My issue comes when I start selecting items on the drawer with the D-pad.
Once an item is selected, the focus is completely lost. Trying to get back to the Drawer and get focus seems very hard and I have to try several times with right/left arrows
WHAT IS EXPECTED:
Drawer should keep focus, or focus should be easy to bring back to the Drawer.
WHAT I TRIED:
The simplest Idea I had was to force the Drawer to get focus again, but this code doesn't change anything:
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
menuItem.setChecked(true);
//This is where I will replace the Fragments in the right area.
navigationView.clearFocus();
navigationView.requestFocus();
return true;
}
});
Thank a lot for your help.
I would start by removing android:layout_gravity="start"
This is simply not needed as its parent is a horizontal LinearLayout.
The Navigation Drawer must be permanently visible on Tablets and TV. They stay hidden for mobile. These are part of the Material Design guidelines.
This requires quite a different setup compared to what I see in your project on GitHub. Which includes supplying different resources using qualifiers.
This tutorial on Navigation Drawer (Design Support) will help you with exactly that setup, as per the latest Material Design guidelines. Alternatively the project files for the tutorial can be found on GitHub.
UPDATE:
As pointed out, Support library v24 creates issues with the dpad. Reverting to v23 works just fine.
So, I have an activity with navigation view. By click on its item I change fragment in activity. All fragment have the same toolbar. But one have this toolbar and TabLayout to it. I would like to know what is better to declare toolbar once on activity like this
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar" />
</RelativeLayout>
or declare it in each fragment.
The disadvantage of the first method is default toolbar shadow. When I add tabs in fragment, shadow looks like
When I tried 2 solution. All my toolbar was with back icon instead drawer animated logo.
Thanks.
I had the exact same problem. This is how I solved it:
Move the toolbars to the fragments like you suggested (so you won't have a shadow separating the two). This allows for a way more flexible way to implement (different) toolbars in your layouts too.
Replace the Toolbar's navigation icon by a custom one like this:
toolbar.setNavigationIcon(R.drawable.ic_action_menu);
(I used the Android Asset Studio to easily create an icon with the preferred color)
Now open the NavigationView with the new menu(home) icon. You can do this through the MainActivity (the one with the NavigationView). Create a public method in that Activity that opens the drawer:
public void openDrawer(){
mDrawerLayout.openDrawer(Gravity.LEFT);
}
Now call this method in the OnOptionsItemSelected in your fragments like this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// handle item selection
switch (item.getItemId()) {
case android.R.id.home: //Menu icon
((MainActivity)getActivity()).openDrawer();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
That's it. Of course the downside is that you must implement the Toolbar in each Fragment. However, this is the only way (that I know of) that enables you to have the Toolbar (+TabLayout) in a Fragment and still be able to control your NavigationView.
You can use AppBarLayout from design support library like:
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
...
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
...
/>
</android.support.design.widget.AppBarLayout>
and then you can change visibility of tablayout.
For more information about desing layout library : link
My main activity is a Swipe View with tabs in the action bar, which can be used to change to a particular fragment directly. I basically followed the developer guidelines on this. So far this works fine and as expected.
However, I now have a couple of items in the menu (Settings, About), which should not be displayed as part of the ViewPager, but rather should replace the ViewPager completely and set the "navigation up" affordance in the action bar. Following along with the answers to this question I know how to use the BackStack to manipulate the action bar and to show the "navigation up" affordance.
However I'm not sure what the best way to replace the ViewPager would be. As far as I know I can either try to disable all ViewPager functionality and make it appear as it would be a single fragment (e.g. disable tabs and swipe), or I could use nested fragments. Yet, I'm not convinced that either of this options is "clean".
Maybe I'm overlooking something here and there is a more intuitive way to achieve the same? What are you guys thinking about this and how do you implement something "basic" as this?
P.S.: Obviously I could use activities for this, but I think that an Activity is too heavy for a simple "About" text and in my understanding one should try to use Fragments wherever possible these days.
As I can understand, you could put the ViewPager inside a parent as FrameLayout and add() the "about" fragment with addToBackState() method above the ViewPager.
You will avoid to disable or refresh the ViewPager. Just add above it a new fragment.
UPDATE
I'm able to achieve it with add() method and a custom background on the added fragment to avoid the overlap issues. And finally make this background clickable to prevent the click events for the behind ViewPager.
See my activity layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.viewpageroverlap.MainActivity"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
My Menu item event:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, OverlapFragment.newInstance(990), null).addToBackStack(null).commit();
return true;
}
return super.onOptionsItemSelected(item);
}
My Overlap Fragment layout:
<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="com.example.viewpageroverlap.MainActivity$OverlapFragment"
android:background="#FF0000"
android:clickable="true" >
<TextView
android:id="#+id/section_label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" />
</RelativeLayout>
This gives me this output:
Note: I used a red background but you can try with Android Resources Color and avoid to use a color declared in your files as android:background="#android:color/white".
WITH TABS
You can do the same as above and reset the navigation with NAVIGATION_MODE_STANDARD:
if (id == R.id.action_settings) {
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, OverlapFragment.newInstance(990), null).addToBackStack(null).commit();
return true;
}
Then, when the user come back to the ViewPager (when he presses the home button or hardware back button), reset the old navigation as:
// do the same with android.R.id.home inside onOptionsItemSelected
#Override
public void onBackPressed() {
// check if the "about" fragment is still displayed
if(this.getSupportFragmentManager().getBackStackEntryCount() > 0) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
this.getSupportFragmentManager().popBackStack();
}
}
Just have the ViewPager in its own fragment and replace that when you want to change to another fragment using regular fragment transactions.
If you turn on addToBackStack those transactions will react to the back button in a natrual way.
I've been trying to move my code across to the DrawerLayout as suggested by android here as SlidingDrawer is deprecated.
My problem is that so far DrawerLayout seems to be either very badly implemented, has unhelpful error messages (no defensive programming) and/or isn't explained well enough in the documentation.
the isDrawerOpen() method is described here:
public boolean isDrawerOpen (View drawer)
Check if the given drawer view is currently in an open state. To be
considered "open" the drawer must have settled into its fully visible
state. To check for partial visibility use
isDrawerVisible(android.view.View).
Parameters: drawer - Drawer view to check
Returns: true if the given drawer view is in an open state
Each of the methods isDrawerOpen(View drawer), openDrawer(View drawer) and closeDrawer(View drawer) don't work when passed: The DrawerLayout in question or either of it's children. I have no idea what I'm supposed to feed into these methods to allow them to function. Can someone let me know?
See below for entire problem description with implementation.
I have a layout like so:
<android.support.v4.widget.DrawerLayout
android:id="#+id/mainmenuPanel"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/dualPane"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/menuPane"
android:layout_width="300dp"
android:layout_height="match_parent" />
</android.support.v4.widget.DrawerLayout>
And in my code I have the following method hooked up to a button:
protected void onCreate(Bundle savedInstanceState) {
...
mMenuPanel = (DrawerLayout) findViewById(R.id.mainmenuPanel);
....
}
public boolean isDrawerOpen() {
if(mMenuPanel != null) {
return mMenuPanel.isDrawerOpen(mMenuPanel);
}
return false;
}
However if you give it itself as an argument (which would be redundant in the extreme) you get the following error:
E/AndroidRuntime(11241): java.lang.ClassCastException:
android.widget.FrameLayout$LayoutParams cannot be cast to
android.support.v4.widget.DrawerLayout$LayoutParams
(Which as you'll notice, doesn't give you any information about what you did wrong. It is instead a symtom of the problem).
Other answers here or here are either incomprehensible or very closely tied to the question without much explanation. Even so I've tried adding a faux LinearLayout or one the DrawerLayouts children and each give this error:
E/AndroidRuntime(11424): java.lang.IllegalArgumentException:
View android.widget.FrameLayout{420f5ea8 V.E..... ........
0,0-800,1172 #7f060038 app:id/menuPane} is not a drawer
Can anyone explain what these methods actually need to have passed to them to work?
And, the answer:
The second child (aka, the "Drawer") is what needs to be passed to the methods. My problem was that by the time I had figured that out I'd reduced the layout to as simple as possible implementation to test - I'd removed the gravity from the "drawer". Without a gravity, you get the above completely unrelated error messages.
I can confirm I got the code to work using the following setup:
mMenuPanel.isDrawerOpen(findViewById(R.id.drawer));
and with the layout:
<android.support.v4.widget.DrawerLayout
android:id="#+id/mainmenuPanel"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/drawer"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"/> <!-- This line was the problem!!!!!!!-->
</android.support.v4.widget.DrawerLayout>
if you want to avoid references to views or layouts in order to call isDrawerOpen, another way can be applied.
In this case you have to indicate the Gravity:
mDrawerLayout.isDrawerOpen(Gravity.START);
Being mDrawerLayout a reference to your DrawerLayout.
as you said, don't forget android:layout_gravity="start"
In your Activity if you have a reference to the DrawerLayout and in this case the FrameLayout with the id R.id.menuPane you can also do...
DrawerLayout mMenuPanel = (DrawerLayout) findViewById(R.id.mainmenuPanel);
FrameLayout mMenuPane = (FrameLayout) findViewById(R.id.menuPane);
mMenuPanel.isDrawerOpen(mMenuPane);
Basically you have to pass in a reference to the view being used as the drawer within the DrawerLayout, which will always be mMenuPanel.getChildAt(1) anyways.
If you are using the drawer layout from the support library as we can see from your question that you are, there is also the method:
closeDrawers()
This will close all open drawers (which is usually just the one).
I'm using a ListView as a secondary view in my SlidingPaneLayout.The main view is a map fragment. The ListView acts as a menu. The problem is that onItemClickedListener never gets called on the ListView. Even the list row never gets highlighted on press. it seems that the ListView can't get the focus.
EDIT:
actually, slidingPaneLayout.findFocus() shows that android.widget.ListView. still no luck on clicking the list items.
Here is my xml
<com.ziz.luke.custom_components.MySlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/slidingpanelayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#+id/contactsList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#00000000" >
</ListView>
<TextView
android:id="#+id/android:empty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal|center_vertical"
android:text="#string/noContacts" />
</RelativeLayout>
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
</com.ziz.luke.custom_components.MySlidingPaneLayout>
How can I solve this??
I've found the answer. I was using a subclass of SlidingPaneLayout in which I was overriding
onInterceptTouchEvent(MotionEvent arg0)
I was trying to do the following:
open the slidingPaneLayout useing a button.
close the slidingPaneLayout useing a button.
close the slidingPaneLayout useing swiping.
prevent the user from opening the slidingPaneLayout using swiping.
So, I created a boolean inside my subclass called shouldSwipe to be returned from the over-ridden method.
the implementation that caused the problem was :
#Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
return shouldSwipe;
}
it caused the problem whenever (shouldSwipe = true) because it tells the system that the touch event already is consumed and prevents it from being propagated.
I solved that using this one:
#Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
// TODO Auto-generated method stub
return shouldSwipe ?super.onInterceptTouchEvent(arg0):shouldSwipe;
}
that's it.
Just a suggestion but maybe using the NavigationDrawer for a Navigation List Drawer would be easier then reinventing the wheel.
http://developer.android.com/design/patterns/navigation-drawer.html
I have just created an example project using SlidingPaneLayout.
I didn't use any map because there is not where the problem is, so I just refer the position of the map with a TextView. I did use a ListFragment that is working and receiving the click listeners. Please download the project from here:
https://dl.dropboxusercontent.com/u/33565803/StackOverFlowExamples/SlidingPaneLayoutExample.zip
Let me know if you have any configuration problem and if it solves your problem ;)
(I am using actionBarSherlock just because I am used to, so you can remove it if you want)
Try using this powerful library to make a simple sliding bar :
https://github.com/jfeinstein10/SlidingMenu
and here is how you can import it and use it :
Solved: how to import SlidingMenu on my project with ActionBarSherlock 4.2.0