【问题标题】:FloatingActionButton hide on list scrollFloatingActionButton 在列表滚动时隐藏
【发布时间】:2015-10-15 12:45:38
【问题描述】:

我正在使用android.support.design.widget 包中的FloatingActionButton

<android.support.design.widget.FloatingActionButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentEnd="true"
    android:layout_marginBottom="20dp"
    android:layout_marginEnd="16dp"
    android:clickable="true"
    android:backgroundTint="@color/primaryColor"
    android:src="@drawable/ic_search_white_24dp"
    app:borderWidth="0dp"
    app:elevation="6dp"
    app:backgroundTint="@color/primaryColorDark"
    app:rippleColor="@color/accentColor" />

是否可以将该按钮配置为在列表视图向下滚动时隐藏动画并在列表视图向上滚动到顶部时再次显示?

【问题讨论】:

  • 使用设计库中的CoordinatorLayout
  • 我想你说的是支持库中的协调器布局。如果这没有帮助,您可以手动连接并计算:stackoverflow.com/questions/10713312/…
  • 如果你使用 ListView 和 kotlin,你可以使用这个method
  • 如果您使用 ListViews 和 Kotlin,请使用 method

标签: android scroll floating-action-button


【解决方案1】:

那些希望使用 recyclerview 的人可以这样做:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (dy > 0 || dy < 0 && fab.isShown())
            fab.hide();
    }

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        if (newState == RecyclerView.SCROLL_STATE_IDLE)
            fab.show();
        super.onScrollStateChanged(recyclerView, newState);
    }
});

