ListView和Recyclerview实现滑动隐藏与显示FloatingActionButton

先看效果:

listview实现
listview实现
recyclerview实现
recyclerview实现

FloatingActionButton

build.gradle 的 dependencies 需要引入

    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support:design:27.1.1'

FloatingActionButton XML布局设计

<android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20.0dip"
        app:backgroundTint="@color/colorfab"
        app:elevation="5.0dip"
        app:fabSize="normal"
        android:src="@drawable/ic_menu_compose"
        app:layout_anchor="@id/listview"
        app:layout_anchorGravity="bottom|right"
        app:pressedTranslationZ="10.0dip"
        app:rippleColor="@color/colorfabclick"
        />

android:src:FAB中显示的图标.
app:backgroundTint:正常的背景颜色 ,这里是在values的colors.xml中引用
app:rippleColor:按下时的背景颜色
app:elevation:正常的阴影大小
app:pressedTranslationZ:按下时的阴影大小
app:layout_anchor:设置FAB的锚点,即以哪个控件为参照设置位置,我在上面设置了listview,所以以 listview为锚点,且父类布局须为CoordinatorLayout
app:layout_anchorGravity:FAB相对于锚点的位置,
app:fabSize:FAB的大小,这里设normal,没试过mini

完整xml布局

<?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:id="@+id/coordinator1">

    <ListView
        android:id="@+id/listview"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        />
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20.0dip"
        app:backgroundTint="@color/colorfab"
        app:elevation="5.0dip"
        app:fabSize="normal"
        android:src="@drawable/ic_menu_compose"
        app:layout_anchor="@id/listview"
        app:layout_anchorGravity="bottom|right"
        app:pressedTranslationZ="10.0dip"
        app:rippleColor="@color/colorfabclick"
        />
</android.support.design.widget.CoordinatorLayout>

listview结合FloatingActionButton设计

其实不管是listview还是recyclerview都是为其添加监听器检测其滑动动作,如果上滑则显示FAB(FloatingActionButton,以下都简称FAB)按钮,下滑隐藏该按钮。
一开始的效果图是用之前老师布置的作业实现的,额外设计了animal类和animaladapter,这里进行简单的arrayadapter展示
在这里插入图片描述
初始化:

	private ListView listView1;
    private FloatingActionButton button5_change;
    private AnimatorSet hideFabAS;
    private AnimatorSet showFabAS;
    private boolean FAB_VISIBLE = true;
    private int previousFirstVisibleItem;   //记录前面第一个Item
    private int lastScrollY;            //记录ListView中最上面的Item(View)的上一次顶部Y坐标()
    private int scrollThreshold = 2;
    private int getTopItemScrollY() {
        if (listView1 == null || listView1.getChildAt(0) == null) return 0;
        View topItem = listView1.getChildAt(0);
        return topItem.getTop();
    }

Animatiorset就是为FAB添加动画,这里简单设置两个,一个显示一个隐藏。
getTopItemScrollY()是获得listview的最上面位置的Y坐标
设置FAB_VISIBLE是为了防止 设计的FAB(button5_change) 频繁执行动画,比如:

Oncreate():

		List<String> list = new ArrayList<>();
        for (int i = 0; i < 150; i++) {
            list.add("TestItem_" + i);
        }
        listView1 = (ListView)findViewById(R.id.listview);
        //listView1.setAdapter(adapter);
        listView1.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,list));
        button5_change=(FloatingActionButton)findViewById(R.id.fab);
        hideFabAS = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.scroll_hide_fab);
        showFabAS = (AnimatorSet)AnimatorInflater.loadAnimator(this,R.animator.scroll_show_fab);
        //AnimatorInflater.loadAnimator加载动画
        hideFabAS.setTarget(button5_change);
        showFabAS.setTarget(button5_change);
        //设置动画目标控件
        listView1.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                //listview初始化的时候会回调onScroll
                if(totalItemCount == 0) {
                    showFabAS.start();//
                    return;
                }
                //滚动过程中:ListView中最上面一个Item还是同一个Item
                if(previousFirstVisibleItem == firstVisibleItem) {
                    int newScrollY =  getTopItemScrollY();//获得当前最上方item Y坐标
                    boolean isExceedThreshold = Math.abs(lastScrollY - newScrollY) > scrollThreshold;
                    if (isExceedThreshold) {
                        if (lastScrollY > newScrollY && FAB_VISIBLE == true) {//下滑
                            FAB_VISIBLE = false;
                            hideFabAS.start();//FAB执行动画
                        } else if(lastScrollY < newScrollY && FAB_VISIBLE == false){//上滑
                            FAB_VISIBLE = true;
                            showFabAS.start();//FAB执行动画
                        }
                    }
                    lastScrollY = newScrollY;
                } else {
                    if (firstVisibleItem > previousFirstVisibleItem && FAB_VISIBLE == true){
                        //向下滑动时FAB执行动画
                        FAB_VISIBLE = false;
                        hideFabAS.start();
                    } else if(firstVisibleItem < previousFirstVisibleItem && FAB_VISIBLE == false){
                        //向上滑动时FAB执行动画
                        FAB_VISIBLE = true;
                        showFabAS.start();
                    }
                    lastScrollY =  getTopItemScrollY();
                    previousFirstVisibleItem = firstVisibleItem;
                }
            }
        });

