FloatingActionButton 在列表滚动时隐藏

Posted

技术标签:

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

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

<android.support.design.widget.FloatingActionButton
    android:layout_
    android:layout_
    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 我想你说的是支持库中的协调器布局。如果这没有帮助,您可以手动连接并计算:***.com/questions/10713312/… 如果你使用 ListView 和 kotlin,你可以使用这个method。 如果您使用 ListViews 和 Kotlin,请使用 method。 【参考方案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_
        android:layout_
        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_
    android:layout_>

          <ListView
              android:id="@+id/lvToDoList"
              android:layout_
              android:layout_></ListView>

          <android.support.design.widget.FloatingActionButton
              android:layout_
              android:layout_
              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_
        android:layout_
        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_
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            android:layout_
            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_
            app:layout_constraintBottom_toBottomOf="parent"
            style="@style/Widget.Design.FloatingActionButton"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_
            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获取了这段代码

【讨论】:

以上是关于FloatingActionButton 在列表滚动时隐藏的主要内容,如果未能解决你的问题,请参考以下文章

带有片段和底部导航栏的 Android FloatingActionButton

Jetpack Compose 无限加载列表(滚到底部自动加载更多)

FloatingActionButton 在 ViewPager 中不可见

在 RecyclerView 的滚动上隐藏 FloatingActionButton

Flutter - 隐藏 FloatingActionButton

向下滚动隐藏效果的 FloatingActionButton