【Android】(已更新解决方案)更新SDK版本(25)后,自定义FloatingActionButton的Behavior(跟随列表滑动显示隐藏)只隐藏不出现的问题

先贴 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 正常工作了。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值