可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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!
回答1:
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 :)
回答2:
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);
}
}
回答3:
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! :-)
回答4:
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);
}
}
回答5:
Just write your code in a handler and put 200 ms delay.
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
openSelectionDrawerItem(position);
}
}, 200);
回答6:
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);
回答7:
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.