【讨论】:

    【解决方案2】:

    对不起!我迟到了很多年才回答这个问题。我希望这仍然可以帮助某人。这也是我的第一个答案。

    伙计们!无需实现滚动监听器。

    将以下内容添加到浮动操作按钮 xml:

    app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
    

    给予:

    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
            android:id="@+id/fabAddOItransferIn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            android:text="@string/btn_text_transfer_in"
            app:icon="@android:drawable/ic_input_add"
            app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent" />
    

    针对我的以下评论, “对不起!我刚刚注意到这有一个奇怪的副作用。如果添加了 app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior,任何小吃栏都会与这个浮动操作按钮重叠。 ☹️ 取消这条线将防止重叠,浮动操作按钮将按照其在协调器布局内的预期行为。 "

    要解决这个问题,请使用以下方法:

    Snackbar.make(floating_action_button, "Some snackbar text!", BaseTransientBottomBar.LENGTH_SHORT).setAnchorView(floating_action_button).show();
    

    【讨论】:

    • 这应该是现在推荐的方法。非常感谢!
    • 不要忘记将按钮包裹在 CoordinatorLayout 中。
    • 对不起!我只是注意到这有一个奇怪的副作用。如果添加了 app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior,则任何小吃栏都将与此浮动操作按钮重叠。☹️ 取消此行将防止重叠,并且浮动操作按钮将按预期在内部运行协调器布局。
    • 如果您使用的是FloatingActionButtonSpeedDial,则可以添加speeddial_scrolling_view_snackbar_behavior 以使FAB 与小吃店相得益彰。
    【解决方案3】:

    Irfan Raza的代码的小改进:

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy){
                if (dy<0 && !fab.isShown())
                    fab.show();
                else if(dy>0 && fab.isShown())
                    fab.hide();
            }
    
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }
        });
    

    浮动操作按钮在向下滚动时隐藏,在向上滚动时显示。

    【讨论】:

    • 如果你向一个方向滚动,这会使 FAB 可见,如果你向相反方向滚动,它会隐藏 FAB,与 @Irfan Raza 发布的完全不同
    【解决方案4】:

    this。在这里,它告诉您如何做您想要实现的目标。您必须在 CoordinatorLayoutListView 中像这样使用它:

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/main_content"
        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">
    
              <ListView
                  android:id="@+id/lvToDoList"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"></ListView>
    
              <android.support.design.widget.FloatingActionButton
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_gravity="bottom|right"
                  android:layout_margin="16dp"
                  android:src="@drawable/ic_done"
                  app:layout_anchor="@id/lvToDoList"
                  app:layout_anchorGravity="bottom|right|end" />
    
    </android.support.design.widget.CoordinatorLayout>
    

    【讨论】:

    • 你链接的帖子说There is no support built-in for CoordinatorLayout to work with ListView according to this Google post.。但是,您在答案中使用 ListView。我还没有实现使它与ListView.. 一起使用
    • 如果这个方法有效,它只适用于 API21+,当引入嵌套滚动时。
    • 为什么这是公认的答案?似乎 CoordinatorLayout 仅适用于 RecyclerView。我错过了什么吗?
    • @ejang 协调器布局是最好的,因为它提供了许多有用的标志,如果你想使用其他功能,如 GooglePlayStore 的动画..
    • 有人可以解释这个答案如何实现@SyedAliNaqi 询问的滚动行为吗?使用上述 XML 不会添加滚动行为。
    【解决方案5】:

    使用这个类,您可以轻松地为您的 FAB 设置动画,这里我实现了 onStopNestedScroll() 方法,以便在滚动停止时显示您的 Fab。 我使用 Handler() 将 1000 miliSeconds 设置为延迟;

    public class FabBehaviour extends CoordinatorLayout.Behavior<FloatingActionButton> {
        private static final String TAG = "ScrollingFABBehavior";
        Handler mHandler;
    
        public FabBehaviour(Context context, AttributeSet attrs) {
            super();
        }
    
        public FabBehaviour() {
            super();
        }
    
        @Override
        public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull final FloatingActionButton child, @NonNull View target, int type) {
            super.onStopNestedScroll(coordinatorLayout, child, target, type);
            if (mHandler == null)
                mHandler = new Handler();
    
    
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
                    Log.d("FabAnim", "startHandler()");
                }
            }, 1000);
        }
    
        @Override
        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
            if (dyConsumed > 0) {
                Log.d("Scrolling", "Up");
                CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
                int fab_bottomMargin = layoutParams.bottomMargin;
                child.animate().translationY(child.getHeight() + fab_bottomMargin).setInterpolator(new LinearInterpolator()).start();
            } else if (dyConsumed < 0) {
                Log.d("Scrolling", "down");
                child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
            }
        }
    
        @Override
        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
            if (mHandler != null) {
                mHandler.removeMessages(0);
                Log.d("Scrolling", "stopHandler()");
            }
            return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
        }
    
    
    }
    

    your_layout.xml

    <android.support.design.widget.FloatingActionButton
            android:id="@+id/imageViewYes"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end|right"
            android:layout_margin="@dimen/fab_margin"
            android:src="@drawable/ic_yes"
            app:backgroundTint="@color/white"
            android:scaleType="center"
            app:elevation="6dp"
            app:fabSize="normal"
            app:layout_behavior="com.your.package.FabBehaviour"
            app:pressedTranslationZ="12dp"
            app:rippleColor="@color/gray" />
    

    【讨论】:

    • 如何在java类中通过代码设置“app:layout_behavior="com.your.package.FabBehaviour"”?
    • 你说容易吗?这比使用滚动监听器复杂得多
    【解决方案6】:

    嘿,需要使用 recyclerview 在向下滚动时自动隐藏浮动操作按钮,为此我们可以以正常方式使用带有浮动操作按钮的默认列表视图,只需在 listview.onscroll 侦听器上进行修改,然后我们就可以感觉到喜欢回收

     listview.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
    
    
            }
    
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    
                int lastItem = firstVisibleItem + visibleItemCount;
                if (lastItem == totalItemCount) {
    
                    fab.setVisibility(View.INVISIBLE);
                }else {
                    fab.setVisibility(View.VISIBLE);
                }
            }
        });
    

    【讨论】:

    • 有了这个实现,如果列表视图不够长,浮动按钮将一直设置为INVISIBLE
    • 只需在 if 语句中将 lastItem == totalItemCount 更改为 lastItem == totalItemCount &amp;&amp; firstVisibleItem &gt; 0 :D
    【解决方案7】:

    在 kotlin 中有我的代码。

    class ScrollAwareFABBehavior (val recyclerView: RecyclerView, val floatingActionButton: FloatingActionButton) {
    
        fun start() {
            recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
                override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                    super.onScrolled(recyclerView, dx, dy)
    
                    if (dy > 0) {
                        if (floatingActionButton!!.isShown) {
                            floatingActionButton?.hide()
                        }
                    } else if (dy < 0) {
                        if (!floatingActionButton!!.isShown) {
                            floatingActionButton?.show()
                        }
                    }
                }
            })
        }
    }
    

    现在,您只需要使用 recyclerView 和构造函数上的 fab 调用 ScrollAwareFABBehavior,然后调用方法 start()。

    ScrollAwareFABBehavior(recyclerView = recyclerViewPlaceFormContainer, floatingActionButton = floatingActionButton).start()
    

    【讨论】:

      【解决方案8】:

      Kotlin + 数据绑定适配器

      @BindingAdapter("bindAdapter:attachFloatingButton")
      fun bindRecyclerViewWithFB(recyclerView: RecyclerView, fb: FloatingActionButton) {
          recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
              override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                  super.onScrolled(recyclerView, dx, dy)
      
                  if (dy > 0 && fb.isShown) {
                      fb.hide()
                  } else if (dy < 0 && !fb.isShown) {
                      fb.show()
                  }
              }
          })
      }
      

      和xml

          <androidx.recyclerview.widget.RecyclerView
                  android:id="@+id/main_recyclerview"
                  android:layout_width="match_parent"
                  app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                  android:layout_height="wrap_content"
                  android:clipToPadding="false"
                  android:paddingBottom="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
                  app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="8dp"
                  android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/main_chips"
                  android:layout_marginBottom="8dp"
                  **bindAdapter:attachFloatingButton="@{mainFb}"**
                  app:layout_constraintBottom_toBottomOf="parent" 
                  app:layout_constraintVertical_bias="0.0"/>
      
      
          <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
                  android:id="@+id/main_fb"
                  android:layout_width="wrap_content"
                  app:layout_constraintBottom_toBottomOf="parent"
                  style="@style/Widget.Design.FloatingActionButton"
                  app:layout_constraintEnd_toEndOf="parent"
                  android:layout_height="wrap_content"
                  android:layout_margin="18dp"
                  android:background="@color/colorPrimaryDark"
                  app:icon="@drawable/ic_add_black_24dp"/>
      

      【讨论】:

        【解决方案9】:

        在这里我为最后一个视图项添加额外的填充以避免列表项与浮动操作按钮重叠

        我在 RecyclerView.Adapter 的 onBindViewHolder 方法中使用它来将列表中最后一项的底部边距设置为 72dp,以便它会在浮动操作按钮上方向上滚动。

        这不需要列表中的虚拟条目。

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            // other binding code goes here.
        
            if (position + 1 == getItemCount()) {
                // set bottom margin to 72dp.
                setBottomMargin(holder.itemView, (int) (72 * Resources.getSystem().getDisplayMetrics().density));
            } else {
                // reset bottom margin back to zero. (your value may be different)
                setBottomMargin(holder.itemView, 0);
            }
        }
        
        public static void setBottomMargin(View view, int bottomMargin) {
            if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
                ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
                params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
                view.requestLayout();
            }
        }
        

        【讨论】:

          【解决方案10】:

          在我看来,实现这一点的最佳方法如下。

          public class ScrollingFABBehavior extends FloatingActionButton.Behavior {
          
          
          private static final String TAG = "ScrollingFABBehavior";
          
          public ScrollingFABBehavior(Context context, AttributeSet attrs) {
              super();
              // Log.e(TAG, "ScrollAwareFABBehavior");
          }
          
          
          public boolean onStartNestedScroll(CoordinatorLayout parent, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
          
              return true;
          }
          
          @Override
          public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
              if (dependency instanceof RecyclerView)
                  return true;
          
              return false;
          }
          
          @Override
          public void onNestedScroll(CoordinatorLayout coordinatorLayout,
                                     FloatingActionButton child, View target, int dxConsumed,
                                     int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
              // TODO Auto-generated method stub
              super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
                      dxUnconsumed, dyUnconsumed);
              //Log.e(TAG, "onNestedScroll called");
              if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
               //   Log.e(TAG, "child.hide()");
                  child.hide();
              } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
                //  Log.e(TAG, "child.show()");
                  child.show();
              }
          }}
          

          如需详细答案,请查看此内容。 Hide FloatingActionButton on scroll of RecyclerView

          【讨论】:

            【解决方案11】:

            对于 Kotlin,它非常简单(API 23+)

            myRecyclerView.setOnScrollChangeListener { _, _, _, _, oldScrollY ->
                if (oldScrollY < 0) myFAB.hide() else myFAB.show()
            }
            

            【讨论】:

            • 它说调用需要 api 23,所以它不适用于以前的版本?
            【解决方案12】:

            使用 kotlin 扩展的另一种 recycleView 方法。

            fun RecyclerView.attachFab(fab : FloatingActionButton) {
                this.addOnScrollListener(object : RecyclerView.OnScrollListener(){
                    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                        super.onScrolled(recyclerView, dx, dy)
                        if (dy > 0)
                            fab.hide()
                        else if (dy < 0)
                            fab.show()
                    }
                })
            }
            

            现在您可以使用以下方法将 fab 附加到任何 recycleView:

            rv.attachFab(requireActivity().fab)
            // in my case i made fab public on activity
            

            【讨论】:

              【解决方案13】:
              mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
              @Override
              public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                  super.onScrolled(recyclerView, dx, dy);
                  if (dy > 0 && mFloatingActionButton.getVisibility() == View.VISIBLE) {
                      mFloatingActionButton.hide();
                  } else if (dy < 0 && mFloatingActionButton.getVisibility() != View.VISIBLE) {
                      mFloatingActionButton.show();
                  }
              }});
              

              【讨论】:

                【解决方案14】:

                补充一下,NestedScrollView 的方法如下:

                        // register the extended floating action Button
                        final ExtendedFloatingActionButton extendedFloatingActionButton = findViewById(R.id.extFloatingActionButton);
                  
                        // register the nestedScrollView from the main layout
                        NestedScrollView nestedScrollView = findViewById(R.id.nestedScrollView);
                  
                        // handle the nestedScrollView behaviour with OnScrollChangeListener
                        // to extend or shrink the Extended Floating Action Button
                        nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
                            @Override
                            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                                // the delay of the extension of the FAB is set for 12 items
                                if (scrollY > oldScrollY + 12 && extendedFloatingActionButton.isExtended()) {
                                    extendedFloatingActionButton.shrink();
                                }
                  
                                // the delay of the extension of the FAB is set for 12 items
                                if (scrollY < oldScrollY - 12 && !extendedFloatingActionButton.isExtended()) {
                                    extendedFloatingActionButton.extend();
                                }
                  
                                // if the nestedScrollView is at the first item of the list then the
                                // extended floating action should be in extended state
                                if (scrollY == 0) {
                                    extendedFloatingActionButton.extend();
                                }
                            }
                        });
                

                我从GeeksForGeeks获取了这段代码

                【讨论】:

                  猜你喜欢
                  • 2016-01-17
                  • 2021-06-17
                  • 2023-01-17
                  • 2019-07-05
                  • 2016-05-13
                  • 2023-02-13
                  • 2018-01-19
                  • 2015-09-25
                  • 1970-01-01
                  相关资源
                  最近更新 更多