DrawerLayout's item click - When is the right time to replace fragment? - android

I'm developing an application which uses the navigation drawer pattern (With DrawerLayout).
Each click on a drawer's item, replaces the fragment in the main container.
However, I'm not sure when is the right time to do the fragment transaction?
When the drawer starts closing? Or after it is closed?
In google's documentaion example, you can see that they are doing the transaction
right after the item click, and then close the drawer.
As a result, the drawer seems laggy and not smooth, and it looks very bad (It happens in my application too).
In Gmail and Google Drive applications, on the other way, It seems like they are doing the transaction after the drawer closed (Am I Right?).
As a result, the drawer is not laggy and very smooth, BUT it takes about 1 second (the time it takes to the drawer get closed) at least, to see the next fragment.
It seems like there is no way the drawer will be smooth when immediately doing fragment transaction.
What do you think about that?
Thanks in advance!

Yup, couldn't agree more, performing a fragment (with view) transaction results in a layout pass which causes janky animations on views being animated, citing DrawerLayout docs:
DrawerLayout.DrawerListener can be used to monitor the state and motion of drawer views. Avoid performing expensive operations such as layout during animation as it can cause stuttering; try to perform expensive operations during the STATE_IDLE state.
So please perform your fragment transactions after the drawer is closed or somebody patches the support library to somehow fix that :)

Another solution is to create a Handler and post a delayed Runnable after you close the drawer, as shown here: https://stackoverflow.com/a/18483633/769501. The benefit with this approach is that your fragments will be replaced much sooner than they would be if you waited for DrawerListener#onDrawerClosed(), but of course the arbitrary delay doesn't 100% guarantee the drawer animation will be finished in time.
That said, I use a 200ms delay and it works wonderfully.
private class DrawerItemClickListener implements OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
drawerLayout.closeDrawer(drawerList);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
switchFragments(position); // your fragment transactions go here
}
}, 200);
}
}

This is what I do to achieve an smooth transaction animation similar to Gmail app:
activity_drawer.xml
<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" >
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- The navigation drawer -->
<ListView
android:id="#+id/left_drawer"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="left"
android:choiceMode="singleChoice" />
</android.support.v4.widget.DrawerLayout>
DrawerActivity.java
private Fragment mContentFragment;
private Fragment mNextContentFragment;
private boolean mChangeContentFragment = false;
private Handler mHandler = new Handler();
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
mDrawerLayout.setDrawerListener(new DrawerListener());
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
...
}
....
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
getSupportFragmentManager().beginTransaction().remove(mContentFragment).commit();
switch (position) {
case 0:
mNextContentFragment = new Fragment1();
break;
case 1:
mNextContentFragment = new Fragment2();
break;
case 2:
mNextContentFragment = new Fragment3();
break;
}
mChangeContentFragment = true;
mDrawerList.setItemChecked(position, true);
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mDrawerLayout.closeDrawer(mDrawerList);
}
}, 150);
}
}
private class DrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
#Override
public void onDrawerClosed(View view) {
if (mChangeContentFragment) {
getSupportFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).replace(R.id.content_frame, mNextContentFragment).commit();
mContentFragment = mNextContentFragment;
mNextContentFragment = null;
mChangeContentFragment = false;
}
}
}
Hope that helps you! :-)

I know this question is old but I ran into the same problem and figured I would post my solution as I think it is a better implementation than adding a hardcoded delay time. What I did was use the onDrawerClosed function to verify that the drawer IS closed before doing my task.
//on button click...
private void displayView(int position) {
switch (position) {
//if item 1 is selected, update a global variable `"int itemPosition"` to be 1
case 1:
itemPosition = 1;
//();
break;
default:
break;
}
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
mDrawerLayout.closeDrawer(mDrawerList); //close drawer
}
and then in onDrawerClosed, open the corresponding activity.
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
// calling onPrepareOptionsMenu() to show action bar icons
supportInvalidateOptionsMenu();
if (itemPosition == 1) {
Intent intent = new Intent(BaseActivity.this, SecondActivity.class);
startActivity(intent);
}
}

