web-dev-qa-db-fra.com

Comment changer FloatingActionButton entre les onglets?

J'essaie d'implémenter FloatingActionButton _ À partir de bibliothèque de prise en charge de Google Design dans deux des trois onglets et conformément au Consignes de conception de matériel. - FloatingActionButton Il est écrit: 

S'il y a un bouton d'action flottant sur plusieurs écrans latéraux (tels que Sur des onglets), lors de la saisie de chaque écran, le bouton devrait afficher et masquer si l'action contenue sur chacun est différente. Si l'action est De même, le bouton doit rester à l'écran (et traduire si nécessaire une nouvelle position )

Example of FAB animation in tabs

Comment puis-je effectuer ce type de transition ou d'animation pour les boutons FAB de mon application?

Cette fonctionnalité n’est pas actuellement intégrée à FloatingActionButton, vous devez donc l’animer vous-même. En supposant que votre FloatingActionButton soit dans votre activité principale, ajoutez la fonction suivante à votre activité.

int[] colorIntArray = {R.color.walking,R.color.running,R.color.biking,R.color.paddling,R.color.golfing};
int[] iconIntArray = {R.drawable.ic_walk_white,R.drawable.ic_run_white,R.drawable.ic_bike_white,R.drawable.ic_add_white,R.drawable.ic_arrow_back_white};

protected void animateFab(final int position) {
    fab.clearAnimation();
    // Scale down animation
    ScaleAnimation shrink =  new ScaleAnimation(1f, 0.2f, 1f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    shrink.setDuration(150);     // animation duration in milliseconds
    shrink.setInterpolator(new DecelerateInterpolator());
    shrink.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            // Change FAB color and icon
            fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position]));
            fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));

            // Scale up animation
            ScaleAnimation expand =  new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            expand.setDuration(100);     // animation duration in milliseconds
            expand.setInterpolator(new AccelerateInterpolator());
            fab.startAnimation(expand);
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });
    fab.startAnimation(shrink);
}

Mettez à jour la couleur et les ressources disponibles pour correspondre à votre projet. Ajoutez un écouteur de sélection d'onglets dans votre méthode onCreate et appelez la fonction animate lorsqu'un onglet est sélectionné.

tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        mViewPager.setCurrentItem(tab.getPosition());
        animateFab(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {

    }
});

Assurez-vous d'avoir suffisamment de couleurs et d'icônes pour correspondre au nombre d'onglets que vous avez.

36
blackcj

Vous trouverez ci-dessous un moyen simple d'obtenir le résultat souhaité.

enter image description here

