先上效果图
整体思路:
1.Fab需要作为CoordinatorLayout的子控件
2.设置好动画,分别隐藏和显示Fab
3.自定义MyBehavior类,继承FloatingAcitionButton.Behavior,重写方法
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type)
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type)
4.在xml文件里,设置Fab的属性
app:layout_behavior
值为MyBehavior类的全称
例如:
com.example.m.viewpagertest.MyBehavior
下面是具体代码
activity_main.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.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/pager"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
fragment_blank.xml
<android.support.design.widget.CoordinatorLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/nestedView">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment"
android:id="@+id/txt_fragment"/>
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@string/hello_blank_fragment" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fabSize="normal"
android:layout_margin="8dp"
android:elevation="2dp"
app:pressedTranslationZ="8dp"
android:clickable="true"
android:layout_gravity="bottom|right"
app:layout_behavior="com.example.m.viewpagertest.MyBehavior"
android:id="@+id/fab"/>
</android.support.design.widget.CoordinatorLayout>
java
package com.example.m.viewpagertest;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
/**
* Created by m on 2018/3/4.
*/
public class AnimatorUtil {
private static AccelerateDecelerateInterpolator LINEAR_INTERRPLATOR =new AccelerateDecelerateInterpolator();
public static void showFab(View view,MyBehavior.AnimateListener ... listener){
if (listener.length!=0){
view.animate()
.scaleX(1f)
.scaleY(1f)
.alpha(1f)
.setDuration(600)
.setInterpolator(LINEAR_INTERRPLATOR)
.setListener(listener[0])
.start();
}else {
view.animate()
.scaleX(1f)
.scaleY(1f)
.alpha(1f)
.setDuration(600)
.setInterpolator(LINEAR_INTERRPLATOR)
.start();
}
}
public static void hideFab(View view,MyBehavior.AnimateListener listener){
view.animate()
.scaleX(0f)
.scaleY(0f)
.alpha(0f)
.setDuration(600)
.setInterpolator(LINEAR_INTERRPLATOR)
.setListener(listener)
.start();
}
}
package com.example.m.viewpagertest;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.widget.NestedScrollView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.w3c.dom.Text;
/**
* A simple {@link Fragment} subclass.
*/
public class BlankFragment extends Fragment {
private NestedScrollView scrollView;
private FloatingActionButton fab;
public BlankFragment() {
// Required empty public constructor
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.fragment_blank, container, false);
Bundle bundle=getArguments();
int id=bundle.getInt("id");
TextView textView=view.findViewById(R.id.txt_fragment);
textView.setText(MainActivity.name[id]);
scrollView=view.findViewById(R.id.nestedView);
fab=view.findViewById(R.id.fab);
scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY==(v.getChildAt(0).getMeasuredHeight()-v.getMeasuredHeight())){
AnimatorUtil.showFab(fab);
}
}
});
return view;
}
//通过id来新建对于的fragment
public static BlankFragment newInstance(int mID){
Bundle bundle=new Bundle();
bundle.putInt("id",mID);
BlankFragment fragment=new BlankFragment();
fragment.setArguments(bundle);
return fragment;
}
}
package com.example.m.viewpagertest;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private int mFragmentCount;//fragment数量
public static String[]name;//每个fragment的textView设置的内容
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFragmentCount=5;
name=new String[]{"one","two","three","four","five"};
ViewPager viewPager=findViewById(R.id.pager);
MyPagerAdapter adapter=new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
}
public class MyPagerAdapter extends FragmentPagerAdapter{
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
//通过BlankFragment自定义的方法,根据位置,返回fragment
@Override
public Fragment getItem(int position) {
return BlankFragment.newInstance(position);
}
@Override
public int getCount() {
return mFragmentCount;
}
}
}
package com.example.m.viewpagertest;
import android.animation.Animator;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AnimationUtils;
/**
* Created by m on 2018/3/4.
*/
public class MyBehavior extends FloatingActionButton.Behavior {
private boolean isAnimateIng = false; // 是否正在动画
private boolean isShow = true; // 是否已经显示
public MyBehavior(Context context, AttributeSet attrs) {
super();
}
//只有当返回值为true才会执行下面的方法,例如onNestedScroll
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
||axes== ViewCompat.SCROLL_AXIS_VERTICAL;
}
//滑动时调用该方法
//dxConsumed: 表示view消费了x方向的距离长度
//dyConsumed: 表示view消费了y方向的距离长度
//消费的距离是指实际滚动的距离
/*
* coordinatorLayout - the CoordinatorLayout parent of the view this Behavior is associated with
child - the child view of the CoordinatorLayout this Behavior is associated with
target - the descendant view of the CoordinatorLayout performing the nested scroll
dxConsumed - horizontal pixels consumed by the target's own scrolling operation
dyConsumed - vertical pixels consumed by the target's own scrolling operation
dxUnconsumed - horizontal pixels not consumed by the target's own scrolling operation, but requested by the user
dyUnconsumed - vertical pixels not consumed by the target's own scrolling operation, but requested by the user
type - the type of input which cause this scroll event
*/
@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
//界面向下滑动,fab动画结束,且正在显示
//隐藏Fab
if ((dyConsumed>0||dyUnconsumed>0)&&!isAnimateIng&&isShow){
AnimatorUtil.hideFab(child,new AnimateListener());
}
//界面向上滑动,fab动画结束,且隐藏
//显示Fab
else if ((dyConsumed<0||dyUnconsumed<0)&&!isAnimateIng&&!isShow){
AnimatorUtil.showFab(child,new AnimateListener());
}
}
public class AnimateListener implements Animator.AnimatorListener {
@Override
public void onAnimationStart(Animator animation) {
isAnimateIng=true;
isShow=!isShow;
}
@Override
public void onAnimationEnd(Animator animation) {
isAnimateIng=false;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}
}
注意,这里还实现了,如果下滑到底部,Fab也会出现
scrollView=view.findViewById(R.id.nestedView);
fab=view.findViewById(R.id.fab);
scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY==(v.getChildAt(0).getMeasuredHeight()-v.getMeasuredHeight())){
AnimatorUtil.showFab(fab);
}
}
});
v.getChildAt(0).getMeasuredHeight() //获得子控件的高度,即LinearLayout高度
v.getMeasuredHeight() //获得NestedScrollView屏幕高度
scrollY //滑动高度
下图分别是NestedScrollView与LinearLayout的高度
从这里我们可以看到,当
下滑的距离=LinearLayout的高度-NestedScrollView的高度
就表示到达底部
关于getHeight与getMeasuredHeight的辨析可以看看这篇文章