Just write your code in a handler and put 200 ms delay.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
openSelectionDrawerItem(position);
}
}, 200);

Instead of delaying your item clicks which may make your app feel slow. I would just delay the closing of the mDrawerLayout. I would not use the DrawerLayout.OnDrawerListener onClose(...) either because those callbacks are so slow to be called.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mDrawerLayout.closeDrawer(GravityCompat.START);
}
}, 200);

If you want it smooth and without any delay, leave the drawer open and close it afterwards when returning (in the onRestart() method).
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
mDrawerLayout.closeDrawer(mDrawerList);
}
The side effect is an (speedy) animation when returning, but this might be acceptable.

Related

Navigation Drawer closing is not smooth for a Fragment

I have implemented a Navigation Drawer for my app that works correctly for the most part.
My Home tab does a couple of API requests in order to display some information, these requests and the workload they require (even if it is rather minimal) block the closing of the Drawer midway, making it not smooth.
My first "solution" was to load the fragment only after the Drawer closes, like such:
toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
displaySelectedScreen(itemSelected.getItemId());
}
};
But this creates a 0.5s wait before the Fragments view gets displayed, and that is not very apealing from a user standpoint.
Here is part of my activity_main.xml:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
This is how I display my Fragments so far:
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(final MenuItem item) {
displaySelectedScreen(item.getItemId());
drawer.closeDrawer(GravityCompat.START);
return true;
}
Here is what happens in displaySelectedScreen:
private void displaySelectedScreen(int itemId) {
Fragment fragment;
fragment = checkFragment(itemId); // Instantiates the right Fragment
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
// Close our Drawer since we have selected a valid item.
drawer.closeDrawer(GravityCompat.START);
}
What could you recommend for my Navigation Drawer to close smoothly?
EDIT:
As requested, here is my code for my AsyncTask that does the API request and returns a JSONObject:
/**
* Requests information from the API via APIRequests
* Extends AsyncTask in order to do network-related actions in background.
*/
private class SearchInfo extends AsyncTask<Void, Integer, JSONObject> {
#Override
protected JSONObject doInBackground(Void... params) {
JSONObject information;
APIRequests apiRequests = new APIRequests();
information = apiRequests.getGameInfo();
requestDone = true;
return information;
}
}
APIRequest's method does a simple HTTP GET via HttpURLConnection and returns a JSONObject with the data retrieved.
It seems you commit Fragment in displaySelectedScreen(), before the drawer closes
#Override
public boolean onNavigationItemSelected(final MenuItem item) {
displaySelectedScreen(item.getItemId()); // Commit is there
drawer.closeDrawer(GravityCompat.START); // And drawer closing simultaneously
return true;
}
What would I do is
// Create a field that marks that something needs to be displayed
// after drawer closes. When null means no action (drawer closed
// by the user, not by selecting an item)
Integer itemIdWhenClosed;
#Override
public boolean onNavigationItemSelected(final MenuItem item) {
itemIdWhenClosed = item.getItemId(); // Mark action when closed
drawer.closeDrawer(GravityCompat.START); // Close it
return true;
}
#Override
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
// If id to show is marked, perform action
if (itemIdWhenClosed != null) {
displaySelectedScreen(itemIdWhenClosed);
// Reset value
itemIdWhenClosed = null;
}
}
// Now here just commit, don't close the drawer since this is already
// fired when it's closed
private void displaySelectedScreen(int itemId) {
final Fragment fragment = checkFragment(itemId);
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
}
Did you try without making API requests, and see if the problem is on the display of the fragment?
Make sure you use another thread to do the API requests in. You can use AsyncTask, Loaders, Services, IntentServices, etc depending on what the specific API request is. That way the Fragment can be displayed immediately and the data can be added to it when it's loaded
The solution I have used is as follows:
Since the data I display won't change often (not on a daily basis at least), I now do my API requests only once per session (process) of my app.
if (!requestDone) { // Where requestDone == true when I have done an API request
getGameInfo();
}
And instantiate the Fragment only when it has not been instantiated (in order to keep requestDone's value).
The animation is now fluid at all times (since I have a splash screen that ends when the API requests are done).
It seems obvious to me now that the number of time data is requested must be equivalent to its probability of being updated.

Tool bar setNavigationOnClickListener breaks ActionbarDrawerToggle functionality

I'm swapping out the action bar for the tool bar, and I nearly have every piece of the puzzle in place. My issue is specifically if I navigate 'up' and restore the navigation drawer, the drawer toggle button no longer works. I figured out if I set the drawer mode to unlocked I have the ability to swipe to open the drawer, but can't click to open the drawer.
So I load fragment A, drawer behaviour is fine, go down to fragment B and apply the up icon, hit up to go back to A, and the drawer won't open with a click any more.
Entering Fragment B:
Toolbar t = mHostingActivity.getToolbar();
mHostingActivity.getDrawerToggle().setDrawerIndicatorEnabled(false);
mHostingActivity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
t.setNavigationIcon(mHostingActivity.getV7DrawerToggleDelegate().getThemeUpIndicator());
t.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popBackStackToTop(mHostingActivity);
}
});
/**
* Pop the back stack and hide the Up caret all the way to the top level of the {#link com.loylap.activities.MainActivity}
*
* #param activity our hosting activity
*/
public static void popBackStackToTop(MainActivity activity) {
if (activity != null) {
FragmentManager fm = activity.getSupportFragmentManager();
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
activity.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
activity.getDrawerToggle().setDrawerIndicatorEnabled(true);
}
}
The navigation drawer is set up just like the sample, maybe the old way of setting up the options is the issue? For example, I still have this in my activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
EDIT:
Okay so I've narrowed down the issue to the setNavigationOnClickListener(). If I don't set this (and go up via back button) - the drawer behaves correctly. So now the question is how do I correctly allow the user to go 'up', and restore the click listener after to we do go up?
So I've figured out I was creating the wrong click listener. Instead of setNavigationOnClickListener(), I need setToolbarNavigationClickListener() :)
A subtle but important change, now the tool bar is behaving in partnership with the v7 ActionBarDrawerToggle
/**
* Create the Up caret for a lower level fragment {#link com.loylap.activities.MainActivity}
*
* #param activity our hosting activity
*/
public static void createUpButton(final MainActivity activity)
{
ActionBarDrawerToggle toggle = activity.getDrawerToggle();
//Disables onClick toggle listener (onClick)
toggle.setDrawerIndicatorEnabled(false);
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popBackStackToTop(activity);
}
});
Toolbar t = activity.getToolbar();
t.setNavigationIcon(activity.getV7DrawerToggleDelegate().getThemeUpIndicator());
activity.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
In my case it was a matter of order, I needed to first set the toolbar and than set the on click listener. in this order:
//works
setSupportActionBar(myToolbar);
myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openDrawer(view);
}
});
rather than this:
//doesn't work
myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openDrawer(view);
}
});
setSupportActionBar(myToolbar);
Inspired by the solution of Daniel Wilson but you only have to do it once and it is all set.
In my NavigationDrawer's setUp() (or you can do it anywhere you are initialising your ActionBarDrawerToggle instance), I write this code:
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!mDrawerToggle.isDrawerIndicatorEnabled()) {
getActivity().onBackPressed();
}
}
});
Now every time android.id.home is pressed and hamburger sign is not shown, the parent activity's onBackPressed() is called.
I think you can't use:
t.setNavigationIcon(mHostingActivity.getV7DrawerToggleDelegate().getThemeUpIndicator());
t.setNavigationOnClickListener(new View.OnClickListener() ...
because it will break your normal navigation drawer behaviour.
Instead try something like this in onCreateOptionsMenu(Menu menu, MenuInflater inflater):
mHostingActivity.getDrawerToggle().setDrawerIndicatorEnabled(false);
mHostingActivity.getDrawerToggle().setHomeAsUpIndicator(mHostingActivity.getV7DrawerToggleDelegate().getThemeUpIndicator());
and then in onOptionsItemSelected
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
popBackStackToTop(mHostingActivity);
return true;
default:
break;
}
return false;
}
PS: don't forget to use setHasOptionsMenu(true); in your fragment onCreateView.
To animate we can use.
ValueAnimator drawerAnimator = ValueAnimator.ofFloat(Constants.HAMBURGER, Constants.BACK);
drawerAnimator.addUpdateListener(drawerAnimationUpdateListener);
drawerAnimator.setDuration(Constants.DRAWER_ANIMATION_DURATION);
drawerAnimator.setInterpolator(new LinearInterpolator());
pass action 0 for HAMBURGER icon and 1 for BACK.
public void updateNavigationDrawer(int action) {
drawerArrowDrawable = actionBarDrawerToggle.getDrawerArrowDrawable();
if (action == Constants.BACK) {
actionBarDrawerToggle.setDrawerIndicatorEnabled(false);
actionBarDrawerToggle.setHomeAsUpIndicator(drawerArrowDrawable);
actionBarDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//onBackPress();
}
});
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED););
} else {
actionBarDrawerToggle.setDrawerIndicatorEnabled(true);
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
if (drawerArrowDrawable.getProgress() != action) {
if (action == Constants.BACK) {
drawerAnimator.start();
} else {
drawerAnimator.reverse();
}
}
}