Ajoutez deux (ou l'équivalent à vos actions sur les onglets) FloatingActionButton dans votre activité principale, comme ci-dessous

<Android.support.design.widget.AppBarLayout
    Android:id="@+id/appbar"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:paddingTop="@dimen/appbar_padding_top"
    Android:theme="@style/AppTheme.AppBarOverlay">

    <Android.support.v7.widget.Toolbar
        Android:id="@+id/toolbar"
        Android:layout_width="match_parent"
        Android:layout_height="?attr/actionBarSize"
        Android:background="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/AppTheme.PopupOverlay">

    </Android.support.v7.widget.Toolbar>

    <Android.support.design.widget.TabLayout
        Android:id="@+id/tabs"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content" />

</Android.support.design.widget.AppBarLayout>

<Android.support.v4.view.ViewPager
    Android:id="@+id/container"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

<Android.support.design.widget.FloatingActionButton
    Android:id="@+id/fabChat"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="end|bottom"
    Android:layout_margin="@dimen/fab_margin"
    Android:src="@drawable/ic_fab_chat" />

<Android.support.design.widget.FloatingActionButton
    Android:id="@+id/fabPerson"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="end|bottom"
    Android:layout_margin="@dimen/fab_margin"
    Android:src="@drawable/ic_fab_person"
    Android:visibility="gone" />

Maintenant, dans votre MainActivity.Java, utilisez les fonctions par défaut de Fab pour masquer et afficher sur chaque sélection d'onglets, comme ci-dessous

private void animateFab(int position) {
    switch (position) {
        case 0:
            fabChat.show();
            fabPerson.hide();
            break;
        case 1:
            fabPerson.show();
            fabChat.hide();
            break;

        default:
            fabChat.show();
            fabPerson.hide();
            break;
    }
}

Appelez la fonction animateFab comme ci-dessous

TabLayout.OnTabSelectedListener onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        animateFab(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {

    }
};

ViewPager.OnPageChangeListener onPageChangeListener = new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        animateFab(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
};
21
Nauman Zubair

Étendre la réponse de blackcj, la solution a fonctionné vraiment bien, comme expliqué. Cependant, je voudrais ajouter quelque chose à cela. 

J'ai regardé cette vidéo au ralenti. Les tirables et les fabuleux s'animent différemment. Lors de la dissimulation, fab et drawable sont synchronisés. Tout en montrant, fab revient en premier, et après 60 à 70% de réalisation, l'animation de départ dessinable à partir de 0 et la rotation et la mise à l'échelle atteignant leur taille maximale.

Cependant, je n'ai pas pu réaliser d'animation saperatly tirable. Mais, j'ai réussi à faire pivoter et à mettre à l'échelle avec différents Interpolators et le temps légèrement modifié. Cela ressemble donc davantage à la vidéo qui est également présentée dans les directives de conception de Google. 

int[] colorIntArray = {R.color.red,R.color.gray,R.color.black};
int[] iconIntArray = {R.drawable.ic_btn1, R.drawable.ic_btn2, R.drawable.ic_btn3};

    protected void animateFab(final int position) {
        fab.clearAnimation();

        // Scale down animation
        ScaleAnimation shrink = new ScaleAnimation(1f, 0.1f, 1f, 0.1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        shrink.setDuration(100);     // animation duration in milliseconds
        shrink.setInterpolator(new AccelerateInterpolator());
        shrink.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // Change FAB color and icon
                fab.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), colorIntArray[position]));
                fab.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), iconIntArray[position]));

                // Rotate Animation
                Animation rotate = new RotateAnimation(60.0f, 0.0f,
                        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                        0.5f);
                rotate.setDuration(150);
                rotate.setInterpolator(new DecelerateInterpolator());

                // Scale up animation
                ScaleAnimation expand = new ScaleAnimation(0.1f, 1f, 0.1f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                expand.setDuration(150);     // animation duration in milliseconds
                expand.setInterpolator(new DecelerateInterpolator());

                // Add both animations to animation state
                AnimationSet s = new AnimationSet(false); //false means don't share interpolators
                s.addAnimation(rotate);
                s.addAnimation(expand);
                fab.startAnimation(s);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        fab.startAnimation(shrink);
    }

Et l'onglet tab change d'auditeur comme d'habitude:

tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        mViewPager.setCurrentItem(tab.getPosition());
        animateFab(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {

    }
});
3
kirtan403

Enfin, j'ai trouvé une solution assez simple et affiche une animation qui est exactement comme le gif attaché - dans les solutions de @ Nauman ou @ Omid, l'animation de spectacle commence avant la fin de l'animation de masquage. Mais veillez à utiliser la plus récente bibliothèque de support! Je l'ai testé avec la version 23.2.1.

Cas d'utilisation:

  • Montrer la fab 1 sur l'onglet 1 (index 0)
  • Montrer la fab 2 sur l'onglet 2 (index 1)
  • Ne montrez aucune fab sur l'onglet 3 (index 2)

Dans votre fichier xml, placez les fabricants avec un identifiant unique et une visibilité définie sur invisible:

<Android.support.design.widget.FloatingActionButton
    Android:id="@+id/fab1"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="bottom|end"
    Android:layout_margin="16dp"
    Android:src="@drawable/some_icon"
    Android:visibility="invisible" />

<Android.support.design.widget.FloatingActionButton
    Android:id="@+id/fab2"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="bottom|end"
    Android:layout_margin="16dp"
    Android:src="@drawable/another_icon"
    Android:visibility="invisible" />

Ajoutez ensuite deux champs pour vos fabs à votre activité (vous pouvez également utiliser des variables locales ou obtenir la vue à chaque fois par findViewById(...)):

