99

VLayout全面解析

 6 years ago
source link: http://mp.weixin.qq.com/s/NDkcq6LsyxfA8SZZHE3EFg
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

VLayout全面解析

Original By 码农职场 2017-11-14 00:00 Posted on

这个开源其实在刚出来的时候我就在csdn进行了分享,可能有的人看过,可能有的人没看过,所以我决定,在分享一次。

前不久,阿里新开源了2个东西,Atlas和vlayout。今天我来介绍下vlayout的使用。在介绍前,先抱怨两句,阿里放开源出来,感觉就是让我们这群人给他们找bug~~我曾遇到一个奇怪的问题,然后一直以为自己写的有问题,结果去down了官方demo跑了一下,结果官方的demo居然并没有做这个效果~不解!!好了,话不多说,我会通过官方的介绍以及自己的写的demo一一介绍。先放上官方的github地址:https://github.com/alibaba/vlayout

Vlayout简介

vlayout全称VirtualLayout,它是一个针对RecyclerView的LayoutManager扩展, 主要提供一整套布局方案和布局间的组件复用的问题。它通过定制化的LayoutManager,接管整个RecyclerView的布局逻辑;LayoutManager管理了一系列LayoutHelper,LayoutHelper负责具体布局逻辑实现的地方;每一个LayoutHelper负责页面某一个范围内的组件布局;不同的LayoutHelper可以做不同的布局逻辑,因此可以在一个RecyclerView页面里提供异构的布局结构,这就能比系统自带的LinearLayoutManager、GridLayoutManager等提供更加丰富的能力。同时支持扩展LayoutHelper来提供更多的布局能力。

主要功能

  • 默认通用布局实现,解耦所有的View和布局之间的关系: Linear, Grid, 吸顶, 浮动, 固定位置等1:LinearLayoutHelper: 线性布局2:GridLayoutHelper: Grid布局, 支持横向的colspan3:FixLayoutHelper: 固定布局,始终在屏幕固定位置显示4:ScrollFixLayoutHelper: 固定布局,但之后当页面滑动到该图片区域才显示, 可以用来做返回顶部或其他书签等5:FloatLayoutHelper: 浮动布局,可以固定显示在屏幕上,但用户可以拖拽其位置6:ColumnLayoutHelper: 栏格布局,都布局在一排,可以配置不同列之间的宽度比值7:SingleLayoutHelper: 通栏布局,只会显示一个组件View8:OnePlusNLayoutHelper: 一拖N布局,可以配置1-5个子元素9:StickyLayoutHelper: stikcy布局, 可以配置吸顶或者吸底10:StaggeredGridLayoutHelper: 瀑布流布局,可配置间隔高度/宽度

  • 上述默认实现里可以大致分为两类:一是非fix类型布局,像线性、Grid、栏格等,它们的特点是布局在整个页面流里,随页面滚动而滚动;另一类就是fix类型的布局,它们的子节点往往不随页面滚动而滚动。

  • 所有除布局外的组件复用,VirtualLayout将用来管理大的模块布局组合,扩展了RecyclerView,使得同一RecyclerView内的组件可以复用,减少View的创建和销毁过程。

使用方法

版本请参考mvn repository上的最新版本(目前最新版本是1.0.3),最新的 aar 都会发布到 jcenter 和 MavenCentral 上,确保配置了这两个仓库源,然后引入aar依赖:

compile ('com.alibaba.android:vlayout:1.0.3@aar') {
    transitive = true
}

或者maven

<dependency>
  <groupId>com.alibaba.android</groupId>
  <artifactId>vlayout</artifactId>
  <version>1.0.3</version>
  <type>aar</type>
</dependency>

LayoutHelper功能介绍

margin, padding

Margin, padding就是外边距、内边距,概念与Android系统的margin, padding一样,但也有不同的地方:

  • 它不是整个RecyclerView页面的margin和padding,它是每一块LayoutHelper所负责的区域的margin和padding。

  • 一个页面里可以有多个LayoutHelper,意味着不同LayoutHelper可以设置不同的margin和padding。

  • LayoutHelper的margin和padding与页面RecyclerView的margin和padding可以共存。

  • 目前主要针对非fix类型的LayoutHelper实现了margin和padding,fix类型LayoutHelper内部没有相对位置关系,不处理边距。

    Image

对于LayoutHelper,调用

public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding)

public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin)

bgColor, bgImg

背景颜色或者背景图,这其实不是布局属性,但是由于在vlayout对视图进行了直接布局,不同区域的视图的父节点都是RecyclerView,如果想要针对某一块区域单独绘制背景,就很难做到了。vlayout框架对此做了特殊处理,对于非fix、非float类型的LayoutHelper,支持配置背景色或背景图。同样目前主要针对非fix类型的LayoutHelper实现这个特性。

