先看效果:
listview实现
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的配置文件实现,也可以通过设计函数实现。
作者:陈诗泱
原文地址