阿里开源库VLayout的使用笔记

来源:互联网 发布:燕十八mysql优化 编辑:程序博客网 时间:2024/05/19 03:28

  VirtualLayout是阿里最近(2017.2)开源的一个用来辅助RecyclerView的LayoutManager扩展库,通过使用里面的LayoutHelper,我们可以轻松的使用一些比较复杂的布局,如:线性布局、Grid布局、固定布局、浮动布局、栏格布局、通栏布局、一拖N布局、一拖N布局、瀑布流布局。还可以组合使用这些布局。天猫APP里面的一些商品布局就是用到这个库。

Demo

  地址

使用步骤

  本笔记是基于VLayout的1.03版本写的,看的时候可以去它的Github官网了解一下。

   1. Gradle引入库:

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

  按我的理解,VLayout原则上并不是改变RecyclerView,而是在它的基础上给予辅助,所以使用步骤也是和RecyclerView差不多的,都是需要绑定一个LayoutManager和一个Adapter,VLayout在这里继承了RecyclerView的LayoutManager和一个Adapter,自定义出自己的VirtualLayoutManager和DelegateAdapter,而VirtualLayoutManager又引入了LayoutHelper来负责各种各样布局逻辑,所以接下来步骤为:
  
   2. 首先是绑定recyclerView(XML布局文件的recyclerView这里就不贴了)和VirtualLayoutManager:

        recyclerView = (RecyclerView) findViewById(R.id.rv);        //绑定VirtualLayoutManager        VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);        recyclerView.setLayoutManager(layoutManager);

  3. 然后就是设置所需要布局的LayoutHelper(这里以LinearLayoutHelper为例):

        //设置线性布局        LinearLayoutHelper linearLayoutHelper = new LinearLayoutHelper();        linearLayoutHelper.setItemCount(4);        linearLayoutHelper.setMarginBottom(100);

  4. 最后就是为LayoutHelper绑定Adapter,从而绑定数据(这里具体看后面的介绍)

DelegateAdapter的使用

  VLayout的一开始应用场景是天猫那种电商页面,所以基本上都是很多种布局组合使用的,所以要用到DelegateAdapter,单个布局的话我曾经试过用普通的Adapter也可以显示。
  在我看来,DelegateAdapter是VLayout专门为LayoutHelper定制的Adapter,虽然它不可以直接setLayoutHelpers绑定,但是它里面有一个继承自RecyclerView.Adapter的内部类Adapter可以绑定LayoutHelper,然后通过一个List把绑定好的Adapter打包起来,再放去DelegateAdapter,这样就可以实现组合使用不同的布局。
  使用DelegateAdapter首先就是要自定义一个它的内部类Adapter,让LayoutHelper和需要绑定的数据传进去(这里我为了方便直接在Adapter里面设置数据,其实如何传入数据并绑定VLayout和普通RecyclerView做法是一样的,这里就不说了),这里的Adapter和普通RecyclerView定义的Adapter只是相差了一个onCreateLayoutHelper()方法,其他的都是一样的做法:

public class MyAdapter extends DelegateAdapter.Adapter<MyAdapter.MainViewHolder> {    private Context context;    private LayoutHelper layoutHelper;    private RecyclerView.LayoutParams layoutParams;    private int count = 0;    public MyAdapter(Context context, LayoutHelper layoutHelper, int count) {        this(context, layoutHelper, count, new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300));    }    public MyAdapter(Context context, LayoutHelper layoutHelper, int count, @NonNull RecyclerView.LayoutParams layoutParams) {        this.context = context;        this.layoutHelper = layoutHelper;        this.count = count;        this.layoutParams = layoutParams;    }    @Override    public LayoutHelper onCreateLayoutHelper() {        return layoutHelper;    }    @Override    public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return new MainViewHolder(LayoutInflater.from(context).inflate(R.layout.list_item,parent,false));    }    @Override    public void onBindViewHolder(MainViewHolder holder, int position) {        holder.tv1.setText(Integer.toString(position));        if (position > 7) {            holder.itemView.setBackgroundColor(0x66cc0000 + (position - 6) * 128);        } else if (position % 2 == 0) {            holder.itemView.setBackgroundColor(0xaa22ff22);        } else {            holder.itemView.setBackgroundColor(0xccff22ff);        }    }    @Override    public int getItemCount() {        return count;    }    static class MainViewHolder extends RecyclerView.ViewHolder {        public TextView tv1;        public MainViewHolder(View itemView) {            super(itemView);            tv1 = (TextView) itemView.findViewById(R.id.item_tv1);        }    }}

  自定义好Adapter之后就是绑定Adapter和LayoutHelper然后打包放入delegateAdapter了:

        //设置Adapter列表        List<DelegateAdapter.Adapter> adapters = new LinkedList<>();        adapters.add(new Adapter(this,linearLayoutHelper,4){            @Override            public void onBindViewHolder(MainViewHolder holder, int position) {                super.onBindViewHolder(holder, position);                if (position == 0) {                    holder.tv1.setText("linearLayout");                }            }        });        //绑定delegateAdapter        DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager);        delegateAdapter.setAdapters(adapters);        recyclerView.setAdapter(delegateAdapter);

  这里重写Adapter的onBindViewHolder()方法是因为我的demo里面展示了多种布局的组合,而我又懒得一个个设置数据然后绑定进去,所以直接在这里设置数据了。

