我正在开发一个应用程序,使用导航抽屉模式(带有DrawerLayout)。
每次点击抽屉的项目,都会替换主容器中的片段。
然而,我不确定何时是正确的时间进行片段事务? 当抽屉开始关闭时?还是在关闭后?
在谷歌的 documentaion example 中,您可以看到他们在项目点击后立即进行事务处理,然后关闭抽屉。
结果,抽屉看起来很卡顿,不流畅,效果很差(我的应用程序也出现了这种情况)。
我正在开发一个应用程序,使用导航抽屉模式(带有DrawerLayout)。
每次点击抽屉的项目,都会替换主容器中的片段。
然而,我不确定何时是正确的时间进行片段事务? 当抽屉开始关闭时?还是在关闭后?
在谷歌的 documentaion example 中,您可以看到他们在项目点击后立即进行事务处理,然后关闭抽屉。
结果,抽屉看起来很卡顿,不流畅,效果很差(我的应用程序也出现了这种情况)。
没错,完全同意。执行带有视图的片段(fragment)事务会导致布局传递,从而导致动画卡顿,参考DrawerLayout
文档:
可以使用DrawerLayout.DrawerListener来监视抽屉视图的状态和运动。在动画期间避免执行昂贵的操作,比如布局,因为它可能会导致卡顿; 尝试在STATE_IDLE状态下执行昂贵的操作。
所以,请在抽屉关闭后执行您的片段事务,或者有人修补支持库以某种方式修复它 :)
Handler
,在关闭抽屉后延迟发布Runnable
,如此所示:https://dev59.com/3mMl5IYBdhLWcg3wgnOl#18483633。这种方法的好处是,如果等待DrawerListener#onDrawerClosed()
,你的片段将被更快地替换,但当然任意的延迟并不能保证抽屉动画会及时完成。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);
}
}
以下是我在实现类似 Gmail 应用中平滑交易动画时所采取的方法:
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;
}
}
}
希望这对你有所帮助!:-)
我知道这个问题已经很久了,但是我遇到了同样的问题,并且想要发布我的解决方案,因为我认为它比添加硬编码延迟时间更好。我的做法是使用onDrawerClosed
函数来验证抽屉确实关闭了,然后再执行我的任务。
//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
}
onDrawerClosed
中打开对应的活动。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);
}
}
只需在处理程序中编写代码并延迟200毫秒即可。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
openSelectionDrawerItem(position);
}
}, 200);
不要延迟你的项目点击,这可能会使你的应用程序感觉很慢。我只会延迟mDrawerLayout的关闭。我也不会使用DrawerLayout.OnDrawerListener onClose(...)
,因为这些回调被调用得太慢了。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mDrawerLayout.closeDrawer(GravityCompat.START);
}
}, 200);
如果你希望它平稳无延迟,那么在返回时将抽屉打开并在 onRestart() 方法中关闭它。
@Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
mDrawerLayout.closeDrawer(mDrawerList);
}
副作用是返回时会有一个(快速的)动画,但这可能是可以接受的。
onItemClick()
中启动新活动时,我看到了卡顿的抽屉关闭动画。这可能是因为许多活动都在UI线程上进行。所以现在我必须从DrawerLayout.DrawerListener
中启动一个新的活动...就像你提到的那样,这会减慢用户体验。 - Someone SomewhereDrawerLayout
也可以在没有动画的情况下关闭:closeDrawer(View drawerView, boolean animate)
。该方法会立即关闭抽屉。 - Ahmed