【问题标题】:FAB animation with viewpager/tabslider带有 viewpager/tabslider 的 FAB 动画
【发布时间】:2015-10-14 07:21:12
【问题描述】:

我正在想办法达到这样的效果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 android-viewpager material-design floating-action-button


    【解决方案1】:

    对于我的问题,我不想在第一个选项卡上显示浮动操作按钮;为了实现这一点,我添加了

    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 的建议在我的活动中添加了 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) {
    
                }
            });
    

    【讨论】:

      【解决方案2】:

      为了获得良好的隐藏和显示动画,请使用:

      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;
                      }
      
                  }
              });
      

      【讨论】:

        【解决方案3】:

        这是另一种解决方案。对我来说其他的问题是,将 FAB 的控制权交给包含 ViewPager 的活动会使事情变得难以编码。

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

        大致步骤如下:

        1. 在包含 ViewPager 的主 Activity 中声明一个 FAB,而不是在片段中(这是我想避免的强制性步骤,但魔法随之而来;否则会使制作好的动画变得困难)李>
        2. 在要在 ViewPager 中显示的每个片段中,我创建了一个可以传递 FAB 的方法,例如 public void shareFab(FloatingActionButton sharedFab);在此方法中,每个片段都会设置 FAB 图标并添加所需的侦听器
        3. 我在 ViewPager 上设置了一个 ViewPager.OnPageChangeListener 侦听器,它将触发动画并调用我的 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>
        

        优点是:

        • 类似于 Material Design 指南中描述的动画!
        • 每个片段对 FAB 的完全控制

        缺点是:

        • 由于 FAB 可以共享给其他 Fragment,每次都要检查 FAB 的引用以确保它不为空
        • 在ViewPager级别声明FAB,有点麻烦

        【讨论】:

          【解决方案4】:

          我发现的解决方案是:

          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 在用户更改视图时“隐藏”,但随后会在视图状态为空闲(已稳定)或正在稳定时触发 show 方法(我需要两者都做,因为它没有感觉正确只使用空闲)。

          【讨论】:

            【解决方案5】:

            我最近找到了令我满意的更好的解决方案。 FAB hide 方法也可以有一个参数——监听器,当按钮被隐藏时触发(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());
            }
            

            【讨论】:

              【解决方案6】:

              首先,您需要将带有浮动操作按钮的布局锚定到 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 中的选项卡片段上尝试过此操作;它似乎行为不正常),将 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) {
              
                      }
                  });
              

              新版本的支持库会自动为您处理切换选项卡时的“pop”和“shrink”动画。

              【讨论】:

              • 谢谢,我已经实现了这一点,但使用自定义动画。我有一个小问题,如何强制 FAB 在片段内运行不同的操作取决于当前页面(每个页面都有自己的片段)
              • @ray20,我正在努力解决这个问题……如果我有什么想法会回复你的。在我的脑海中,您可以根据片段的索引更改页面更改时的操作(OnPageChangeListener 将在那里提供帮助)。从那以后仍然是一个令人讨厌的“解决方案”,您还必须根据当前选择的选项卡更改点击的作用。如果我想出更清洁的方法,会通知您。
              • 我是通过在 setUserVisibleHint 方法中设置 onClickListener 来实现的。
              • 还有其他问题 - 使用支持库中的 fab 我无法像你说的那样强制它自动动画。 ViewPager 和 FloatingActionButton 在 CoordinatorLayout 中,它在 DrawerLayout 中但不工作,我不知道除了为 FAB 设置锚属性之外,我是否应该在布局/代码中做一些事情。我也想知道如何强制 Fab 使用 viewPager 和选项卡上的列表..
              • 你能发布你的代码吗?我的代码也在抽屉布局中(不过我只发布了 coordinatorlayout 部分),并且让它工作(尽管我不太喜欢这种讨厌的方式),并且我在每个选项卡中都使用了带有 RecyclerViews 的 ViewPager。但是,您必须将 FAB 锚定到 ViewPager,而不是锚定到选项卡内的列表,尤其是在您折叠工具栏时。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多