LayoutHelper的使用

  VLayout的1.03版本有9种布局,分别对应9种LayoutHelper,下面一一分析(不过每种LayoutHelper只具体分析一些比较常见和特别(有bug)的方法,具体详细的方法可以看官方API,虽然我觉得那讲得不是很有条理,但是用来查询还是很好的):

LinearLayoutHelper——线性布局

  这里的线性布局,就是普通的Item从上往下排,继承于BaseLayoutHelper,没有什么特别的方法:

        //设置线性布局        LinearLayoutHelper linearLayoutHelper = new LinearLayoutHelper();        //设置Item个数        linearLayoutHelper.setItemCount(5);        //设置间隔高度        linearLayoutHelper.setDividerHeight(1);        //设置布局底部与下个布局的间隔        linearLayoutHelper.setMarginBottom(100);

ps:要注意的是setItemCount()方法设置的Item数量如果与Adapter的getItemCount()方法返回的数量不同,会取决于后者。setDividerHeight()设置的间隔会与RecyclerView的addItemDecoration()添加的间隔叠加.

GridLayoutHelper——Grid布局

  Grid布局,可以通过设置spanCount属性来设置每行个数(1-5),或者通过设置自定义的SpanSizeLookup来控制每行的个数(SpanSizeLookup可以定义某些位置的Item的占格数):

        //设置Grid布局        GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(4);        //是否自动扩展        gridLayoutHelper.setAutoExpand(false);        //自定义设置某些位置的Item的占格数        gridLayoutHelper.setSpanSizeLookup(new GridLayoutHelper.SpanSizeLookup() {            @Override            public int getSpanSize(int position) {                if (position > 13) {                    return 2;                }else {                    return 1;                }            }        });


  这里要关了自动扩展,这有一个bug,最后一行如果发生扩展的话会到时最后一个Item会扩展然后再宽度乘2(已经在GitHub上反映给作者,作者也做出了完善——在后面的版本里,在两者冲突的情况下,只支持setSpanSizeLookup())。
  还有可以通过vGap和hGap的属性来控制横向和纵向的间隔,这个具体可以看文档。

FixLayoutHelper——固定布局

  固定布局,顾名思义就是固定在一个地方不动的布局,创建的时候需要设置alignType(表示吸边时的基准位置,默认左上角,有四个取值,分别是TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT)和偏移量x,y,具体意思可以看官方给出的这张图:

FixLayoutHelper fixLayoutHelper = new FixLayoutHelper(FixLayoutHelper.TOP_LEFT,0,0);


  从它的源码:

    @Override    public void setItemCount(int itemCount) {        if (itemCount > 0) {            super.setItemCount(1);        } else {            super.setItemCount(0);        }    }

  可以看出,它只能设置一个Item。

ScrollFixLayoutHelper——固定布局

  这个也是固定布局,而且使继承自FixLayoutHelper的,特性都继承了上面的,比上面多出来的功能就是可以通过设置showType来决定这个布局的Item是否显示,可以用来做一些返回顶部之类的按钮:
- SHOW_ALWAYS:与FixLayoutHelper的行为一致,固定在某个位置;
- SHOW_ON_ENTER:默认不显示视图,当页面滚动到这个视图的位置的时候,才显示;
- SHOW_ON_LEAVE:默认不显示视图,当页面滚出这个视图的位置的时候显示;

        //设置固定布局        ScrollFixLayoutHelper scrollFixLayoutHelper = new ScrollFixLayoutHelper(ScrollFixLayoutHelper.TOP_RIGHT,0,0);        scrollFixLayoutHelper.setShowType(ScrollFixLayoutHelper.SHOW_ON_ENTER);