Support back button in navigation drawer

I'm trying to support back button, while the design on my app is navigation drawer. The problem is the fragments doesn't pushed into backstack, but I solved it:
getFragmentManager().beginTransaction().addToBackStack();
but the problem, is when I'm clicking the back button - the title keep. I solved it too with
getActivity().getActionBar().setTitle(str);
but still, the problem is in the navigation list - on the left side. it doesn't changed appropriate to back button (just stay as it was).
Actually, my question is how can i get backStack top - so than i can easily solve it, or if you have another solution.
Maybe a bit late but still for people who don't know how to it. I think this will solve your problem:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
public void selectItem(int position) {
listView.setItemChecked(position, true);
String[] simple_array = fragment_items.toArray(new String[fragment_items.size()]);
setTitle(simple_array[position]);
}
public void setTitle(String title){
getSupportActionBar().setTitle(title);
}
so when I click on an item in the listview of the navigation drawer, it calls the function selectItem with the parameter position. In the selectItem method I converted my arraylist to a normal array to set my title. The position in your listview is the same as the positions in your array. So simple_array[0] is equal to you first list item. Hope this helps =)
write the line after setContentView()
getActionBar().setDisplayHomeAsUpEnabled(true);
and use the
#Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: {
CommonFunctions.writeLogs(getApplicationContext(), CONFIG, LOGGER,
"onCreateOptionsMenu()" + "home");
finish();
}
break;
}
default:
break;
}
return true;
}
Hope it will solve your problem