public class MainActivity extends AppCompatActivity {
    private FloatingActionButton fab1;
    private FloatingActionButton fab2;

Dans votre fonction onCreate(...), recherchez ces vues et enregistrez-les dans les champs déclarés:

fab1 = (FloatingActionButton) findViewById(R.id.fab1);
fab2 = (FloatingActionButton) findViewById(R.id.fab2);

Ensuite, déclarez une fonction qui montre la bonne fab pour la position donnée. Le cas par défaut (onglet 3 ou plus) est assez simple: appelez simplement la méthode hide() sur les fabs. show() et hide() implémentent déjà une animation de dimensionnement. Mais si nous cachons fab2 sur l'onglet 1, nous devons attendre la fin de l'opération avant de pouvoir afficher fab1. Définissez donc un FloatingActionButton.OnVisibilityChangedListener comme paramètre pour la méthode hide(...) et affichez la nouvelle fab désirée dans la méthode onHidden(...) de cet écouteur. Le résultat est le suivant:

public void showRightFab(int tab) {
    switch (tab) {
        case 0:
            fab2.hide(new FloatingActionButton.OnVisibilityChangedListener() {
                @Override
                public void onHidden(FloatingActionButton fab) {
                    fab1.show();
                }
            });
            break;

        case 1:
            fab1.hide(new FloatingActionButton.OnVisibilityChangedListener() [
                @Override
                public void onHidden(FloatingActionButton fab) {
                    fab2.show();
                }
            });
            break;

        default:
            fab1.hide();
            fab2.hide();
            break;
    }
}

C'était la partie la plus difficile! Ajoutez maintenant un écouteur à ViewPager pour appeler la fonction showRightFab(...) chaque fois que l'onglet sélectionné est modifié.

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageSelected(int position) {
        showRightFab(position);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}

    @Override
    public void onPageScrollStateChanged(int state) {}
});

Enfin, appelez la fonction une fois manuellement dans la méthode onCreate(...) pour afficher la fabrique à l’onglet par défaut, car la méthode onPageSelected(...) de ViewPager.OnPageChangeListener n’est pas appelée au démarrage (par exemple, si vous ouvrez l’application et affiche l’onglet 1, aucune fab s'affiche car la fonction showRightFab(...) n'a jamais été appelée).

showRightFab(viewPager.getCurrentItem());

Ce travail est parfaitement dans mon application!

2
Felix Edelmann

En développant les réponses de blackcj & kirtan403, j'ai également ajouté la possibilité de masquer fab sur un onglet choisi (dans ce cas le premier onglet), question de bernzkie sous la réponse de blackcj.

Pour ce faire, j'ai d'abord déclaré int[] avec 3 éléments, chacun pour le fab de chacun des 3 onglets. Je règle le premier élément de chaque élément sur 0 car il s'agira de l'élément invisible fab du premier onglet du premier onglet, qui n'a pas besoin de ressources.

int[] colorIntArray = {0, R.color.fab_red, R.color.fab_green};
int[] iconIntArray = {0, R.drawable.fab_pencil, R.drawable.fab_chevron};

J'ai ensuite défini une instruction if dans la méthode onCreate de Activity, qui comporte le fab et des onglets. Cette déclaration masque l’usine de fabrication et la réduit, de sorte que, si elle est rendue visible à nouveau, elle puisse uniquement être agrandie, et non inutilement, puis à nouveau. Je règle l'échelle pour qu'elle corresponde à l'échelle finale de l'animation de blackcj.

    if (tabLayout.getSelectedTabPosition() == 0) {
        // if on the 1st tab
        fab.hide();
        // scale down to only scale up when switching from 1st tab
        fab.setScaleX(0.2f);
        fab.setScaleY(0.2f);
    }