Image

   使用背景色

public void setBgColor(int bgColor)

使用背景图首先为LayoutManager提供一个ImageView简单工厂

this.mLayoutManager.setLayoutViewFactory(new LayoutViewFactory() {
            @Override
            public opinion generateLayoutView(@NonNull Context context) {
                return new XXImageView(context);
            }
        });

再为LayoutHelper提设置图片加载的Listener

baseHelper.setLayoutViewBindListener(new BindListener(imgUrl));
baseHelper.setLayoutViewUnBindListener(new UnbindListener(imgUrl));

private static class BindListener implements BaseLayoutHelper.LayoutViewBindListener {
        private String imgUrl;

        public BindListener(String imgUrl) {
            this.imgUrl = imgUrl;
        }

        @Override
        public void onBind(View layoutView, BaseLayoutHelper baseLayoutHelper) {
            //loading image
        }
    }

private static class UnbindListener implements BaseLayoutHelper.LayoutViewUnBindListener {
    private String imgUrl;

    public UnbindListener(String imgUrl) {
        this. imgUrl = imgUrl;
    }

    @Override
    public void onUnbind(View layoutView, BaseLayoutHelper baseLayoutHelper) {
            //cancel loading image
    }
}

aspectRatio

为了保证布局过程中视图的高度一致,我们设计了aspectRatio属性,它是宽与高的比例,LayoutHelper里有aspectRatio属性,通过vlayout添加的视图的LayoutParams也有aspectRatio属性,后者的优先级比前者高,但含义不一样。

  • LayoutHelper定义的aspectRatio,指的是一行视图整体的宽度与高度之比,当然整体的宽度是减去了RecyclerView和对应的LayoutHelper的margin, padding。

  • 视图的LayoutParams定义的aspectRatio,指的是在LayoutHelper计算出视图宽度之后,用来确定视图高度时使用的,它会覆盖通过LayoutHelper的aspectRatio计算出来的视图高度,因此具备更高优先级。

    Image

对于LayoutHelper,调用

public void setAspectRatio(float aspectRatio)

对于LayoutParams,调用

((VirutalLayoutManager.LayoutParams) layoutParams).mAspectRatio

dividerHeight

LinearLayoutHelper的属性,LinearLayoutHelper是像ListView一样的线性布局,dividerHeight就是每个组件之间的间距。

Image

对于LinearLayoutHelper,调用

public void setDividerHeight(int dividerHeight)

weights

ColumnLayoutHelper, GridLayoutHelper的属性,它们都是提供网格状的布局能力,建议使用GridLayoutHelper,它的能力更加强大,参考下文介绍。默认情况下,每个网格中每一列的宽度是一样的,通过weights属性,可以指定让每一列的宽度成比例分配,就像LinearLayout的weight属性一样。 weights属性是一个float数组,每一项代表某一列占父容器宽度的百分比,总和建议是100,否则布局会超出容器宽度;如果布局中有4列,那么weights的长度也应该是4;长度大于4,多出的部分不参与宽度计算;如果小于4,不足的部分默认平分剩余的空间。

Image

对于ColumnLayoutHelper, GridLayoutHelper,调用

public void setWeights(float[] weights)

vGap, hGap

GridLayoutHelper与StaggeredGridLayoutHelper都有这两个属性,分别控制视图之间的垂直间距和水平间距。

Image

对于GridLayoutHelper, StaggeredGridLayoutHelper,调用

public void setHGap(int hGap)

public void setVGap(int vGap)

spanCount, spanSizeLookup

GridLayoutHelper的属性,参考于系统的GridLayoutManager,spanCount表示网格的列数,默认情况下每一个视图都占用一个网格区域,但通过提供自定义的spanSizeLookUp,可以指定某个位置的视图占用多个网格区域。

Image

使用spanCount调用

public void setSpanCount(int spanCount)

使用spanSizeLookup

public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup)

autoExpand

GridLayoutHelper的属性,当一行里视图的个数少于spanCount值的时候,如果autoExpand为true,视图的总宽度会填满可用区域;否则会在屏幕上留空白区域。

Image
public void setAutoExpand(boolean isAutoExpand)

StaggeredGridLayoutHelper中有这个属性,与GridLayoutHelper里的spanCount类似,控制瀑布流的列数。

public void setLane(int lane)

fixAreaAdjuster

fix类型的LayoutHelper,在可能需要设置一个相对父容器四个边的偏移量,比如整个页面里有一个固定的标题栏添加在vlayout容器上,vlayout内部的fix类型视图不希望与外部的标题有所重叠,那么就可以设置一个fixAreaAdjuster来做偏移。