Navigation drawer doesn't close

The navigation drawer in my app is not closing. I am using activities instead of fragments. When i click on any item in the listview, it opens other activities as it should but when i go back, the drawer is still open. I have tried using DrawerLayout.closeDrawers(); but it did not work. How do I close the navigation drawer?
Here is my code:
Java
public class MainActivity extends FragmentActivity {
final String[] data ={"Aluminium","Gold","Zinc"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
final DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
final ListView navList = (ListView) findViewById(R.id.left_drawer);
navList.setAdapter(adapter);
navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
switch (pos){
case 0:
Intent i = new Intent(MainActivity.this,Aluminium.class);
startActivity(i);
break;
case 1:
Intent i2 = new Intent(MainActivity.this,Gold.class);
startActivity(i2);
break;
case 2:
Intent i3 = new Intent(MainActivity.this,Zinc.class);
startActivity(i3);
break;
}
}
});
}
}
XML
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:background="#000000"
android:layout_height="match_parent" >
</FrameLayout>
<ListView android:id="#+id/left_drawer"
android:layout_width="220dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="1dp"
android:background="#000000"/>
</android.support.v4.widget.DrawerLayout>
have you tried :
mDrawerLayout.closeDrawer(drawerListView);
You can add this before calling startActivity()
In continuation to others answers and # Chinmay Dabke question of 'but the drawer closes half then pauses and then closes fully' in one of the comments, here is what you could do:
first as others suggested,
this line is missing. drawer.closeDrawer(navList);
And as far as the pausing of drawer is concerned while closing, you could do something like this.
use a Runnable and a Handler like this:
mRunnable = = new Runnable() {
#Override
public void run() {
//say
selectItem(pos); //Implement your switch case logic in this func
}
}
and then in the onDrawerClosed overrided method
#Override
public void onDrawerClosed(View view) {
if (mRunnable != null) {
mHandler.post(mRunnable);
mRunnable = null;
}
}
Hope this helps!
I would suggest you to use fragments for navigation drawer and to solve this issue of drawer not closing properly, I found this article very useful (using fragments). http://www.michenux.net/android-asynctask-in-fragment-best-pratices-725.html
Call
drawer.closeDrawer(navList);
in onItemClick() method
Try
drawer.closeDrawer(Gravity.START);
Your drawer gravity is start so Use that to close the corresponding drawer
I didn't see any code where you are closing the ListView from drawer... close the ListView Drawer on ListItem click...
navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
drawer.closeDrawer(navList);
switch (pos){
case 0:
Intent i = new Intent(MainActivity.this,Aluminium.class);
startActivity(i);
break;
case 1:
Intent i2 = new Intent(MainActivity.this,Gold.class);
startActivity(i2);
break;
case 2:
Intent i3 = new Intent(MainActivity.this,Zinc.class);
startActivity(i3);
break;
}
}
});
You need to close the drawer on list item click
drawer.closeDrawer(navList);
Also what is the use of FrameLayout in your xml. It is not used as a container to add or replace fragments
call the drawer.closeDrawer(navList); function before switch case
use
if(drawer.isDrawerOpen(navList))
{
drawer.closeDrawer(navList);
}
In onResume() and start of onItemClick() method.
or you can try another approach..run a Ui thread when you are selecting drawer item
private void selectDrawerItemItem(final int position){
//Toast.makeText(getApplicationContext(), "ItemClicked", Toast.LENGTH_SHORT).show();
darwer.closeDrawer(navList);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Fragment fragment = new Fragment(Activity.this);
Bundle args = new Bundle();
args.putInt(Fragment.ARG_PLANET_NUMBER, position);
fragment.setArguments(args);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).commit();
// update selected item and title, then close the drawer
navList.setItemChecked(position, true);
setTitle(" " + navListTitles[position]);
}
}, 200);
// update the main content by replacing fragments
}
I was having the same problem.
I used
mDrawerLayout.closeDrawer(drawerListView);
before starting my new activity. It beautifully slides the drawer back in.
private DrawerLayout mDrawerLayout;
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerLayout.closeDrawers();
it works
Here is the code:
Lets say you have the drawer class
in your activity call the class as and make a vaiable and assign it(put the drawer layout within a fragment for smoother user experience)
Drawer drawer;
drawer = (Drawer)getActivity().getSupportFragmentManager().findFragmentById(R.id.theid);
drawer.mDrawerLayout.closeDrawer(Gravity.END);
//End for right and Start for left
Hope it helps

