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 )
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.
Vous trouverez ci-dessous un moyen simple d'obtenir le résultat souhaité.
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) {
}
};
É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) {
}
});
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:
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!
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. :)
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();
}
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();
}