I am implementing the new DrawerLayout in a new project. My code is almost verbatim from the DrawerLayout guide and works find until I change orientation. What happens on orientation change is that any View set via findViewById is returning null. In the below code Both mDrawerList and mDrawerLayout are null on orientation change, but not when Activity is first opened.
I check for null value to prevent the NullPointerException but I receive another warning in LogCat:
05-22 20:56:08.375: W/PhoneWindow(16528): Previously focused view reported id 2130968626 during save, but can't be found during restore.
The Activity now loads but the DrawerLayout will no longer slide out from the left as before. What's wrong here?
This is the method that my onCreate calls.
public void initializeDrawer() {
// Get list of menu items from array resource
mDrawerItems = getResources().getStringArray(R.array.nav_drawer_items);
if (mDrawerList == null)
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// Set the adapter for the listview
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.drawer_list_item, mDrawerItems));
// Set the lists click listener
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// Set up the action bar toggle listener to get the draw moving
if (mDrawerLayout == null)
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer, R.string.drawer_open,
R.string.drawer_close) {
/** Called when a drawer has settled in a completely closed state. */
public void onDrawerClosed(View view) {
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to
// onPrepareOptionsMenu()
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View drawerView) {
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // creates call to
// onPrepareOptionsMenu()
}
};
// Attach the listener
mDrawerLayout.setDrawerListener(mDrawerToggle);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
It sounds strange to me that initializeDrawer() gets called twice.
If the method is in the super class then it should be called via onCreate only there. Why does the subclass call initializeDrawer again (it should only call super() which in turn calls initializeDrawer).
Your problem might be caused because the listeners get messed up when you call initializeDrawer twice.
try adding the following to your Activity:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
Related
Introduction
I have an activity (let's call BaseActivity) that initializes the drawer layout in onPostCreate
public void initDrawerLayout(){
setSupportActionBar(getYellowToolbar());
getSupportActionBar().setDisplayShowTitleEnabled(false);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerLayout.setScrimColor(getResources().getColor(R.color.blue_menu));
// Toggle
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
getYellowToolbar(), R.string.drawer_open, R.string.drawer_close) {
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
}
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
EventBus.getDefault().post(new UiHelper());
}
};
mDrawerLayout.addDrawerListener(mDrawerToggle);
findViewById(R.id.menu_back).setOnClickListener(new
View.OnClickListener() {
#Override
public void onClick(View view) {
mDrawerLayout.closeDrawer(Gravity.LEFT);
}
});
mExpandableListView = (ExpandableListView) findViewById(R.id.listmenu);
mExpandableListView.setOnGroupClickListener(this);
mExpandableMenuAdapter = getMenuAdapter();
mExpandableListView.setAdapter(mExpandableMenuAdapter);
mDrawerToggle.syncState();
}
Then I have a specific fragment working under an Activity which extends BaseActity.
This fragment adds to the top toolbar (yellowtoolbar) at onResume time an additional button inside R.menu.scanner_menu.
Toolbar toolbar = getYellowToolbar();
if (toolbar == null)
return;
toolbar.getMenu().clear();
toolbar.inflateMenu(R.menu.scanner_menu);
/**
* Add click listener on scan
*/
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
.
.
return false;
}
});
Situation
BaseActivity onPostCreate init drawer layout
ChildrenActivity extends BaseActivity
Fragment inflates new button onResume
Problem
When I reach this fragment from another fragment belonging to the same activity the inflate of the additional button is successful and I can click it with no problem.
While if I starts the application (so the onPostCreate of the BaseActivity is called) having the fragment to be shown at first so both the initDrawerLayout from BaseActivity and the inflate of the fragment are called almost at the same time the inflate of the additional button is unsuccessful and I cannot see it.
Additional info
I have tried to add a generic button in the fragment and clicking on it inflates one more time the menu button in the top toolbar and it correctly works. I think the problem seems to be the toolbar rendering by the drawer layout initialization that is still not completed the moment the fragment inflates the new button, so the toolbar just skip the new button and goes on with the BaseActivity.
In my application, I'm using a Navigation Drawer. I have given each item in the Navigation Drawer a different Icon for opening the Nav Drawer.
When I initially start the app, the drawer icon for the first fragment animates like normal. But when I click on another Nav Drawer Item, the animations break.
In my MainActivity, I have this code for toggling the nav drawer:
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ab_mytasks, R.string.drawer_open,
R.string.drawer_close) {
public void onDrawerClosed(View view) {
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to
// onPrepareOptionsMenu()
}
public void onDrawerOpened(View drawerView) {
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // creates call to
// onPrepareOptionsMenu()
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
mDrawerToggle.syncState();
Then in each of my fragments, I have this code for setting the custom icon:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final ActionBar actionBar = getActivity().getActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setHomeAsUpIndicator(R.drawable.ab_mytasks);
}
I have tried putting in the same mDrawerToggle method as in my MainActivity into my fragments. But the app crashes when I use getActivity().invalidateOptionsMenu().
Here is an image representation of my problem:
1 = Animation works as normal
2 = Selected another fragment from Nav Drawer
3 = Original Fragment icon animation is broken
When you change the icon (setHomeAsUpIndicator) you will no longer get an animation.
/** Called when drawer is closed */
public void onDrawerClosed(View view)
{
getActionBar().setTitle(mTitle);
invalidateOptionsMenu();
}
/** Called when a drawer is opened */
public void onDrawerOpened(View drawerView)
{
getActionBar().setTitle("Select a river");
invalidateOptionsMenu();
}
};
// Setting DrawerToggle on DrawerLayout
mDrawerLayout.setDrawerListener(mDrawerToggle);
// Creating an ArrayAdapter to add items to the listview mDrawerList
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
getBaseContext(),
R.layout.drawer_list_item ,
getResources().getStringArray(R.array.rivers)
);
// Setting the adapter on mDrawerList
mDrawerList.setAdapter(adapter);
// Enabling Home button
getActionBar().setHomeButtonEnabled(true);
// Enabling Up navigation
getActionBar().setDisplayHomeAsUpEnabled(true);
Try to use getSupportActionBar() instead of getActionBar().
Fragment does not have method getActionBar().
A fragment is usually used as part of an activity's user interface and contributes its own layout to the activity. To provide a layout for a fragment, you must implement the onCreateView() callback method, which the Android system calls when it's time for the fragment to draw its layout.
So, fragment requires its activity in which it is present.
So, try getActivity().getActionBar();
I'm trying to add navigation drawer to my FragmentList.
I set custom layout with frame container, all works nice, but there is no drawer toggle icon near app icon.
I think problem is that i do not override onPostCreate in my fragment, because, in a simple, i dont have it in fragment. But in on PostCreate i need to call toggle.SyncState.
How i can solve my problem?
Below my toggle implementation:
mDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout, R.drawable.ic_drawer,
R.string.app_name, // nav drawer open - description for
// accessibility
R.string.hello_world // nav drawer close - description for
// accessibility
) {
public void onDrawerClosed(View view) {
getActivity().getActionBar().setTitle("Assigned");
// calling onPrepareOptionsMenu() to show action bar icons
getActivity().invalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
getActivity().getActionBar().setTitle("Main");
// calling onPrepareOptionsMenu() to hide action bar icons
getActivity().invalidateOptionsMenu();
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
getActivity().getActionBar().setHomeButtonEnabled(true);
getActivity().getActionBar().setDisplayShowHomeEnabled(true);
getActivity().getActionBar().setDisplayUseLogoEnabled(true);
getActivity().getActionBar().setDisplayShowTitleEnabled(true);
getActivity().getActionBar().setDisplayShowCustomEnabled(false);
Simply call it in onActivityCreated method. This method is called in fragment just before onPostCreate and after onCreate methods in Activity.
onActivityCreated(Bundle) tells the fragment that its activity has
completed its own Activity.onCreate().
public class DrawerFragment extends Fragment {
private ActionBarDrawerToggle drawerToggle;
...
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
drawerToggle.syncState();
}
}
I am following this post http://derekrwoods.com/2013/09/creating-a-static-navigation-drawer-in-android/
I want drawer to be opened when in landscape mode. Here is my onCreate function.
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_order);
drawerList = (ListView) findViewById(R.id.left_drawer);
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
FrameLayout frameLayout = (FrameLayout) findViewById(R.id.content_frame);
if (((ViewGroup.MarginLayoutParams) frameLayout.getLayoutParams()).leftMargin == (int) getResources()
.getDimension(R.dimen.drawer_size)) {
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
drawerLayout.setScrimColor(Color.TRANSPARENT);
isDrawerLocked = true;
}
// Set the adapter for the list view
// drawerItems = getResources().getStringArray(R.array.drawerOptions);
drawerItems = DummyContent.ITEMS
.toArray(new DummyItem[DummyContent.ITEMS.size()]);
drawerList.setAdapter(new ArrayAdapter<DummyItem>(this,
android.R.layout.simple_list_item_1, drawerItems));
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
R.drawable.ic_drawer, R.string.action_short,
R.string.action_short) {
/** Called when a drawer has settled in a completely closed state. */
public void onDrawerClosed(View view) {
getActionBar().setTitle("test");
// ((FragmentInterface) fragment).showMenuActions();
invalidateOptionsMenu();
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View drawerView) {
getActionBar().setTitle("Select Option");
// ((FragmentInterface) fragment).hideMenuActions();
invalidateOptionsMenu();
}
};
if (!isDrawerLocked) {
drawerLayout.setDrawerListener(drawerToggle);
}
// Set the drawer toggle as the DrawerListener
DrawerItemClickListener drawerItemClickListener = new DrawerItemClickListener();
drawerList.setOnItemClickListener(drawerItemClickListener);
if (!isDrawerLocked) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
Now the problem is whenever my activity is starting in portrait mode, after rotation drawer is collapsible (can be closed using sliding right to left gesture) even after setting DrawerLayout.LOCK_MODE_LOCKED_OPEN. This problem does not occur when opened directly in Landscape mode.
I had to move that code to my onResume():
#Override
public void onResume() {
super.onResume();
Display display = getWindowManager().getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_90 || display.getRotation() == Surface.ROTATION_270) {
// Landscape
isDrawerLocked = true;
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
} else {
// Portrait
isDrawerLocked = false;
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
}
You should handle configuration changes yourself.
Use the onConfigurationChanged callback method to always know which orientation you're in. Make sure to include the android:configurationChanges="orientation" tag in your manifest. See this section of the user guide for a quick description of handling configuration changes.
I think the root of the issue is that Android is handling life cycle methods slightly differently than you're expecting. Using the explicit onConfigurationChanged method will remove any ambiguity.