项目需求讨论-Vlayout来快速构建及扩展复杂界面

大家好,今天又带来了项目中具体遇到的需求。做一个首界面,该首界面有很多功能块,同时这些功能块是动态的,因为登录的人的权限的不同,会显示不同的功能块,因为功能模块的数量不一定,所以当功能块多的时候,整个界面是可以上下滑动的。其实类似有点像淘宝的首界面。如下图所示。

界面


首先我说的不是最优的。可能有更好的解决方式。我这里也只是尝试了新的方式而已。

我先说下我最刚开始想到的最传统的想法:

1.首先因为功能块多的时候,需要界面能够滚动,所以我想到最外面用的ScrollView,然后ScrollView中包含了一个竖向排布的LienarLayout。 然后在放入一个ImageView显示这个顶部图片:

然后需要二个横向的LinearLayout,用来显示这个大的分类标题:

然后再放入二个GridView显示功能模块:

OK。我发现我的首界面写好了之后:

<ScrollView>
    <LinearLayout>
        <ImageView>  <顶部图片>
        <LinearLayout><View/><TextView/><LinearLayout>   <我的服务标题栏>
        <GridView />   <我的服务功能块>

        <LinearLayout><View/><TextView/><LinearLayout>   <我的功能标题栏>
        <GridView />   <我的功能功能块>
    <LinearLayout>
<ScrollView/>复制代码

1.布局的内容非常之多。维护很不方便 2.定制化功能差了很多,如果我下次想在《我的服务》和《我的功能》大功能分类中,再多加一个《我的售后》,又的去布局中查找相应的位置,然后去去添加新的布局代码,或者是我想删除模块功能了,我还得去布局中找出来,然后去删除它。反正就是很麻烦。 3.当前这个界面还算简单的,毕竟功能块都是以类似九宫格的形式呈现,如果哪天多了个《我的售后》,然后这个《我的售后》不是以这种九宫格的形式呈现,整个界面中有各种各样的布句呈现,管理会变的十分麻烦。


有没有什么好的办法呢。

上面说到过。我们的界面有没有像淘宝的首界面,各种布句杂糅在一起,然后又可以上下滚动,没错,那我就模仿淘宝的首页一样写个不就行了么。

这时候我的思路就变了:整个界面就使用一个RecycleView来完成。

然后里面的不同布局方式使用不同的LayoutManager不就可以了么。当然因为前面讲了我们可以模仿淘宝的首页来写,那我们当然是使用阿里巴巴开源的vlayout

这时候介绍一下我们的主角:vlayout

vlayout is a powerfull LayoutManager extension for RecyclerView, it provides a group of layouts for RecyclerView. Make it able to handle a complicate situation when grid, list and other layouts in the same recyclerview.

我们可以看到,vlayout是一个强大的RecycleView的LayoutManager,它可以帮我在RecycleView中呈现多种布局方式。


正式起航:

首先,vlayout的基本使用方法,其他大神写的很多也很好。我也不会浪费时间再写一遍:

请看这篇,基本就能够对Vlayout有所了解及使用了:

重要的事情说三遍!!!大家一定要看一遍再使用。

Android开源库V - Layout:淘宝、天猫都在用的UI框架,赶紧用起来吧! Android开源库V - Layout:淘宝、天猫都在用的UI框架,赶紧用起来吧! Android开源库V - Layout:淘宝、天猫都在用的UI框架,赶紧用起来吧!

我们回头再来看我们上面的具体的项目需求:

(我会先用VLayout实现一种简单的处理。然后再实现更加通用的处理!一定要看完最后的通用处理哦!)

简单处理:

我们首先整个activity的布局变为了:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/work_recycleview"
    android:background="@android:color/white"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:overScrollMode="never"
    >

</android.support.v7.widget.RecyclerView>复制代码

是不是变的干净简洁了!!!

然后我们要使用Vlayout来设置我们RecycleView中的各种布局。

RecycleView workRecycleview = (RecycleView)findViewById(R.id.work_recycleview);
//建立我们的委托LayoutManger
VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);
workRecycleview.setLayoutManager(layoutManager);
//通过这个layoutManager来管理一系列的LayoutHelper
//所以我们先建立一个List来存放等会要用到的LayoutHelper.
List<LayoutHelper> helperList = new LinkedList<>();
//1.
//因为第一个底部图片就这一项,所以我们就直接使用SingleLayoutHelper
SingleLayoutHelper bannerLayoutHelper = new SingleLayoutHelper();
bannerLayoutHelper.setItemCount(1);
helperList.add(bannerLayoutHelper);