FloatLayoutHelper——浮动布局

  浮动布局,就是可以拖动的Item,这个也有alignType(表示吸边时的基准位置,默认左上角,有四个取值,分别是TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT)和偏移量x,y

        //设置浮动布局        FloatLayoutHelper floatLayoutHelper = new FloatLayoutHelper();        //设置初始位置        floatLayoutHelper.setDefaultLocation(20,250);

  注意,这里有一个不知道算bug的问题,FloatLayoutHelper设置的Item必须要在屏幕滚动到它那个实际原始位置,加载之后才能被拖动,像我这个例子,我是在线性布局和Gird布局之后才初始化这个浮动Item的,所以要屏幕滚动这个位置,才能拖动:

ColumnLayoutHelper——栏格布局

  栏格布局,就是只有一栏的布局,这一栏可以设置多个Item,但是需要有对应的Weight属性,Weight属性在这里就和LinearLayout水平排列时的的Weight属性差不多: weights属性是一个float数组,每一项代表某一列占父容器宽度的百分比,总和建议是100,否则布局会超出容器宽度;如果布局中有4列,那么weights的长度也应该是4;长度大于4,多出的部分不参与宽度计算;如果小于4,不足的部分默认平分剩余的空间。

        ColumnLayoutHelper columnLayoutHelper = new ColumnLayoutHelper();        columnLayoutHelper.setItemCount(5);        columnLayoutHelper.setWeights(new float[]{30,10,30,20,10});        columnLayoutHelper.setMarginBottom(100);

SingleLayoutHelper——通栏布局

  通栏布局,就是一个单独的Item,好像没有什么特别的。

OnePlusNLayoutHelper——一拖N布局

  一拖N布局,字面上是很难看出这是什么,但是看源码上的注释就一目了然了:

/** * <pre> * Currently support 1+3(max) layout * 1 + 0                        1 + 1  * -------------------------    -------------------------  * |                       |    |           |           |  * |                       |    |           |           |  * |           1           |    |           |           |  * |                       |    |     1     |     2     |  * |                       |    |           |           |  * |                       |    |           |           |  * -------------------------    ------------------------- *                               * * 1 + 2                        1 + 3 * -------------------------    ------------------------- * |           |           |    |           |           | * |           |     2     |    |           |     2     | * |           |           |    |           |           | * |     1     |-----------|    |     1     |-----------| * |           |           |    |           |     |     | * |           |     3     |    |           |  3  |  4  | * |           |           |    |           |     |     | * -------------------------    ------------------------- *                              *  1 + 4 * ------------------------- * |           |           | * |           |     2     | * |           |           | * |     1     |-----------| * |           |   |   |   | * |           | 3 | 4 | 5 | * |           |   |   |   | * ------------------------- * </pre> * * @author villadora * @since 1.0.0 */

  就是这样,根据Item的数目改变而改变布局,最大数量是一拖四也就是五个。

        //设置一拖N布局        OnePlusNLayoutHelper onePlusNLayoutHelper = new OnePlusNLayoutHelper(5);        onePlusNLayoutHelper.setMarginBottom(100);

StickyLayoutHelper——stikcy布局

  stikcy布局,根据stickyStart属性,当视图的位置在屏幕范围内时,视图会随页面滚动而滚动;当视图的位置滑出屏幕时,StickyLayoutHelper会将视图固定在顶部(stickyStart = true)或者底部(stickyStart = false),固定的位置支持设置偏移量offset。QQ的联系人分组那里的功能就很像这个。

        //设置Sticky布局        StickyLayoutHelper stickyLayoutHelper = new StickyLayoutHelper();        stickyLayoutHelper.setStickyStart(false);


这是吸底,吸顶的有个bug,返回去的时候会被覆盖住:

StaggeredGridLayoutHelper——瀑布流布局

  瀑布流布局,和Gird相似,但是Gird布局是每一栏的Item高度是要相等的,而这里是可以调整的:

        //设置瀑布流布局        StaggeredGridLayoutHelper staggeredGridLayoutHelper = new StaggeredGridLayoutHelper();        staggeredGridLayoutHelper.setLane(3);        staggeredGridLayoutHelper.setHGap(5);        staggeredGridLayoutHelper.setVGap(5);        staggeredGridLayoutHelper.setMarginBottom(100);        adapters.add(new MyAdapter(this,staggeredGridLayoutHelper,31){            @Override            public void onBindViewHolder(MainViewHolder holder, int position) {                super.onBindViewHolder(holder, position);                ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,150 +position % 5 * 20);                holder.itemView.setLayoutParams(layoutParams);                if (position == 0) {                    holder.tv1.setText("staggeredGridLayout");                }            }        });

总结

  阿里公开的这个VLayout功能还是很强大的,但是还是有不少bug的,大家用之前可以去Github的官方的Issues里面看看有什么bug,改进了什么,确定是有bug也可以在上面发。

2 0