带有viewpager / tabslider的FABanimation

我试图find方法来实现这种效果https://material-design.storage.googleapis.com/publish/material_v_4/material_ext_publish/0B6Okdz75tqQsVlhxNGJCNTEzNFU/components-buttons-fab-behavior_04_xhdpi_009.webm

我正在使用android.support.v4.view.ViewPager

我感谢任何提示和帮助

Solutions Collecting From Web of "带有viewpager / tabslider的FABanimation"

首先,您需要将浮动动作button锚定到ViewPager的布局:

<android.support.design.widget.CoordinatorLayout android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <android.support.v7.widget.Toolbar android:id="@+id/action_bar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" app:tabGravity="center" app:tabIndicatorColor="?attr/colorAccent" app:tabMode="scrollable" app:tabSelectedTextColor="?attr/colorAccent" app:tabTextColor="@android:color/black" /> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager" 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/fab" app:layout_anchor="@id/viewpager" app:layout_anchorGravity="bottom|right|end" android:layout_width="56dp" android:layout_height="56dp" android:src="@drawable/ic_add_white" app:borderWidth="@dimen/fab_border_width" app:fabSize="normal" /> </android.support.design.widget.CoordinatorLayout> 

现在你已经将FAB锚定到了ViewPager上(注意:我已经在viewpager中select了一个片段来尝试这个function;它似乎没有正常工作),添加一个OnPageChangeListener到你的ViewPager中,像这样:

  viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { switch (position) { case INDEX_OF_TAB_WITH_FAB: fab.show(); break; default: fab.hide(); break; } } @Override public void onPageScrollStateChanged(int state) { } }); 

切换选项卡上的“popup”和“缩小”的animation将自动由新版本的支持库进行处理。

这是另一个解决scheme。 对于其他人来说,问题在于,将FAB的控制权交给包含ViewPager的活动使得编码变得困难。

我需要Fragments对FAB进行很多控制,以便轻松更改FAB图标并在其上设置特定的侦听器。

粗略地说,步骤是:

  1. 在包含ViewPager的主要活动中声明一个FAB,而不是在片段中(这是我想要避免的一个强制性步骤,但魔法来临之后;否则会使一个好的animation变得困难)
  2. 在ViewPager中要显示的每个片段中,我创build了一个可以传递FAB的方法,如public void shareFab(FloatingActionButton sharedFab) ; 在这个方法里面,每个片段都会设置FAB图标并添加所需的监听器
  3. 我在ViewPager.OnPageChangeListener上设置了一个ViewPager.OnPageChangeListener监听器,它将触发animation并调用我的shareFab方法。

所以每次显示一个片段时,包含的活动都会给显示的片段提供FAB的引用。 该片段然后重置/回收FAB以适应其需要。

这里是一个例子:

要显示的片段

 public void Fragment1 extends Fragment { private FloatingActionButton mSharedFab; @Override public void onDestroy() { super.onDestroy(); mSharedFab = null; // To avoid keeping/leaking the reference of the FAB } public void shareFab(FloatingActionButton fab) { if (fab == null) { // When the FAB is shared to another Fragment if (mSharedFab != null) { mSharedFab.setOnClickListener(null); } mSharedFab = null; } else { mSharedFab = fab; mSharedFab.setImageResource(R.drawable.ic_search); mSharedFab.setOnClickListener((fabView) -> { // Your code }); } } } 

容器活动

 public class ActivityMain extends AppCompatActivity { FloatingActionButton mSharedFab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mSharedFab = (FloatingActionButton) findViewById(R.id.shared_fab); ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); Fragment1 fragment1 = new Fragment1(); Fragment2 fragment2 = new Fragment2(); adapter.addFragment(fragment1, "Fragment1"); adapter.addFragment(fragment2, "Fragment2"); ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager); viewPager.setAdapter(adapter); fragment1.shareFab(mSharedFab); // To init the FAB 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_DRAGGING: mSharedFab.hide(); // Hide animation break; case ViewPager.SCROLL_STATE_IDLE: switch (viewPager.getCurrentItem()) { case 0: fragment2.shareFab(null); // Remove FAB from fragment fragment1.shareFab(mSharedFab); // Share FAB to new displayed fragment break; case 1: default: fragment1.shareFab(null); // Remove FAB from fragment fragment2.shareFab(mSharedFab); // Share FAB to new displayed fragment break; } mSharedFab.show(); // Show animation break; } } }); } } 

…和主要活动的经典布局

 <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="fixed" app:tabGravity="fill"/> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager" 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/shared_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" /> </android.support.design.widget.CoordinatorLayout> 

优点是:

  • animation就像材料devise指南中描述的一样!
  • 通过每个片段完全控制FAB

缺点是:

  • 由于FAB可以共享到另一个片段, 所以每次都要检查FAB的引用,以确保它不为空
  • 在ViewPager级别的FAB声明,有点麻烦

对于隐藏和显示的好animation使用这个:

 mViewPager.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_IDLE: fab.show(); break; case ViewPager.SCROLL_STATE_DRAGGING: case ViewPager.SCROLL_STATE_SETTLING: fab.hide(); break; } } }); 

我发现的解决scheme是:

  1. 我的ViewPager中显示的片段实现了一个接口(在我的情况下FloatingActionButtonFragment )。 这个接口要求片段有一个方法handleFABClick(View)

  2. 我的ViewPager更改侦听器,然后如下所示:

     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: displayFloatingActionButtonIfNeeded(viewPager.getCurrentItem()); break; case ViewPager.SCROLL_STATE_IDLE: displayFloatingActionButtonIfNeeded(viewPager.getCurrentItem()); break; default: mFloatingActionButton.hide(); } } }); private void displayFloatingActionButtonIfNeeded(int position) { if (mFragmentStatePagerAdapter.getItem(position) instanceof FloatingActionButtonFragment) { final FloatingActionButtonFragment floatingActionButtonFragment = (FloatingActionButtonFragment) mFragmentStatePagerAdapter.getItem(position); mFloatingActionButton.show(); mFloatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { floatingActionButtonFragment.handleFABClick(v); } }); } } 

这使得当用户改变视图时,FAB“隐藏”,但是当视图状态为空闲(已经解决)或者正在解决(我需要做这两个,因为它感觉不对,只是使用闲)。

我最近find了更好的解决scheme,我感到满意。 FAB隐藏方法也可以有一个参数监听器,当隐藏button时触发(show方法也有这个)。 唯一的缺点是您必须手动实现viewpager和tablayout更改。

 @Override public void onPageSelected(int position) { onToolbarTitleChanged(adapter.getPageTitle(position).toString()); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { } @Override public void onTabSelected(TabLayout.Tab tab) { viewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(final TabLayout.Tab tab) { fab.hide(new FloatingActionButton.OnVisibilityChangedListener() { // Hide FAB @Override public void onHidden(FloatingActionButton fab) { fab.show(); // After FAB is hidden show it again } }); } @Override public void onTabReselected(TabLayout.Tab tab) { } 

如果只想在特定选项卡上显示FAB,则可以将onTabSelected方法更改为:

 @Override public void onTabSelected(TabLayout.Tab tab) { if (tab.getPosition() == 2) { createButton.hide(); } viewPager.setCurrentItem(tab.getPosition()); } 

对于我的问题,我不想在第一个选项卡上显示浮动操作button; 为了达到这个我补充说

 android:visibility="invisible" 

在浮动的行动酒吧。

 <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="@dimen/fab_margin" android:visibility="invisible" app:srcCompat="@android:drawable/ic_input_add" /> 

我已经按照@chantell的build议在我的活动中添加了ViewpagerListner

 mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { switch (position) { case 0: addFloatingActionBt.hide(); break; default: addFloatingActionBt.show(); break; } } @Override public void onPageScrollStateChanged(int state) { } });