//2.
//因为大标题栏是一个横向的LinearLayout,所以使用LinearLayoutHelper
LinearLayoutHelper personTitleHelper = new LinearLayoutHelper();
personTitleHelper.setItemCount(1);
helperList.add(personTitleHelper);

//3.
//因为功能块目前是九宫格,所以使用的是GridLayoutHelper
GridLayoutHelper personGridHelper = new GridLayoutHelper(3);
personGridHelper.setAutoExpand(false);
personGridHelper.setWeights(new float[]{33, 33, 33});
//设置登录时候获取到的该用户权限下显示的功能数量。
personGridHelper.setItemCount(mPersonFunctions.size());
helperList.add(personGridHelper);

//4.
//同2界面
LinearLayoutHelper companyTitleHelper = new LinearLayoutHelper();
companyTitleHelper.setItemCount(1);
helperList.add(companyTitleHelper);

//5.
//同3界面
GridLayoutHelper companyGridHelper = new GridLayoutHelper(3);
companyGridHelper.setWeights(new float[]{33, 33, 33});
companyGridHelper.setAutoExpand(false);
//设置登录时候获取到的该用户权限下显示的功能数量。
companyGridHelper.setItemCount(mCompanyFunctions.size());
helperList.add(companyGridHelper);复制代码

如果我们需要增加新的布局控制。我们只需要添加新的LayoutHelper,按顺序添加到我们的helperList中即可。

目前的LayoutHelper有以下几种:

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

既然用到RecycleView ,那怎么可以没有Adapter呢,上面的准备工作做了一部分后,我们开始写我们的Adapter。

我们这里选择继承了VirtualLayoutAdapter: 我们在构造函数中传入我们二个九宫格功能块对应的List进来。

public class WorkAdapter extends VirtualLayoutAdapter {
    int oneFuncs, twoFuncs;//
    public List<FunctionBean> oneFunctions;
    public List<FunctionBean> twoFunctions;

    public static final int BANNER_VIEW_TYPE = 0;
    public static final int DIVIDER_VIEW_TYPE = 1;
    public static final int FUN_VIEW_TYPE = 2;

    public WorkAdapter(@NonNull VirtualLayoutManager layoutManager, List<FunctionBean> oneFunctions, List<FunctionBean> twoFunctions,funcItemOnClickListener listener) {
        super(layoutManager);
        this.oneFunctions = oneFunctions;
        this.twoFunctions = twoFunctions;
        this.listener = listener;

        oneFuncs = oneFunctions.size();
        twoFuncs = twoFunctions.size();
    }


}复制代码

我们来分别看Adapter中每个方法具体的复写: 1.

@Override
public int getItemCount() {
    int totalCount = 0;
    List<LayoutHelper> helpers = getLayoutHelpers();
    if (helpers == null) {
        return 0;
    }
    for (int i = 0; i < helpers.size(); i++) {
        totalCount += helpers.get(i).getItemCount();
    }
    return totalCount;
}复制代码

我们可以看到在getItemCount()方法中,我们通过遍历了LayoutHelper,分别取每个LayoutHelper中我们刚设置的个数。然后加起来,作为整个RecycleView 的个数。

2.

@Override
public int getItemViewType(int position) {
    if (position == 0) {
        return BANNER_VIEW_TYPE;
    } else if (position == 1 || position == (2 + oneFuncs)) {
        return DIVIDER_VIEW_TYPE;
    } else {
        return FUN_VIEW_TYPE;
    }
}复制代码

我们可以看到。我们在getItemViewType方法中肯定position的值,返回不同的type,这样等会在onCreateViewHolder方法中就可以返回不同的ViewHolder了。

3.

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case BANNER_VIEW_TYPE:
            return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_work_banner, parent, false));
        case DIVIDER_VIEW_TYPE:
            return new DividerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_work_divider, parent, false));
        case FUN_VIEW_TYPE:
            return new FuncViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_work_func, parent, false));
        default:
            return null;
    }
}复制代码