Image
public void setAdjuster(FixAreaAdjuster adjuster)

alignType, x, y

FixLayoutHelper, ScrollFixLayoutHelper, FloatLayoutHelper的属性,表示吸边时的基准位置,有四个取值,分别是TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT。x和y是相对这四个位置的偏移量,最终的偏移量还要受上述的fixAreaAdjuster影响。

  • TOP_LEFT:基准位置是左上角,x是视图左边相对父容器的左边距偏移量,y是视图顶边相对父容器的上边距偏移量;

  • TOP_RIGHT:基准位置是右上角,x是视图右边相对父容器的右边距偏移量,y是视图顶边相对父容器的上边距偏移量;

  • BOTTOM_LEFT:基准位置是左下角,x是视图左边相对父容器的左边距偏移量,y是视图底边相对父容器的下边距偏移量;

  • BOTTOM_RIGHT:基准位置是右下角,x是视图右边相对父容器的右边距偏移量,y是视图底边相对父容器的下边距偏移量;

Image

设置基准调用

public void setAlignType(int alignType)

设置偏移量调用

public void setX(int x)

public void setY(int y)

showType

ScrollFixLayoutHelper的属性,取值有SHOW_ALWAYS, SHOW_ON_ENTER, SHOW_ON_LEAVE。

  • SHOW_ALWAYS:与FixLayoutHelper的行为一致,固定在某个位置;

  • SHOW_ON_ENTER:默认不显示视图,当页面滚动到这个视图的位置的时候,才显示;

  • SHOW_ON_LEAVE:默认不显示视图,当页面滚出这个视图的位置的时候显示;

    Image
public void setShowType(int showType)

stickyStart, offset

StickyLayoutHelper的属性,当视图的位置在屏幕范围内时,视图会随页面滚动而滚动;当视图的位置滑出屏幕时,StickyLayoutHelper会将视图固定在顶部(stickyStart = true)或者底部(stickyStart = false),固定的位置支持设置偏移量offset。

Image
public void setStickyStart(boolean stickyStart)

public void setOffset(int offset)



实例演示

上面我们已经详细介绍的各种LayoutHelper以及它的各种属性,现在,我们通过demo来进行实例演示。

LinearLayoutHelper

我们activity只要进行一些简单的配置就可以了:

  VirtualLayoutManager manager = new VirtualLayoutManager(this);
        recycler.setLayoutManager(manager);
        DelegateAdapter adapter = new DelegateAdapter(manager, true);
        adapter.addAdapter(new DelegateRecyclerAdapter(this,new LinearLayoutHelper()));
        recycler.setAdapter(adapter);

对于adapter ,我们继承DelegateAdapter来实现,代码很简单,如下:


 public LayoutHelper onCreateLayoutHelper() {
        return helper;
    }

    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyViewholder(inflater.inflate(R.layout.item, parent, false));
    }

    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((MyViewholder) holder).text.setText(position + 1 + "");
    }

    public int getItemCount() {
        return 60;
    }

    public class MyViewholder extends RecyclerView.ViewHolder {

        private TextView text;

        public MyViewholder(View view) {
            super(view);
            text = (TextView) view.findViewById(R.id.text);
        }
    }

效果图:

Image

GridLayoutHelper

我们只要把LinearLayouthelper改成Gridlayouthelper就可以了:

  VirtualLayoutManager manager = new VirtualLayoutManager(this);
        recycler.setLayoutManager(manager);
        DelegateAdapter adapter = new DelegateAdapter(manager, true);
        adapter.addAdapter(new DelegateRecyclerAdapter(this,new GridLayoutHelper(3)));
        recycler.setAdapter(adapter);

效果图:

Image

StaggeredGridLayoutHelper

还是直接修改LayoutHelper就可以了:

     VirtualLayoutManager manager = new VirtualLayoutManager(this);
        recycler.setLayoutManager(manager);
        DelegateAdapter adapter = new DelegateAdapter(manager, true);
        //StaggeredGridLayoutHelper(int num,int gap)
        //num为每行显示数目,gap为两个item的边距
        adapter.addAdapter(new StaggeredAdapter(this,new StaggeredGridLayoutHelper(3,20)));
        recycler.setAdapter(adapter);

为了做成瀑布流的效果,我们对每个item进行一个随机高度的设置:

    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ViewGroup.LayoutParams layoutParams = ((MyViewholder) holder).text.getLayoutParams();
        layoutParams.height = 260 + position % 7 * 20;
        ((MyViewholder) holder).text.setLayoutParams(layoutParams);
        ((MyViewholder) holder).text.setText(position + 1 + "");
    }

