DrawerLayout item click - Когда подходящее время для замены фрагмента?

Я разрабатываю приложение, которое использует шаблон ящика навигации (с DrawerLayout).

Каждый щелчок по элементу ящика заменяет фрагмент в основном контейнере.

Однако, я не уверен, когда настало время сделать транзакцию фрагмента? Когда ящик начинает закрываться? Или после его закрытия?

В google documentaion example вы можете видеть, что они делают транзакцию сразу после щелчка элемента, а затем закройте ящик.
В результате ящик выглядит лагги, а не гладкий, и он выглядит очень плохо (это случается и в моем приложении).

В Gmail и Google Диске, с другой стороны, похоже, что они делают транзакция после закрытия ящика (я прав?).
В результате ящик не лагги и очень плавный, НО требуется, по крайней мере, 1 секунда (время, необходимое для закрытия ящика), чтобы увидеть следующий фрагмент.

Похоже, что ящик не будет гладким, если вы сразу сделаете транзакцию фрагмента.

Что вы думаете об этом?

Спасибо заранее!

Ответ 1

Yup, не мог согласиться больше, выполнение транзакции с фрагментом (с представлением) приводит к переходу макета, который вызывает анимированные анимации анимации, цитируя DrawerLayout docs:

DrawerLayout.DrawerListener может использоваться для контроля состояния и движения представлений ящика. Избегайте выполнения дорогостоящих операций, таких как макет во время анимации, поскольку это может вызвать заикание; попробуйте выполнить дорогостоящие операции в состоянии STATE_IDLE.

Итак, пожалуйста, выполните транзакции фрагмента после закрытия ящика, или кто-то исправляет библиотеку поддержки, чтобы как-то исправить это:)

Ответ 2

Еще одно решение - создать Handler и отправить задержку Runnable после закрытия ящика, как показано ниже: fooobar.com/questions/108627/.... Преимущество такого подхода заключается в том, что ваши фрагменты будут заменены гораздо раньше, чем они были бы, если бы вы ждали DrawerListener#onDrawerClosed(), но, конечно, произвольная задержка не гарантирует 100% анимацию ящика со временем.

Тем не менее, я использую задержку в 200 мс и прекрасно работает.

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

Это то, что я делаю, чтобы добиться плавной анимации транзакций, похожей на приложение 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;
         }
     }
 }

Надеюсь, что тебе поможет!:-)

Ответ 4

Я знаю, что этот вопрос старый, но я столкнулся с одной и той же проблемой и решил, что я опубликую свое решение, поскольку я думаю, что это более эффективная реализация, чем добавление времени жесткой задержки. Я использовал функцию 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);
    }
}

Ответ 5

Просто напишите свой код в обработчике и поставьте задержку 200 мс.

 new Handler().postDelayed(new Runnable() {
  @Override
   public void run() {
       openSelectionDrawerItem(position);          
   }
 }, 200);

Ответ 6

Вместо того, чтобы задерживать клики, которые могут привести к замедлению вашего приложения. Я просто задержу закрытие mDrawerLayout. Я бы не использовал DrawerLayout.OnDrawerListener onClose(...) либо потому, что эти обратные вызовы так медленно вызываются.

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        mDrawerLayout.closeDrawer(GravityCompat.START);
    }
}, 200);

Ответ 7

Если вы хотите, чтобы он был гладким и без каких-либо задержек, оставьте ящик открытым и закройте его после возвращения (в методе onRestart()).

@Override
protected void onRestart() {
    // TODO Auto-generated method stub
    super.onRestart();
    mDrawerLayout.closeDrawer(mDrawerList);     
}

Побочный эффект - это (быстрая) анимация при возврате, но это может быть приемлемо.