android嵌套滚动与协调滚动的实现方式汇总-kb88凯时官网登录

来自:网络
时间:2022-12-26
阅读:
免费资源网 - https://freexyz.cn/
目录

android的协调滚动的几种实现方式

,我们讲了嵌套滚动的实现方式,为什么有了嵌套滚动还需要协调滚动这种方式呢?(不细讲原理,本文只探讨实现的方式与步骤!)

那在一些细度化的操作中,如我们需要一些控件随着滚动布局做一些粒度比较小的动画、移动等操作,那么我们就需要监听滚动,然后改变当前控件的属性。

如何实现这种协调滚动的布局呢?我们使用 coordinatorlayout appbarlayout 或者 coordinatorlayout behavior 实现,另一种方案是 motionlayout。我们看看都是怎么实现的吧。

一、coordinatorlayout behavior

coordinatorlayout 顾名思义是协调布局,其原理很简单,在onmeasure()的时候保存childview,通过 predrawlistener监听childview的变化,最终通过双层for循环找到对应的behavior,分发任务即可。coordinatorlayout实现了nestedscrollingparent2,那么在childview实现了nestedscrollingchild方法时候也能解决滑动冲突问题。

而behavior就是一个应用于view的观察者模式,一个view跟随者另一个view的变化而变化,或者说一个view监听另一个view。

在behavior中,被观察view 也就是事件源被称为denpendcy,而观察view,则被称为child。

一般自定义behavior来说分两种情况:

  • 监听另一个view的状态变化,例如大小、位置、显示状态等
  • 监听coordinatorlayout里的滑动状态

这里我们以之前的效果为主来实现自定义的behavior,先设置nestedscrollview在imageview下面:

public class myscrollbehavior extends viewoffsetbehavior {
    private int topimgheight;
    private int toptextheight;
    public myscrollbehavior(context context, attributeset attrs) {
        super(context, attrs);
    }
    @override
    public boolean layoutdependson(@nonnull coordinatorlayout parent, @nonnull nestedscrollview child,
                                   @nonnull view dependency) {
        return dependency instanceof imageview ;
    }
    @override
    protected void layoutchild(coordinatorlayout parent, nestedscrollview child, int layoutdirection) {
        super.layoutchild(parent, child, layoutdirection);
        if (topimgheight == 0) {
            final list dependencies = parent.getdependencies(child);
            for (int i = 0, z = dependencies.size(); i < z; i  ) {
                view view = dependencies.get(i);
                if (view instanceof imageview) {
                    topimgheight = view.getmeasuredheight();
                } 
            }
        }
        child.settop(topimgheight);
        child.setbottom(child.getbottom()   topimgheight);
    }
}

然后设置监听coordinatorlayout里的滑动状态,imageview做同样的滚动

public class myimagebehavior extends coordinatorlayout.behavior {
    private int topbarheight = 0;  //负图片高度
    private int downendy = 0;   //默认为0
    public myimagebehavior(context context, attributeset attrs) {
        super(context, attrs);
    }
    @override
    public boolean onstartnestedscroll(@nonnull coordinatorlayout coordinatorlayout,
                                       @nonnull view child, @nonnull view directtargetchild,
                                       @nonnull view target, int axes, int type) {
        //监听垂直滚动
        return (axes & viewcompat.scroll_axis_vertical) != 0;
    }
    @override
    public void onnestedprescroll(@nonnull coordinatorlayout coordinatorlayout, @nonnull view child,
                                  @nonnull view target, int dx, int dy, @nonnull int[] consumed, int type) {
        if (topbarheight == 0) {
            topbarheight = -child.getmeasuredheight();
        }
        float transy = child.gettranslationy() - dy;
        //处理上滑
        if (dy > 0) {
            if (transy >= topbarheight) {
                translationbyconsume(child, transy, consumed, dy);
                translationbyconsume(target, transy, consumed, dy);
            } else {
                translationbyconsume(child, topbarheight, consumed, (child.gettranslationy() - topbarheight));
                translationbyconsume(target, topbarheight, consumed, (child.gettranslationy() - topbarheight));
            }
        }
        if (dy < 0 && !target.canscrollvertically(-1)) {
            //处理下滑
            if (transy >= topbarheight && transy <= downendy) {
                translationbyconsume(child, transy, consumed, dy);
                translationbyconsume(target, transy, consumed, dy);
            } else {
                translationbyconsume(child, downendy, consumed, (downendy - child.gettranslationy()));
                translationbyconsume(target, downendy, consumed, (downendy - child.gettranslationy()));
            }
        }
    }
    @override
    public boolean onnestedfling(@nonnull coordinatorlayout coordinatorlayout, @nonnull view child, @nonnull view target, float velocityx, float velocityy, boolean consumed) {
        return super.onnestedfling(coordinatorlayout, child, target, velocityx,
                velocityy, consumed);
    }
    private void translationbyconsume(view view, float translationy, int[] consumed, float consumeddy) {
        consumed[1] = (int) consumeddy;
        view.settranslationy(translationy);
    }
}

分别为imageview和nestedscrollview设置对应的 behavior。



    
    
        
        
        
            
        
    

我们先把textview隐藏先不处理textview。效果如下:

这样我们就实现了自定义 behavior 监听滚动的实现。那么我们加上textview 的 behavior 监听imageview的滚动,做对应的滚动。

先修改 myscrollbehavior 让他在imageview和textview下面

