带有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

我感谢任何提示和帮助

  • Android以编程方式设置button文本
  • 可以在buttonxml中添加参数?
  • 防止按键在Android上隐藏软键盘
  • 由png和overlay组成的可绘制的XML
  • Android ViewPager上一个/下一个button
  • 如何让button的背景恢复为默认(以编程方式)?
  • 使用切换语句来处理button点击
  • 列表视图中的button正在创build滚动和点击button上的问题
  • 首先,您需要将浮动动作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) { } });