4.

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (holder instanceof DividerViewHolder) {
        if (position == 1) {
            ((DividerViewHolder) holder).dividerTitle.setText("我的服务");
            ((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#F9C025"));
        } else {
            ((DividerViewHolder) holder).dividerTitle.setText("我的功能");
            ((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#35A7FF"));
        }
    } else if (holder instanceof FuncViewHolder) {
        if(position > 1 && position < 2+ oneFuncs){
          ...
          ...
          ...

        }else if(position > 2+ oneFuncs){
          ...
          ...
          ...
        }
    }
}复制代码

就这样我们就具体的实现了多个布局的设置,而且当你要再加一个新的也很方便。但是也许你这时候会发现,如果我们的布局很长,有很多九宫格,或者真的像淘宝一样,这个界面有各种功能块。那我们刚写的

@Override
public int getItemViewType(int position) {
    if (position == 0) {
        return BANNER_VIEW_TYPE;
    } else if (position == 1 || position == (2 + oneFuncs)) {
        return DIVIDER_VIEW_TYPE;
    } else {
        return FUN_VIEW_TYPE;
    }
}复制代码

这里你判断的position就会很多。你可能就要有很多的if-else 来控制返回不同的type.

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (holder instanceof DividerViewHolder) {
        if (position == 1) {
            ((DividerViewHolder) holder).dividerTitle.setText("我的服务");
            ((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#F9C025"));
        } else {
            ((DividerViewHolder) holder).dividerTitle.setText("我的功能");
            ((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#35A7FF"));
        }
    } else if (holder instanceof FuncViewHolder) {
        if(position > 1 && position < 2+ oneFuncs){
          ...
          ...
          ...

        }else if(position > 2+ oneFuncs){
          ...
          ...
          ...
        }
    }
}复制代码

然后在onBindViewHolder方法里面也要有很多的if-else,到后面维护又会变的很麻烦,所以这样写相对简单,适合模块不多的情况。



复杂的并且更通用的实现:

我们前面是把很多LayoutHelper加入到了Adapter中,然后RecycleView直接设置该Adapter, 我们这次就不这么做了。而且中间多设置一步,就是先每个LayoutHelper设置一个Adapter,成为<子的Adapter>,然后把这些Adapter再统一放入到一个<总的Adapter>中,再把这个<总的Adapter>赋值给RecycleView即可。

因为这样在<总的Adapter>中,对于每个Helper就可以知道个数,并且ViewHolder等赋值都被分配到了那些<子的Adapter>中处理了。我们不用再if-else的写很多情况了。

我们直接来看这个<总的Adapter> 是如何实现的:

(不过我不会完全很仔细的讲解,代码我也不会贴全部,就贴一些主要的地方,讲主要的部分。)

1.

public class DelegateAdapter extends VirtualLayoutAdapter<RecyclerView.ViewHolder> {

    private int mTotal = 0;
    private int mIndex = 0;
    private SparseArray<Adapter> mItemTypeAry = new SparseArray<>();
    @NonNull
    private final List<Pair<AdapterDataObserver, Adapter>> mAdapters = new ArrayList<>();
    private final SparseArray<Pair<AdapterDataObserver, Adapter>> mIndexAry = new SparseArray<>();

}复制代码

我们先定义了几个参数,

mIndex用来等于标记各个加入的<子的Adapter>的序号

一个用来存<子的Adapter>的Map(别问我为什么Map类型里面只填了一个Adapter,不是Key-Value? 可以补下SparseArray和ArrayMap的知识了,Android中用来替换HashMap的类。)

一个存放了Pair<AdapterDataObserver, Adapter>的List集合(Pair如果也不知道,也可以去补充下,就简单理解为一个有二个属性的对象,第一个属性是AdapterDataObserver,第二个是Adapter)

一个存放了Pair<AdapterDataObserver, Adapter>的Map集合。

2. 那我们知道肯定要有个方法把这些子的Adapter给加进来:

public void setAdapters(@Nullable List<Adapter> adapters) {
    clear();//把相关的参数都重新置空,这里不写出来了。

    if (adapters == null) {
        adapters = Collections.emptyList();
    }

    List<LayoutHelper> helpers = new LinkedList<>();

    mTotal = 0;

    Pair<AdapterDataObserver, Adapter> pair;
    for (Adapter adapter : adapters) {
        // every adapter has an unique index id

        //自定义类AdapterDataObserver ,继承于RecyclerView.AdapterDataObserver
        AdapterDataObserver observer = new AdapterDataObserver(mTotal, mIndex++);
        adapter.registerAdapterDataObserver(observer);

       //子的Adapter中自定义的方法:onCreateLayoutHelper(),用来返回子的Adapter中的LayoutHelper
        LayoutHelper helper = adapter.onCreateLayoutHelper();

        //而且这些子的Adapter中的LayoutHelper的个数,就是这些子的Adapter的个数
        helper.setItemCount(adapter.getItemCount());

        //总数为每个LayoutHelper的个数之和,也就是每个子的Adapter的个数之和
        mTotal += helper.getItemCount();
        helpers.add(helper);


        pair = Pair.create(observer, adapter);
        //这里的mIndexAry存放了以加入的顺序mIndex为Key的Pair<AdapterObserver,Adapter>
        mIndexAry.put(observer.mIndex, pair);
        //同时mAdapters的List集合中也存放了Pair<AdapterObserver,Adapter>
        mAdapters.add(pair);
    }

    super.setLayoutHelpers(helpers);
}复制代码

我们看下AdapterDataObser的部分代码:

  protected class AdapterDataObserver extends RecyclerView.AdapterDataObserver {
    int mStartPosition;
    int mIndex = -1;

    public AdapterDataObserver(int startPosition, int index) {
        this.mStartPosition = startPosition;
        this.mIndex = index;
    }
}复制代码

我们可以看到我们刚在new每个AdapterDataObser的时候传入的构造函数参数是mTotal, mIndex++,这样是不是正好每个Adapter中的AdapterDataObserver中的mStartPosition参数就是你的这个Adapter在所有整个RecycleView中的开始的position值。而mIndex又说明了这个AdapterDataObserver是第几个,也就是这个Adapter是所有的<子的Adapter>中的第几个。

3.

@Override
public int getItemCount() {
    return mTotal;
}复制代码

总数就是返回上面我们的mTotal参数。

4.

@Override
public int getItemViewType(int position) {
    Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position);
    if (p == null) {
        return RecyclerView.INVALID_TYPE;
    }

    int subItemType = p.second.getItemViewType(position - p.first.mStartPosition);

    if (subItemType < 0) {
        // negative integer, invalid, just return
        return subItemType;
    }

   if (mHasConsistItemType) {
    mItemTypeAry.put(subItemType, p.second);
    return subItemType;
    }

    int index = p.first.mIndex;

    return (int) getCantor(subItemType, index);
}复制代码

我们一步步来看这个比较关键的地方,我们之所以不用我们最刚开始第一次讲的Vlayout使用的方法,就是因为我们的LayoutHelper多了之后,在getItemViewType()方法中返回不同的ViewType需要很多if-else来处理。所以这里我们看他是如何自动处理的。

第一步: Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position); 我们看findAdapterByPosition方法的具体实现:

@Nullable
public Pair<AdapterDataObserver, Adapter> findAdapterByPosition(int position) {

    //获取我们上面的mAdapter集合,里面存的是Pair<AdapterObserver,Adapter>
    final int count = mAdapters.size();
    if (count == 0) {
        return null;
    }

    int s = 0, e = count - 1, m;
    Pair<AdapterDataObserver, Adapter> rs = null;

    // binary search range
    while (s <= e) {
        m = (s + e) / 2;
        rs = mAdapters.get(m);
        int endPosition = rs.first.mStartPosition + rs.second.getItemCount() - 1;

        if (rs.first.mStartPosition > position) {
            e = m - 1;
        } else if (endPosition < position) {
            s = m + 1;
        } else if (rs.first.mStartPosition <= position && endPosition >= position) {
            break;
        }

        rs = null;
    }

    return rs;
}复制代码

通过这个方法的字面意思我们不难理解:通过这个<总的Adapter>返回的item的position,来知道这个position是属于我们存了Adapter集合中的哪个Adapter的。 先获取我们上面已经保存了各个Pair<AdapterObserver,Adapter>的mAdapters集合,然后判断个数,为0就直接返回了。不为0,我们就通过二分法查找的方式来进行查找。我们前面已经在每个AdapterDataObserver中存了相对于的Adapter的起始的Position,我们只需要不停的判断现在传给这个方法的position是在(子的Adapter 的起始position) 与 (子的Adapter 的起始position + 子的Adapter的个数)之间,如果是,就说明是属于这个Adapter,我们就在mAdapters集合中取出相应的Pair<AdapterObserver,Adapter>

第二步:

int subItemType = p.second.getItemViewType(position - p.first.mStartPosition);

if (subItemType < 0) {
    // negative integer, invalid, just return
    return subItemType;
}

if (mHasConsistItemType) {
        mItemTypeAry.put(subItemType, p.second);
        return subItemType;
}

int index = p.first.mIndex;

return (int) getCantor(subItemType, index);复制代码

这里的(position - p.first.mStartPosition)其实就是这个<总的Adapter>的处于position的这一项,在这个<子的Adapter>里面的具体的position值。最后通过这个<子的Adapter>的getItemViewType来得到<子的Adapter>的ViewType。这样就自动帮我们判断了在<总的Adapter>中的某个position值的Item的所属的<子的Adapter>的ViewType了。而不用写很多if-else来判断了。

这里又要分二种情况,也就是一个boolean值mHasConsistItemType来控制:

在这个<总的Adapter>构造函数中传入,它的作用是whether sub adapters itemTypes are consistent,就是我们的所有的<子的Adapter>的itemType都是一样的。因为如果你在<子的Adapter>中没有覆写getItemViewType方法的话,默认都是返回0,即:

public int getItemViewType(int position) {
        return 0;
}复制代码

我们也知道,RecycleView在运行的时候,执行顺序是:

getItemViewType ->onCreateViewHolder ->
getItemViewType ->onCreateViewHolder ->
getItemViewType ->onCreateViewHolder ->...复制代码

如果我们的mHasConsistItemType设置为true的话: 所以我们如果所有的<子的Adapter>中的要用同一个viewType的话,比如这里是0,我们就在getItemViewType方法中执行mItemTypeAry.put(subItemType, p.second);,这样当前的这个<子的Apdater>就存在了key为0的集合中了,然后我们在onCreateViewHolder方法中通过Adapter adapter = mItemTypeAry.get(viewType);取出来就行了,这时候因为viewType为0,就正好取出来我们刚存的Adapter,然后再进入下一次的getItemViewType的时候,就用新的adapter覆盖了key为0的value值,然后再拿到onCreateViewHolder方法里面使用。

如果我们的mHasConsistItemType设置为false的话: 那这时候就用了另外一种方法,首先,因为子的Adapter默认拿到的ViewType都是0,所以我们用了要设置一个可逆算法,比如A方法和还原的B方法,A方法中我们每次传入viewType和另外一个值(这里选定了上面我们拿到的Pair<AdapterDataObserver, Adapter>中的AdapterDataObserver的index值),因为每个<子的Adapter>的index值不同,所以生成的ViewType也不同,然后我们在onCreateViewHolder方法里面,用还原的B方法,获取到index值,然后通过这个index再找回<子的Adapter>,这时候我们就可以调用<子的Adapter>的onCreateViewHolder方法了。

A方法:

private static long getCantor(long k1, long k2) {
    return (k1 + k2) * (k1 + k2 + 1) / 2 + k2;
}复制代码

B方法:(具体看onCreateViewHolder方法中)

// reverse Cantor Function
int w = (int) (Math.floor(Math.sqrt(8 * viewType + 1) - 1) / 2);
int t = (w * w + w) / 2;
int index = viewType - t;
int subItemType = w - index;复制代码

5.

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    // reverse Cantor Function
    int w = (int) (Math.floor(Math.sqrt(8 * viewType + 1) - 1) / 2);
    int t = (w * w + w) / 2;

    int index = viewType - t;
    int subItemType = w - index;

    Adapter adapter  = findAdapterByIndex(index);
    if (adapter == null) {
        return null;
    }

    return adapter.onCreateViewHolder(parent, subItemType);
}复制代码

6.

@SuppressWarnings("unchecked")
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);
    if (pair == null) {
        return;
    }

    pair.second.onBindViewHolder(holder, position - pair.first.mStartPosition);
    pair.second.onBindViewHolderWithOffset(holder, position - pair.first.mStartPosition, position);
}复制代码

findAdapterByPosition:(也是二分法查找,和上面的findAdapterByIndex方法一样,不介绍了。)

@Nullable
public Pair<AdapterDataObserver, Adapter> findAdapterByPosition(int position) {
    final int count = mAdapters.size();
    if (count == 0) {
        return null;
    }

    int s = 0, e = count - 1, m;
    Pair<AdapterDataObserver, Adapter> rs = null;

    // binary search range
    while (s <= e) {
        m = (s + e) / 2;
        rs = mAdapters.get(m);
        int endPosition = rs.first.mStartPosition + rs.second.getItemCount() - 1;

        if (rs.first.mStartPosition > position) {
            e = m - 1;
        } else if (endPosition < position) {
            s = m + 1;
        } else if (rs.first.mStartPosition <= position && endPosition >= position) {
            break;
        }

        rs = null;
    }

    return rs;
}复制代码

看了上面我们发现了,最后我们虽然给RecycleView赋值了一个<总的Adapter>,但是实际上的onCreateViewHolder方法和onBindViewHolder方法都是调用了每个具体的<子的Adapter>的。

所以我们最终在我们的Activity中的使用

final DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, true);
recyclerView.setAdapter(delegateAdapter);
List<DelegateAdapter.Adapter> adapters = new LinkedList<>();
adapters.add(XXXXX);//添加不同的子Adapter.
...
//比如这样:
GridLayoutHelper layoutHelper;
layoutHelper = new GridLayoutHelper(4);
layoutHelper.setMargin(0, 10, 0, 10);
layoutHelper.setHGap(3);
layoutHelper.setAspectRatio(4f);
adapters.add(new ASubAdapter(this, layoutHelper, 8));
...
...
delegateAdapter.setAdapters(adapters);复制代码

