Как изменить FloatingActionButton между вкладками?
Я пытаюсь реализовать FloatingActionButton
от Библиотеки поддержки дизайна Google на двух из трех вкладок и в соответствии с Руководством по дизайну материалов - FloatingActionButton
он говорит:
Если на нескольких боковых экранах есть кнопка с плавающим действием (такая как на вкладках), после ввода каждого экрана кнопка должна показывать и скрыть, если действие, содержащееся на каждом, отличается. Если действие то же самое, кнопка должна оставаться на экране (и переводить на новый положение, если необходимо.)
![Example of FAB animation in tabs]()
Как я могу сделать такой переход или анимацию для кнопок FAB в моем приложении?
Ответы
Ответ 1
Эта функция в настоящее время не встроена в 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 и вызовите функцию анимации, когда выбрана вкладка.
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) {
}
});
Удостоверьтесь, что у вас достаточно цветов и значков в соответствии с количеством вкладок, которые у вас есть.
Ответ 2
Ниже приведен простой способ достижения желаемого результата.
![enter image description here]()
Добавьте два (или эквивалент ваших действий с вкладками) 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) {
}
};
Ответ 3
Расширение blackcj Ответ. Решение работает очень хорошо, как объяснено. Однако я хотел бы добавить что-то в этом.
Я смотрел это видео в замедленном темпе. Выбираемые и Fab анимации по-разному. При скрытии, fab и drawable синхронизируются. Показывая, fab возвращается первым, и после завершения 60-70 процентов анимация начала анимации от 0 и поворот и масштабирование приходят в полный размер.
Тем не менее, я не смог добиться гибкой анимации. Но мне удалось повернуть и масштабировать с помощью разных Interpolators и немного измененного времени. Так что это похоже на видео, которое также представлено в руководстве по дизайну 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);
}
И вкладка вкладки изменить прослушиватель, как обычно:
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) {
}
});
Ответ 4
Наконец, я нашел решение, которое довольно легко и отображает анимацию, точно такую же, как приложенные gif - в решениях @Nauman oder @Omid, анимация шоу начинается до завершения анимации скрыть. Но обязательно используйте новейшую библиотеку поддержки! Я тестировал его с версией 23.2.1.
Случай использования:
- Показать fab 1 на вкладке 1 (индекс 0)
- Показать fab 2 на вкладке 2 (индекс 1)
- Не показывайте какую-либо фабрику на вкладке 3 (индекс 2)
В вашем xml поместите на фабрики с уникальным идентификатором и видимостью, установленными на невидимые:
<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" />
Затем добавьте два поля для ваших фабрик в свою активность (вы также можете использовать локальные переменные или каждый раз просматривать "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);
Затем объявите функцию, которая показывает правильную фабрику для данной позиции. Случай по умолчанию (вкладка 3 или более) довольно просто: просто вызовите метод hide()
на фабриках. 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
onPageSelected(...)
не вызывается при запуске (например, если вы открываете приложение и он показывает вкладку 1, no fab не будет отображаться, потому что функция showRightFab(...)
никогда не вызывалась).
showRightFab(viewPager.getCurrentItem());
Это отлично работает в моем приложении!
Ответ 5
Расширяя ответы на blackcj и kirtan403, я также добавил возможность скрывать fab
на выбранной вкладке (в данном случае 1-ю вкладку), которые отвечают на бернские вопросы по запросу blackcj.
Чтобы достичь этого, я сначала объявил int[]
тремя предметами, каждый для fab
каждого из трех вкладок. Я устанавливаю первый элемент в каждом из 0, потому что это будет первая вкладка, невидимая 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
и вкладки. Это утверждение скрывает фабрику и масштабирует ее, так что, когда она снова становится видимой, ее можно сделать только масштабированием, а не излишним, а затем снова. Я установил масштаб, чтобы он соответствовал конечной шкале масштабирования с уменьшением масштаба 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.:)
Ответ 6
вы можете добавить слушателя в 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();
}
Ответ 7
Что сработало для меня:
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();
}