web-dev-qa-db-fra.com

Clic sur l'élément de DrawerLayout - Quel est le bon moment pour remplacer le fragment?

Je développe une application qui utilise le modèle de tiroir de navigation (With DrawerLayout).

Chaque clic sur l'élément d'un tiroir remplace le fragment dans le conteneur principal.

Cependant, je ne sais pas quel est le bon moment pour effectuer la transaction de fragment? Quand le tiroir commence à se fermer? Ou après sa fermeture?

Dans google's exemple de documentation , vous pouvez voir qu'ils effectuent la transaction juste après le clic sur l'élément, puis fermez le tiroir.
En conséquence, le tiroir semble lag et pas lisse, et il a l'air très mauvais (cela arrive aussi dans mon application).

Dans les applications Gmail et Google Drive , en revanche, il semble qu'ils effectuent la transaction après la fermeture du tiroir (Ai-je raison?).
En conséquence, le tiroir n'est pas décalé et très lisse, MAIS il faut environ 1 seconde (le temps qu'il faut pour que le tiroir se ferme) au moins, pour voir le fragment suivant.

Il semble qu'il n'y ait aucun moyen que le tiroir soit fluide lors d'une transaction de fragment immédiate.

Qu'est ce que tu penses de ça?

Merci d'avance!

46
dor506

Oui, je ne pourrais pas être plus d'accord, effectuer une transaction de fragment (avec vue) entraîne une passe de mise en page qui provoque des animations saccadées sur les vues en cours d'animation, citant DrawerLayoutdocs :

DrawerLayout.DrawerListener peut être utilisé pour surveiller l'état et le mouvement des vues de tiroir. Évitez d'effectuer des opérations coûteuses telles que la mise en page pendant l'animation car cela peut provoquer un bégaiement; essayez d'effectuer des opérations coûteuses pendant l'état STATE_IDLE.

Veuillez donc effectuer vos transactions de fragments après la fermeture du tiroir ou que quelqu'un corrige la bibliothèque de support pour résoudre ce problème :)

24
eveliotc

Une autre solution consiste à créer un Handler et à publier un Runnable différé après avoir fermé le tiroir, comme indiqué ici: https://stackoverflow.com/a/18483633/769501 . L'avantage de cette approche est que vos fragments seront remplacés beaucoup plus tôt qu'ils ne le seraient si vous attendiez DrawerListener#onDrawerClosed(), mais bien sûr, le retard arbitraire ne garantit pas à 100% que l'animation du tiroir sera terminée à temps .

Cela dit, j'utilise un retard de 200 ms et cela fonctionne à merveille.

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);
    }
}
25
Makario

Voici ce que je fais pour réaliser une animation de transaction fluide similaire à l'application 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;
         }
     }
 }

J'espère que cela vous aide! :-)

15
saguinav

Je sais que cette question est ancienne mais j'ai rencontré le même problème et j'ai pensé publier ma solution car je pense que c'est une meilleure mise en œuvre que l'ajout d'un temps de retard codé en dur. Ce que j'ai fait était d'utiliser la fonction onDrawerClosed pour vérifier que le tiroir IS fermé avant de faire ma tâche.

//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
}

puis dans onDrawerClosed, ouvrez l'activité correspondante.

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
user1282637

Écrivez simplement votre code dans un gestionnaire et mettez 200 ms de retard.

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

Au lieu de retarder les clics sur votre article, votre application peut se sentir lente. Je retarderais simplement la fermeture de mDrawerLayout. Je n'utiliserais pas non plus la DrawerLayout.OnDrawerListener onClose(...) car ces rappels sont si lents à être appelés.

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

Si vous voulez qu'il soit lisse et sans délai, laissez le tiroir ouvert et fermez-le après lors du retour (dans la méthode onRestart ()).

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

L'effet secondaire est une animation (rapide) lors du retour, mais cela peut être acceptable.

0
Nesreteip