如果我想新加一个功能块,只要新建一个针对这个功能块的Adapter,然后添加到adapters集合中就可以了。完全不用修改原来的代码。只需要在这个新加的功能块的Adapter中处理即可。

具体的使用第二种方式的代码及DelegateAdapter.java 的源码 可以在GitHub中自行观看vlayout

求别乱喷,求点赞。哈哈。。。。。。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术专栏

深入理解Spring源码(一)-IOC容器的定位,载入,注册

前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰...

3001
来自专栏Java架构沉思录

深入理解-Spring-之源码剖析IOC(一)

作为Java程序员,Spirng我们再熟悉不过,可以说比自己的女朋友还要亲密,每天都会和他在一起,然而我们真的了解spring吗?

1003
来自专栏Android开发与分享

【Android】DataBinding库(MVVM设计模式)

4767
来自专栏Google Dart

Flutter 构建完整应用手册-列表 顶

显示数据列表是移动应用程序的基本模式。 Flutter包含ListView部件,使列表变得轻而易举!

2612
来自专栏ppjun专栏

android面试题

比如设置android:layout_gravity="right"的button会显示在父view的最右边。所以layout_gravity是设置当前view...

1622
来自专栏Flutter入门

Weex是如何在Android客户端上跑起来的

Weex可以通过自己设计的DSL,书写.we文件或者.vue文件来开发界面,整个页面书写分成了3段,template、style、script,借鉴了成熟的MV...

4305
来自专栏10km的专栏

jface databinding:输入无效数值时强制恢复初始值-updateModelToTarget

解决方案 Binding类中的updateModelToTarget方法,就是实现从数据对象到目标对象(比如Widget)的更新方法,只要调用这个方法就能强制让...

2185
来自专栏大内老A

ASP.NET MVC的Razor引擎:RazorView

Razor引擎具有两个核心的类型,一个是表示View本身的类型RazorView,另一个则是获取和创建它的RazorViewEngine,我们将用两篇文章对它们...

2427
来自专栏xingoo, 一个梦想做发明家的程序员

前端那点事儿——Tocify自动生成文档目录

今天偶然间看到文档服务器有一个动态目录功能,点击目录能跳转到指定的位置;窗口滑动也能自动更新目录的焦点。 效果 框架 原来使用的是一个开源的jquery-...

2606
来自专栏向治洪

android自定义属性

1、引言 对于自定义属性,大家肯定都不陌生,遵循以下几步,就可以实现: 自定义一个CustomView(extends View )类 编写values/a...

20410

扫码关注云+社区

领取腾讯云代金券