Как изменить FloatingActionButton между вкладками?
Я пытаюсь реализовать FloatingActionButton от Библиотека Поддержки Дизайна Google в двух из трех вкладок, и в соответствии с Руководство По Проектированию Материалов-FloatingActionButton он говорит:
Если есть плавающая кнопка действия на нескольких боковых экранов (таких как на вкладках), при входе в каждый экран, кнопка должна показывать и скрыть, если действие, содержащееся в каждом отличающийся. Если действие то же самое, кнопка должна оставаться на экране (и перевести на новый положение, если необходимо.)
Как я могу сделать такой переход или анимацию для кнопок FAB в моем приложении?
7 ответов
эта функция в настоящее время не встроена в FloatingActionButton, поэтому вам придется анимировать ее самостоятельно. Предполагая, что FloatingActionButton находится в вашей основной деятельности, добавьте следующую функцию в свою деятельность.
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);
}
обновите цвет и привлекаемые ресурсы в соответствии с вашим проектом. Добавьте прослушиватель выбора вкладки в метод onCreate и вызовите функцию animate при выборе вкладки.
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) {
}
});
убедитесь, что у вас есть достаточно цветов и значков сопоставьте количество вкладок, которые у вас есть.
Ниже приведен простой способ достичь желаемого результата
добавить два (или эквивалент действий вкладки)FloatingActionButton
в вашей основной деятельности, как показано ниже
<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" />
теперь В класса MainActivity.java использует функции Fab по умолчанию, чтобы скрыть и показать на каждом выборе вкладки, как показано ниже
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;
}
}
вызов animateFab
функции как ниже
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) {
}
};
расширение ответа blackcj, решение работало действительно хорошо, как объяснено. Однако я хотел бы кое-что добавить.
Я смотрел это видео в замедленном режиме. Drawable и fab анимируются по-разному. При скрытии fab и drawable синхронизируются. Во время показа fab возвращается первым, а после 60-70 процентов завершения рисоваемой анимации запуска от 0 и поворота и масштабирования до полного размера.
однако, я не смог добиться drawable анимация saperatly. Но мне удалось повернуть и масштабировать с помощью разных Интерполяторов и слегка измененного времени. Так что это больше похоже на видео, которое также представлено в рекомендации по разработке 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);
}
и tab tab изменить прослушиватель, как обычно:
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) {
}
});
наконец, я нашел решение, которое довольно легко и показывает анимацию, которая точно такая же, как прикрепленные gif – в решениях oder @Omid @Nauman, анимация шоу начинается до того, как анимация hide закончилась. Но обязательно используйте новейшую библиотеку поддержки! Я протестировал его с версией 23.2.1.
использовать случае:
- показать fab 1 на вкладке 1 (индекс 0)
- показать fab 2 на вкладке 2 (индекс 1)
- не показывать fab на вкладке 3 (индекс 2)
в вашем xml, место для fabs с уникальным идентификатором и видимостью, установленной на невидимый:
<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" />
затем добавьте два поля для своих fabs в свою деятельность (вы также можете использовать локальные переменные или получать представление каждый раз по findViewById(...)
):
public class MainActivity extends AppCompatActivity {
private FloatingActionButton fab1;
private FloatingActionButton fab2;
в своем onCreate(...)
функция, найдите эти представления и сохраните их в объявленных полях:
fab1 = (FloatingActionButton) findViewById(R.id.fab1);
fab2 = (FloatingActionButton) findViewById(R.id.fab2);
далее объявите функцию, которая показывает правильный fab для данной позиции. Случай по умолчанию (вкладка 3 или более) довольно просто: просто вызовите hide()
метод на fabs. show()
и hide()
уже реализована анимация масштабирования. Но если мы спрячем fab2 на вкладке 1, мы должны подождать, пока он не закончится, прежде чем мы сможем показать fab1. Поэтому установите FloatingActionButton.OnVisibilityChangedListener
в качестве параметра hide(...)
метод и показать желаемый новый fab в onHidden(...)
метод этого слушателя. Результат таков:
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;
}
}
это была самая сложная часть! Теперь добавьте прослушиватель в ViewPager для вызова showRightFab(...)
функция при каждом изменении выбранной вкладки.
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) {}
});
наконец, вызвать функцию один раз вручную в onCreate(...)
метод для отображения fab на вкладке по умолчанию, потому что ViewPager.OnPageChangeListener
' s onPageSelected(...)
метод не вызывается при запуске (e. г. в противном случае, если вы откроете приложение, и оно покажет вкладку 1, fab не будет отображаться, потому что
расширяя ответы blackcj и kirtan403, я также добавил возможность скрыть fab
на выбранной вкладке (В данном случае на 1-й вкладке), который отвечает на вопрос бернцки под ответом blackcj.
для достижения этого я сначала заявил int[]
С 3-х пунктов, каждый для fab
каждой из 3 вкладок. Я установил первый элемент в каждом 0, потому что это будет невидимая вкладка 1st fab
для этого не нужны ресурсы.
int[] colorIntArray = {0, R.color.fab_red, R.color.fab_green};
int[] iconIntArray = {0, R.drawable.fab_pencil, R.drawable.fab_chevron};
затем я установил if
заявление в onCreate
метод Activity
С fab
и вкладки. Это утверждение скрывает fab и масштабирует его, так что, когда он снова становится видимым, его можно сделать только масштабировать вверх, а не вниз без необходимости, а затем снова вверх. Я установил масштаб в соответствии с финальной шкалой анимации 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);
}
затем за пределами onCreate
метод, я добавил blackcj по animateFab
метод, с kirtan403 в rotate
модификации. Тем не менее, я изменил animateFab
метод также имеет условный оператор, где:
если он возвращается на 1-ю вкладку,
fab
скрыт (он автоматически масштабируется при скрытии);при переключении с вкладки, где fab уже полноразмерный и видимый на другую вкладку, где он должен быть видимым, он выполняет полный масштаб вниз, изменить & масштабировать анимацию;
-
при переходе С вкладка со скрытым
fab
(в данном случае 1-я вкладка),fab
становится видимым, затем масштабируется (не масштабируется, а затем масштабируется) с помощью пользовательской анимации.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); } }
затем я просто добавил прослушиватель выбора вкладки blackcj в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) {
}
});
надеюсь, это поможет, это, безусловно, работает безупречно для меня. Спасибо blackcj & kirtan403. :)
вы можете добавить прослушиватель в viewpager и показать и скрыть fab в соответствии с его состоянием когда вы начинаете прокручивать viewpager, это порядок состояний SCROLL_STATE_DRAGGING SCROLL_STATE_SETTLING SCROLL_STATE_IDLE
например:
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();
}
вот что сработало для меня:
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();
}