Animator中的scroll_hide_fab.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together"
    >
    <objectAnimator
        android:propertyName="translationY"
        android:valueTo="500.00"
        android:duration="200"
        android:startOffset="0"
        android:repeatCount="0"
        android:valueType="floatType"
        />
    <objectAnimator
        android:propertyName="alpha"
        android:valueFrom="1.00"
        android:valueTo="0.00"
        android:duration="200"
        android:startOffset="0"
        android:valueType="floatType"
        android:repeatCount="0"
        />
</set>

Animator中的scroll_hide_fab.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together"
    >
    <objectAnimator
        android:propertyName="translationY"
        android:valueTo="0.0"
        android:duration="100"
        android:startOffset="0"
        android:repeatCount="0"
        android:valueType="floatType"
        />
    <objectAnimator
        android:propertyName="alpha"
        android:valueFrom="0.00"
        android:valueTo="1.00"
        android:duration="100"
        android:startOffset="0"
        android:valueType="floatType"
        android:repeatCount="0"
        />
</set>

实现这个效果从网上找了很多对listview的监听的方法,该监听方法是针对listview的所有item来对FAB进行动画执行,以下的链接给了很大帮助。
参考地址

RecyclerView结合FloatingActionButton设计

RecyclerView是新控件,较于listview可以实现更多的功能。
在使用RecyclerView时候,必须指定一个适配器Adapter和一个布局管理器LayoutManager。适配器继承RecyclerView.Adapter类。
该设计比较常见所以网上有很多实现,我是直接参考下面的链接的
参考地址
先设计了FAB动画的接口:

package com.example.myapplication_1;

public interface FABStateListener {

    public void onFABHide();

    public void onFABShow();
}

activity中:onFABHide()和onFABShow()是对接口的实现,onClickFAB是点击FAB回到顶端

	private RecyclerView recyclerView;
    private FloatingActionButton floatingActionButton;
    public void onClickFAB(View view) {
        recyclerView.smoothScrollToPosition(0);
    }

    @Override
    public void onFABHide() {
        RelativeLayout.LayoutParams layoutParams = (LayoutParams) floatingActionButton.getLayoutParams();
        floatingActionButton.animate().translationY(floatingActionButton.getHeight() + layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
    }

    @Override
    public void onFABShow() {
        floatingActionButton.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
    }

Oncreate():

floatingActionButton = findViewById(R.id.fabr);
        recyclerView = findViewById(R.id.rcv);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 150; i++) {
            list.add("TestItem_" + i);
        }
        recyclerView.setAdapter(new RecFabAdapter(list));//设置自己构建的适配器
        recyclerView.addOnScrollListener(new RecScrollListener(this));
        //设置自己构建的监听器

自己构建适配器 继承RecyclerView.Adapter类 重写三个方法:
onCreateViewHolder()方法,负责承载每个子项的布局。它有两个参数,其中一个是 int viewType;
onBindViewHolder()方法,负责将每个子项holder绑定数据。俩参数分别是RecyclerView.ViewHolder holder, int position;
getItemCount() 返回数组的size

package com.example.myapplication_1;


import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;


public class RecFabAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private List<String> list;

    public RecFabAdapter(List<String> list) {
        this.list = list;
    }


    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.rev_litem, viewGroup, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
        String str = list.get(position);
        MyViewHolder holder = (MyViewHolder) viewHolder;
        holder.textView.setText(str);
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        private TextView textView;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.rec_tv);//自定义布局
        }
    }
}

监听器 重写onScrolled方法

package com.example.myapplication_1;


import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;


public class RecScrollListener extends RecyclerView.OnScrollListener {
    private static final int THRESHOLD = 10;
    private int distance = 0;
    private FABStateListener fabStateListener;
    private boolean visible = true;//是否可见

    public RecScrollListener(FABStateListener hideScrollListener) {
        this.fabStateListener = hideScrollListener;
    }

    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        //Log.i("mtx","dy:"+dy);
        if (distance > THRESHOLD && visible) {
            //隐藏
            visible = false;
            fabStateListener.onFABHide();
            distance = 0;
        } else if (distance < -20 && !visible) {
            //显示
            visible = true;
            fabStateListener.onFABShow();
            distance = 0;
        }
        if (visible && dy > 0 || (!visible && dy < 0)) {
            distance += dy;
        }
    }
}

自定义布局 rev_litem

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/rec_tv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="sss"
        android:textSize="16sp" />
</LinearLayout>

listview滑动实现FAB的隐藏显示是比较简单的,不需要多实现adapter和相关类,不过显然用recyclerview可以实现更多的功能,设计贴合自己需要的类,功能也更为强大,FAB的动画可以通过animator和anim的配置文件实现,也可以通过设计函数实现。

作者:陈诗泱
原文地址

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值