public class myscrollbehavior extends viewoffsetbehavior {
    private int topimgheight;
    private int toptextheight;
    public myscrollbehavior(context context, attributeset attrs) {
        super(context, attrs);
    }
    @override
    public boolean layoutdependson(@nonnull coordinatorlayout parent, @nonnull nestedscrollview child,
                                   @nonnull view dependency) {
        return dependency instanceof imageview || dependency instanceof textview ;
    }
    @override
    protected void layoutchild(coordinatorlayout parent, nestedscrollview child, int layoutdirection) {
        super.layoutchild(parent, child, layoutdirection);
        if (topimgheight == 0) {
            final list dependencies = parent.getdependencies(child);
            for (int i = 0, z = dependencies.size(); i < z; i  ) {
                view view = dependencies.get(i);
                if (view instanceof imageview) {
                    topimgheight = view.getmeasuredheight();
                } else if (view instanceof textview) {
                    toptextheight = view.getmeasuredheight();
                    view.settop(topimgheight);
                    view.setbottom(view.getbottom()   topimgheight);
                }
            }
        }
        child.settop(topimgheight   toptextheight);
        child.setbottom(child.getbottom()   topimgheight   toptextheight);
    }
}

然后设置监听imageview的滚动:

public class mytextbehavior extends coordinatorlayout.behavior {
    private int imgheight;
    public mytextbehavior(context context, attributeset attrs) {
        super(context, attrs);
    }
    @override
    public boolean layoutdependson(@nonnull coordinatorlayout parent, @nonnull view child, @nonnull view dependency) {
        return dependency instanceof imageview;
    }
    @override
    public boolean ondependentviewchanged(@nonnull coordinatorlayout parent, @nonnull view child, @nonnull view dependency) {
        //跟随imageview滚动,imageview滚动多少我滚动多少
        float translationy = dependency.gettranslationy();
        if (imgheight == 0) {
            imgheight = dependency.getheight();
        }
        float offsettranslationy = imgheight   translationy;
        child.settranslationy(offsettranslationy);
        return true;
    }
}

xml修改如下:



    
    
        
        
        
            
        
    

ok,修改完成之后我们看看最终的效果:

看到上面的示例,我们把常用的几种 behavior 都使用了一遍,系统的viewoffsetbehavior 和监听滚动的 behavior 监听view的 behavior。

为了实现这么一个简单的效果就用了这么多类,这么复杂。我分分钟就能实现!

行行,我知道你厉害,这不是为了演示同样的效果,使用不同的方式实现嘛。通过 behavior 可以实现一些嵌套滚动不能完成的效果,比如鼎鼎大名的支付宝kb88凯时d88尊龙官网手机app官网登录首页效果,美团详情效果等。behavior 更加的灵活,控制的粒度也更加的细。

但是如果只是简单实现上面的效果,我们可以用 appbarlayout 内部自带的 behavior 也能实现类似的效果,appbarlayout内部已经封装并使用了 behavior 。我们看看如何实现。

二、coordinatorlayout appbarlayout

其实内部也是基于 behavior 实现的,内部实现为 headerbehavior 和 headerscrollingviewbehavior 。

对一些场景使用进行了封装,滚动效果,吸顶效果,折叠效果等。我们看看同样的效果,使用 appbarlayout 如何实现吧:



    
    
        
            
            
        
        
            
        
    

效果:

so easy ! 真的是太方便了,类似的效果我们都能使用 appbarlayout 来实现,比如一些详情页面顶部图片,下面列表或viewpager的都可以使用这种方式,更加的便捷。

三、motionlayout

不管怎么说,appbarlayout 只能实现一些简单的效果,如果想要一些粒度比较细的效果,我们还得使用自定义 behavior 来实现,但是它的实现确实是有点复杂,2019年谷歌推出了 motionlayout 。

淘宝的出现可以说让世上没有难做的生意,那么 motionlayout 的出现可以说让 android 没有难实现的动画了。不管是动画效果,滚动效果,motionlayout 绝杀!能用 behavior 实现的 motionlayout 几乎是都能做。

使用 motionlayout 我们只需要定义起始点和结束点就行了,我们这里不需要根据百分比fram进行别的操作,所以只定义最简单的使用。

我们看看如何用 motionlayout 实现同样的效果:



    
    
        
        
        
            
        
    

定义的scene_scroll_13.xml



    
        
    
    
        
        
        
    
    
        
        
        
    

效果:

非常的简单,效果很流畅,性能也很好。有时候都不得不感慨一句,有了 motionlayout 要你 behavior 何用。

总结

android真的是太卷了,以前学rxjava dagger2 nestedscrolling behavior 等,这些都是很难学的,更难以应用,如果能学会,那都是高工了。现在谷歌新框架层出不穷,越来越易用了,越来越好入门了。以前学的都已经被淘汰,新入android的同学已经可以无需门槛,直接学谷歌的脚手架就能完成效果了。

言归正传,这几种方案大家都理解了吗?什么时候需要用协调滚动,什么时候需要用嵌套滚动,大家可以做到心中有数。能用 motionlayout 的还是推荐使用 motionlayout 实现,毕竟实现简单,性能优秀嘛!

当然如果仅限这种效果来说,还有很多的方式实现如rv listview,纯粹的自定义view也能实现是吧,自定义viewgroup,viewdraghelper一样能实现,就是稍微麻烦点,这里也仅从嵌套滚动和协调滚动这点来实现的。

好了,如果大家理解了协调滚动和嵌套滚动,那万变不离其宗,几乎应用开发中全部的滚动效果都是基于这两条,内部的具体实现方案几乎都是基于这6种方案来实现。

后面如果大家有兴趣,我会出一期超复杂的嵌套具体实现相关的功能,类似美团外卖点餐的页面分为上、中、下布局。下布局又分左右列表布局 ,还分上布局抽屉效果和中布局吸顶效果。

免费资源网 - https://freexyz.cn/
返回顶部
顶部
网站地图