效果图:

Image

FixLayoutHelper

对于fixlayout类型的,我们需要先后添加一次LinearLayoutHelper和FixLayoutHelper。

        VirtualLayoutManager manager = new VirtualLayoutManager(this);
        recycler.setLayoutManager(manager);
        DelegateAdapter adapter = new DelegateAdapter(manager, true);
        adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));
        adapter.addAdapter(new ScrollFixAdapter(this, new FixLayoutHelper(FixLayoutHelper.BOTTOM_LEFT, 200, 200), recycler));
        recycler.setAdapter(adapter);

效果图:

Image

ScrollFixLayoutHelper

同上,代码是差不多的,不过官方所说的标签,置顶等功能。并不能实现。官方demo也并没有实现此功能。虽然我们可以通过点击图片来进行置顶。但是具体功能感觉和fixlayout无异。文末有demo。博友们自己下载试试就知道了。

ColumnLayoutHelper

代码如下:

        VirtualLayoutManager manager = new VirtualLayoutManager(this);
        recycler.setLayoutManager(manager);
        DelegateAdapter adapter = new DelegateAdapter(manager, true);
        adapter.addAdapter(new DelegateRecyclerAdapter(this,new ColumnLayoutHelper()));
        recycler.setAdapter(adapter);

效果图:

Image

SingleLayoutHelper

    VirtualLayoutManager manager = new VirtualLayoutManager(this);
    recycler.setLayoutManager(manager);
    DelegateAdapter adapter = new DelegateAdapter(manager, true);
    adapter.addAdapter(new DelegateRecyclerAdapter(this,new SingleLayoutHelper()));
    recycler.setAdapter(adapter);

效果图:

Image

OnePlusNLayoutHelper

一拖N布局,听起来感觉高大上,不过我并不知道这玩意能用在什么地方.....

    VirtualLayoutManager manager = new VirtualLayoutManager(this);
    recycler.setLayoutManager(manager);
    DelegateAdapter adapter = new DelegateAdapter(manager, true);
    OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper(3);
    adapter.addAdapter(new OnePlusNRecyclerAdapter(this,helper));
    recycler.setAdapter(adapter);

效果图:

Image

FloatLayoutHelper

        VirtualLayoutManager manager = new VirtualLayoutManager(this);
        recycler.setLayoutManager(manager);
        DelegateAdapter adapter = new DelegateAdapter(manager, true);
        adapter.addAdapter(new FloatAdapter(this,new FloatLayoutHelper()));
        adapter.addAdapter(new DelegateRecyclerAdapter(this,new LinearLayoutHelper()));
        recycler.setAdapter(adapter);

效果图:

Image

StickyLayoutHelper

这个吸顶和吸底效果还是比较强大,自我感觉可以深入研究的就这个和FloatLayoutHelper了。具体代码如下:


        VirtualLayoutManager manager = new VirtualLayoutManager(this);         
       recycler.setLayoutManager(manager);        
       DelegateAdapter adapter = new DelegateAdapter(manager, true);         //在顶部时需先添加sticklayout,在底部时最后添加sticklayout        
       StickyLayoutHelper helper = new StickyLayoutHelper(true);
       //adapter.addAdapter(new StickRecyclerAdapter(this, helper, recycler));
       //adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));          
       //顶部和实体合二为一        
       adapter.addAdapter(new DelegateRecyclerAdapter(this, helper));        
       adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));          
       //底部
       //StickyLayoutHelper helper = new StickyLayoutHelper(false);
       //adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));
       //adapter.addAdapter(new StickRecyclerAdapter(this, helper));        
       recycler.setAdapter(adapter);

效果图:顶部:

Image

实体和顶部合二为一:

Image

总结

对于这个开源,我的总结就是,每个开源都有它的强大之处,至于我们会不会发现就看我们如何去理解了。而且这个开源可以多个LayoutHelper进行结合,比之前那些LinearLayoutmanager、 Gridlayoutmanager之类的强大太多。我感觉有了这个。我之前那个SWPullRecyclerLayout可以在精炼,进行多方面布局的结合来使用。现在,我们已经学会如何使用它的。之后我想我们就应该试着去看它的源码来了解它是如何实现的。因为我看了下源码,这个开源15年就开始做的。我记得recyclerview也是15年4月才发布出来。可想而知,阿狸团队的强大。

demo的源码我已上传到csdn:点击下载demo

推荐阅读

Gradle的快速构建

MVP时尚版

MVP之乞丐版的自我救赎

Image

分享是一种美德,关注是一种智慧。Image

我就是马云飞

长按识别二维码,关注公众号


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK