先贴 Behavior 的代码,这个 Behavior 的效果是上滑消失下滑显示,呈现效果是缩放隐藏,展开出现,这是题外话。

public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();    private boolean mIsAnimatingOut = false;    public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {        super();    }    @Override    public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,                                       final View directTargetChild, final View target, final int nestedScrollAxes) {        // 确定是在垂直方向上滑动        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);    }    @Override    public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,                               final View target, final int dxConsumed, final int dyConsumed,                               final int dxUnconsumed, final int dyUnconsumed) {        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);        if (dyConsumed > 0 && !this.mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {            // 不显示FAB            animateOut(child);        }        else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {            // 显示FAB            animateIn(child);        }    }    // 定义滑动时的属性动画效果    private void animateOut(final FloatingActionButton button) {            if (Build.VERSION.SDK_INT >= 14) {                ViewCompat.animate(button).scaleX(0.0F).scaleY(0.0F).alpha(0.0F).setInterpolator(INTERPOLATOR).withLayer()                        .setListener(new ViewPropertyAnimatorListener() {                            public void onAnimationStart(View view) {                                ScrollAwareFABBehavior.this.mIsAnimatingOut = true;                            }                            public void onAnimationCancel(View view) {                                ScrollAwareFABBehavior.this.mIsAnimatingOut = false;                            }                            public void onAnimationEnd(View view) {                                ScrollAwareFABBehavior.this.mIsAnimatingOut = false;                                view.setVisibility(View.GONE);                            }                        }).start();            }            else {                Animation anim = AnimationUtils.loadAnimation(button.getContext(), R.anim.fade_out_end);                anim.setInterpolator(INTERPOLATOR);                anim.setDuration(200L);                anim.setAnimationListener(new Animation.AnimationListener() {                    public void onAnimationStart(Animation animation) {                        ScrollAwareFABBehavior.this.mIsAnimatingOut = true;                    }                    public void onAnimationEnd(Animation animation) {                        ScrollAwareFABBehavior.this.mIsAnimatingOut = false;                        button.setVisibility(View.GONE);                    }                    @Override                    public void onAnimationRepeat(final Animation animation) {                    }                });                button.startAnimation(anim);            }    }    private void animateIn(FloatingActionButton button) {            button.setVisibility(View.VISIBLE);            if (Build.VERSION.SDK_INT >= 14) {                ViewCompat.animate(button).scaleX(1.0F).scaleY(1.0F).alpha(1.0F)                        .setInterpolator(INTERPOLATOR).withLayer().setListener(null)                        .start();            }            else {                Animation anim = AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_anim);                anim.setDuration(200L);                anim.setInterpolator(INTERPOLATOR);                button.startAnimation(anim);            }    }}

因为项目需求要跟进 Android 7.1 的新特性,所以就跟着需求走,把 SDK 改成了25,然后就出现了题目中所说的问题,上滑隐藏,接下来不管怎么滑都无法显示出来了,带着疑问上网各种找,好不容易找到一篇相关的,当时作者给的解决方案是改回24,但明显不符合我的要求,然后作者还给出了问题的卡住点,是 Behavior 里面的 onNestedScroll 方法,然后我切换了24 以及 25 两个版本的SDK分别进行了调试,才发现确实是这样的,24 SDK时不管上滑下滑均会调用到这个方法,但是25 SDK起就只会跑两次,后来不管怎么滑都无法调用那个方法。

那么是否是因为有新的方法替代了呢?翻了翻对应的方法,发现并没有,方法没有过期,也没有出现新的方法。

瞬间就蒙了,切回24那我7.1的新特性就无法实现了,接下来我检查了下Behavior所用到的库,发现用到了 design 库,然后我就想会不会是这个库的版本的问题?

去谷歌那找了这个库的历史版本,发现25 SDK已经有了四个版本:25.0.0,25.0.1,25.1.0,25.1.1。

当时项目里面用的是最新的25.1.1,我试着单独把 design 库的版本切回25.0.0,重新 run 一遍,发现 Behavior 居然正常工作了!然后我又切了25.0.1也可以,但当我切到25.1.0以及25.1.1的时候又不行了。

所以初步断定这个可能是25.1.0和25.1.1两个版本的 design 库的 bug。

总结:解决方法是单独把 design 库切回25.0.0或者25.0.1版本。


2017.6.14更新解决方案:

因为26的 SDK 已经出来了,这个问题会因为更新26的 SDK 而继续存在,所以必须得查找新的解决方案。

上次说到 Behavior 里面的 onNestedScroll 方法无法再次调用的问题,后来上网查了相关资料,发现有人提及了 CoordinatorLayout 的 onNestedScroll() 方法在24 SDK 和25 SDK 中有小差别:

@Override    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,            int dxUnconsumed, int dyUnconsumed) {        final int childCount = getChildCount();        boolean accepted = false;        for (int i = 0; i < childCount; i++) {            final View view = getChildAt(i);            if (view.getVisibility() == GONE) {                // If the child is GONE, skip...                continue;            }            final LayoutParams lp = (LayoutParams) view.getLayoutParams();            if (!lp.isNestedScrollAccepted()) {                continue;            }            final Behavior viewBehavior = lp.getBehavior();            if (viewBehavior != null) {                viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed,                        dxUnconsumed, dyUnconsumed);                accepted = true;            }        }        if (accepted) {            onChildViewsChanged(EVENT_NESTED_SCROLL);        }    }

代码中的

            if (view.getVisibility() == GONE) {                // If the child is GONE, skip...                continue;            }

是在25 SDK 后加入的,那么问题的来源就找到了:我们 FloatingActionButton 的 Behavior 里面当向上滑动时通过调用 animateOut(final FloatingActionButton button) 方法让其隐藏,动画效果结束后我们调用的正是 setVisibility(View.GONE) 方法,这样就导致了CoordinatorLayout 在 onNestedScroll() 方法中因为其 getVisibility() == GONE 所以就被略过了,并不会触发 FloatingActionButton 的 onNestedScroll() 方法,这样就导致了下滑无法让其出现的问题。

那么解决方法很简单,不要用 hide() 和 setVisibility(View.GONE) 方法,可以改用 setVisibility(View.INVISIBLE) 方法;或者调整为将其滑出屏幕显示范围来达到隐藏的效果。

调整后的 Behavior 代码如下:

public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();    private boolean mIsAnimatingOut = false;    public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {        super();    }    @Override    public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,                                       final View directTargetChild, final View target, final int nestedScrollAxes) {        // 确定是在垂直方向上滑动        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);    }    @Override    public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,                               final View target, final int dxConsumed, final int dyConsumed,                               final int dxUnconsumed, final int dyUnconsumed) {        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);        if (dyConsumed > 0 && !this.mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {            // 不显示FAB            animateOut(child);        }        else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {            // 显示FAB            animateIn(child);        }    }    // 定义滑动时的属性动画效果    private void animateOut(final FloatingActionButton button) {            if (Build.VERSION.SDK_INT >= 14) {                ViewCompat.animate(button).scaleX(0.0F).scaleY(0.0F).alpha(0.0F).setInterpolator(INTERPOLATOR).withLayer()                        .setListener(new ViewPropertyAnimatorListener() {                            public void onAnimationStart(View view) {                                ScrollAwareFABBehavior.this.mIsAnimatingOut = true;                            }                            public void onAnimationCancel(View view) {                                ScrollAwareFABBehavior.this.mIsAnimatingOut = false;                            }                            public void onAnimationEnd(View view) {                                ScrollAwareFABBehavior.this.mIsAnimatingOut = false;                                view.setVisibility(View.INVISIBLE);                            }                        }).start();            }            else {                Animation anim = AnimationUtils.loadAnimation(button.getContext(), R.anim.fade_out_end);                anim.setInterpolator(INTERPOLATOR);                anim.setDuration(200L);                anim.setAnimationListener(new Animation.AnimationListener() {                    public void onAnimationStart(Animation animation) {                        ScrollAwareFABBehavior.this.mIsAnimatingOut = true;                    }                    public void onAnimationEnd(Animation animation) {                        ScrollAwareFABBehavior.this.mIsAnimatingOut = false;                        button.setVisibility(View.INVISIBLE);                    }                    @Override                    public void onAnimationRepeat(final Animation animation) {                    }                });                button.startAnimation(anim);            }    }    private void animateIn(FloatingActionButton button) {            button.setVisibility(View.VISIBLE);            if (Build.VERSION.SDK_INT >= 14) {                ViewCompat.animate(button).scaleX(1.0F).scaleY(1.0F).alpha(1.0F)                        .setInterpolator(INTERPOLATOR).withLayer().setListener(null)                        .start();            }            else {                Animation anim = AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_anim);                anim.setDuration(200L);                anim.setInterpolator(INTERPOLATOR);                button.startAnimation(anim);            }    }}

这样就可以把 design 库的版本切到最新而不影响 FloatingActionButton 的 Behavior 正常工作了。

更多相关文章

  1. Android出现Attempt to invoke virtual method... on a null obj
  2. 打开Android(安卓)C文件中的LOG
  3. android图形图像函数汇集
  4. Android(安卓)CoordinatorLayout+AppBarLayout+ToolBar实现标题
  5. Android(安卓)Service的两种启动方式,你知道吗?
  6. android软键盘不覆盖屏幕方法
  7. Android游戏引擎《Rokon》:主要类和方法的介绍文档
  8. Android培训班(85)升级到4.0版本
  9. Android实战技巧:用Pull方法解析XML文件

随机推荐

  1. Android 设计指南简体中文版
  2. Android(安卓)UI五大布局
  3. Android四大组件之Service
  4. [信息图表]iPhone和Android的系统生命周
  5. android mvp模式讲解
  6. Android的AndroidManifest.xml android:p
  7. android 图片处理 resize 探秘(图片缩放、
  8. 方便的Android無線管理軟體---remote web
  9. android MVC结构探讨
  10. Android学习札记49:在Android中使用Applic