How do organize an app using fragments?

I am currently re-coding most of the back end of my android app in order to follow the design guidelines more closely. Currently I am using all activities and zero fragments. I am trying to switch to fragments in order to use the slide out navigation draw and eventually some sliding tabs.
For navigation right now I have this drop down menu which when an item is clicked launches a new activity:
The "Your Statistics" activity is kind of like the home page, where the user will enter the app too. I also want the user to be able to get back to that "page" from anywhere in the app.
My activity that I plan to run the draw from I have a draw layout called fragment_main:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<FrameLayout
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
<ListView
android:id="#+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FFF"
android:choiceMode="singleChoice"/>
</android.support.v4.widget.DrawerLayout>
and my activity which loads the drawer layout is:
public class MainDraw extends FragmentActivity {
final String[] data ={"one","two","three"};
final String[] fragments ={
"com.beerportfolio.beerportfoliopro.FragmentOne",
"com.beerportfolio.beerportfoliopro.FragmentTwo",
"com.beerportfolio.beerportfoliopro.FragmentThree"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
//todo: load statistics
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActionBar().getThemedContext(), android.R.layout.simple_list_item_1, data);
final DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
final ListView navList = (ListView) findViewById(R.id.drawer);
navList.setAdapter(adapter);
navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
drawer.setDrawerListener( new DrawerLayout.SimpleDrawerListener(){
#Override
public void onDrawerClosed(View drawerView){
super.onDrawerClosed(drawerView);
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this, fragments[pos]));
tx.commit();
}
});
drawer.closeDrawer(navList);
}
});
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main,Fragment.instantiate(MainDraw.this, fragments[0]));
tx.commit();
}
}
IN my //todo: comment I should load my first "home" fragment there which is my statistics "page" ? And then all the other fragments will be transitioned in and out based on the draw clicks?
Thanks for your help in advance, I want to make sure I am doing this right, I used to code just to get things working which is why I am now re doing a huge chunk of my code. Please share any other fragment tips to that I might need!
First of all read the well written documentation, it answers to your doubts.
I would share my personal pattern to convert existing Activity to Fragment
Create your on abstract Fragment class from which derive all drawer fragments, this can help to group common attributes
Use a method like selectItem() on docs, it helps to explicit do a call at first run (showing the "home" fragment) and then from onItemClick
move inflating XML layout from Activity.onCreate() code to Fragment.onCreateView() (ie setContentView to inflater.inflate(R.layout.my_layout, container, false), in many cases you can copy all code from onCreate() to onCreateView
move initialization code from Activity.onCreate() to Fragment.onActivityCreated(), this is very useful when both Activity (including fragment) and the direct Fragment exist, for example if your app exposes a "Share with" action you continue to have the Activity that inside the XML includes a <fragment/> and the fragment can be created from the drawer, too
if you need to communicate from Activity to Fragment and viceversa I suggest to create an interface and store it inside the 'onAttach()' (see google example)
Action bar items must be hidden when drawer is open, again take a look at example used in doc, here is very useful the interface to communicate from activity to fragment, the main activity can tell if drawer is open and the fragment can call the interface
public interface FragmentActivityStatus {
public boolean isDrawerOpen();
}
The activity
public class MainActivity extends Activity implements FragmentActivityStatus {
#Override
public boolean isDrawerOpen() {
return drawerLayout.isDrawerOpen(drawerList);
}
}
The fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
fragmentActivityStatus = (FragmentActivityStatus)activity;
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
boolean isMenuVisible = !fragmentActivityStatus.isDrawerOpen();
menu.findItem(R.id.my_menu).setVisible(isMenuVisible);
super.onPrepareOptionsMenu(menu);
}
Not related to fragment, in your code you declare class names as string, consider to create a Class array if you refactor packages the code continue to work, then you can call the Class.getName() to obtain the string to pass to Fragment.instantiate()
final Class<?>[] fragments = {
FragmentOne.class,
FragmentTwo.class,
FragmentThree.class};
Then
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this,
fragments[pos].getName()));
tx.commit();

Categories

Resources