Как изменить FloatingActionButton между вкладками?

Я пытаюсь реализовать FloatingActionButton от Библиотека Поддержки Дизайна Google в двух из трех вкладок, и в соответствии с Руководство По Проектированию Материалов-FloatingActionButton он говорит:

Если есть плавающая кнопка действия на нескольких боковых экранов (таких как на вкладках), при входе в каждый экран, кнопка должна показывать и скрыть, если действие, содержащееся в каждом отличающийся. Если действие то же самое, кнопка должна оставаться на экране (и перевести на новый положение, если необходимо.)

Example of FAB animation in tabs

Как я могу сделать такой переход или анимацию для кнопок 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) {

    }
});

убедитесь, что у вас есть достаточно цветов и значков сопоставьте количество вкладок, которые у вас есть.


Ниже приведен простой способ достичь желаемого результата

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) {

    }
};

расширение ответа 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();
}