Ensuite, en dehors de la méthode onCreate, j'ai ajouté la méthode animateFab de blackcj, avec la modification rotate de kirtan403. Cependant, j'ai modifié la méthode animateFab pour avoir également une instruction conditionnelle, où:

  • s'il retourne au premier onglet, la variable fab est masquée (elle est automatiquement réduite lorsque vous la masquez);

  • lorsque vous passez d'un onglet où la fab est déjà pleine taille et visible à un autre onglet où elle est censée être visible, il effectue l'animation complète à l'échelle réduite, modifiée et redimensionnée;

  • lors du changement de de l'onglet avec le fab caché (dans ce cas, le 1er onglet), le fab est rendu visible, puis redimensionné (pas réduit, puis redimensionné) avec l'animation personnalisée.

    protected void animateFab(final int position) {
        fab.clearAnimation();
        if (tabLayout.getSelectedTabPosition() == 0) {
            // if on the 1st tab
            fab.hide();
        } else if (fab.getScaleX() == 1f) {
            // if the fab is full scale
            // Scale down animation
            ScaleAnimation shrink =  new ScaleAnimation(1f, 0.2f, 1f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            shrink.setDuration(150);     // animation duration in milliseconds
            shrink.setInterpolator(new DecelerateInterpolator());
            shrink.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
    
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    // Change FAB color and icon
                    fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position]));
                    fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));
    
                    // Scale up animation
                    ScaleAnimation expand = new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                    expand.setDuration(100);     // animation duration in milliseconds
                    expand.setInterpolator(new AccelerateInterpolator());
    
                    // Rotate Animation
                    Animation rotate = new RotateAnimation(60.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                    rotate.setDuration(200);
                    rotate.setInterpolator(new DecelerateInterpolator());
    
                    // Add both animations to animation state
                    AnimationSet animationSet = new AnimationSet(false); //false means don't share interpolators
                    animationSet.addAnimation(expand);
                    animationSet.addAnimation(rotate);
                    fab.startAnimation(animationSet);
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
            fab.startAnimation(shrink);
        } else {
            // if the fab is already scaled down
            // Change FAB color and icon
            fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position]));
            fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));
            fab.show();
    
            // Scale up animation
            ScaleAnimation expand = new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            expand.setDuration(100);     // animation duration in milliseconds
            expand.setInterpolator(new AccelerateInterpolator());
    
            // Rotate Animation
            Animation rotate = new RotateAnimation(60.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            rotate.setDuration(200);
            rotate.setInterpolator(new DecelerateInterpolator());
    
            // Add both animations to animation state
            AnimationSet animationSet = new AnimationSet(false); //false means don't share interpolators
            animationSet.addAnimation(expand);
            animationSet.addAnimation(rotate);
            fab.startAnimation(animationSet);
        }
    
    }
    

Ensuite, je viens d'ajouter l'écouteur de sélection d'onglets inchangé de blackcj à la méthode onCreate.

    tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            viewPager.setCurrentItem(tab.getPosition());
            animateFab(tab.getPosition());
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });

J'espère que cela aide, cela fonctionne certainement parfaitement pour moi. Merci à Blackcj & Kirtan403. :)

2
inferKNOX

vous pouvez ajouter un écouteur à viewpager et afficher et masquer la fab en fonction de son état lorsque vous commencez à faire défiler le viewpager, il s'agit de l'ordre des états

par exemple:

viewPager.addOnPageChangeListener(this);
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if(state==ViewPager.SCROLL_STATE_IDLE)
            fab.show();
        else if(state==ViewPager.SCROLL_STATE_DRAGGING)
            fab.hide();

    }
1
Omid Aminiva

C'est ce qui a fonctionné pour moi:

private boolean isFloatingActionButtonHidden = false;
private int[] colorIntArray = {R.color.walking,R.color.running,R.color.biking,R.color.paddling,R.color.golfing};
private int[] iconIntArray = {R.drawable.ic_walk_white,R.drawable.ic_run_white,R.drawable.ic_bike_white,R.drawable.ic_add_white,R.drawable.ic_arrow_back_white};

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        switch (state) {
            case ViewPager.SCROLL_STATE_SETTLING:
                // This is triggered just before the view pager reaches the final state
                // if you want to trigger the animation after the page reaches its final position
                // just move this to "case ViewPager.SCROLL_STATE_IDLE:"
                showFloatingActionButton(viewPager.getCurrentItem());
                break;
            case ViewPager.SCROLL_STATE_IDLE:
                // This is only triggered if user pulls to the left of the start or right of the end
                if (isFloatingActionButtonHidden) {
                    showFloatingActionButton(viewPager.getCurrentItem());
                }
                break;
            default:
                // in all other cases just hide the fab if it is not visable
                if (!isFloatingActionButtonHidden) {
                    hideFloatingActionButton();
                }
        }
   }
});

private void showFloatingActionButton(int position) {
    fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        floatingActionButton.setBackgroundTintList(getResources().getColorStateList(iconIntArray[position], getTheme()));
    } else {
        floatingActionButton.setBackgroundTintList(getResources().getColorStateList(iconIntArray[position]));
    }

    floatingActionButton.show();
}

private void hideFloatingActionButton() {
    isFloatingActionButtonHidden = true;
    floatingActionButton.hide();
}
0
Wilko