视图在RecyclerView中的动态位置

时间:2019-07-15 18:17:20

标签: java android android-recyclerview android-view

我的问题基于先前关于我的Code的问题-这是一种具有不同视图的新闻源,可以垂直或水平滚动或显示各种内容(基本上是广告)

  • 垂直视图
  • 水平视图
  • 广告查看

现在我在Ben P的帮助下进行了管理,在上一篇文章中,我使用以下代码来组织我的观点:

this.items = new ArrayList<>();

if (listVertical.size() > 0) {
    items.add(listVertical.remove(0));
}

if (listHorizontal.size() > 0) {
    items.add(listHorizontal.remove(0));
}

while (listVertical.size() > 0 || listAd.size() > 0) {   

    if (listAd.size() > 0) {
        items.add(listAd.remove(0));
    }

    int count = 5;

    while (count > 0 && listVertical.size() > 0) {
        items.add(listVertical.remove(0));
    }
}

这是静态消息传递的静态方法。因此,我尝试使用JSON中的position元素来组织视图,如下所示:

{
"horizontal": [{...,position:"1",},{...,position:"3",...},{...,position:"4",...}] ,
"vertical": [{...,position:"2",},{...,position:"5",},{...,position:"6",}] 
"ad": [{...,position:"0",},{...,position:"7",},{...,position:"8",}] 
}

使用此代码,应该可以将Feed定位:

int length_tot= vertical.size() + horizontal.size() + ad.size();
this.items = new ArrayList<>();

for(int i = 0; i < lenth_tot; i++) {
    if(vertical.getallthepositions_somehow == i) {
        items.add(vertical.remove(0));
    }

    if(horizontal.getallthepositions_somehow == i) {
        items.add(horizontal.remove(0));
    }

    if(ad.getallthepositions_somehow == i) {
        items.add(ad.remove(0));
    }
}

如您所见,我被困在遍历所有列表的过程中,寻找下一个要添加到我的项列表中的位置。

所以我的问题是:

找到下一个要添加的职位的最快方法是什么?我的解决方案感觉太沉重了,您是否想过(从处理器的角度来看)更经济的解决方案?

这是我的onBindViewHolder

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

    if (holder.getItemViewType() == VIEW_TYPE_VERTICAL)
        verticalView((VerticalViewHolder) holder, position);

    if (holder.getItemViewType() == VIEW_TYPE_HORIZONTAL)
        horizontalView((HorizontalViewHolder) holder, position);
}

以及适配器中的getItemViewType

@Override
public int getItemViewType(int position) {
    if (items.get(position) instanceof Vertical)
        return VIEW_TYPE_VERTICAL;

    if (items.get(position) instanceof Horizontal)
        return VIEW_TYPE_HORIZONTAL;
}

编辑:

所以我想从服务器中获取JSON:

{
"horizontal": [{...,position:"1",},{...,position:"3",...},{...,position:"4",...}] ,
"vertical": [{...,position:"2",},{...,position:"5",},{...,position:"6",}] 
"ad": [{...,position:"0",},{...,position:"7",},{...,position:"8",}] 
}

是具有以下查看顺序的新闻源:

- [AD] (position=0)
- [HORIZONTAL] (position =1)
- [VERTICAL] (position=2)
- [HORIZONTAL]
- [HORIZONTAL]

以此类推...

因此,由于每个json数组中的psoition项,服务器基本上决定了适配器中我的视图的顺序

Ben P-我列表中正确顺序的代码:

    Object[] itemsObject = new Object[listVertical.size() + listHorizontal.size() + adList.size()];

    for (Vertical item : listVertical) {
        itemsObject[Integer.parseInt(item.getPosition())] = item;
    }

    for (Horizontal item : listHorizontal) {
        itemsObject[Integer.parseInt(item.getPosition())] = item;
    }

    for (Adfeed item : adList) {
        it
    ArrayList<Object> itemsPos = new ArrayList<>();
    itemsPos.addAll(listVertical);
    itemsPos.addAll(listHorizontal);
    itemsPos.addAll(adList);

    Collections.sort(items, new Comparator<Object>() {
        @Override
        public int compare(Object o1, Object o2) {
            return Integer.compare(o1.getPosition(),o2.getPosition())
        }
    });

垂直,水平和Adfeed包含.getPosition()。那么如何在比较器中访问它们?

2 个答案:

答案 0 :(得分:2)

实际上,无论如何,您都需要构造要在适配器中传递的项目列表,以显示在RecyclerView中。因此,遍历每个列表中的所有元素(即水平,垂直和广告)并不是一个坏主意。实际上,您正在对要显示在RecyclerView中的项目进行一些预处理,以便可以相应地执行。

我可以建议在预处理数据时进行一些小改进。在您的代码中,您必须在每次迭代中再次遍历所有元素。可以实际优化。考虑到该预处理不在适配器范围内,您可以执行以下操作。

int total = vertical.size() + horizontal.size() + ad.size();
int[] types = new int[total];

// Considering the positions are not overlapping.
for(int i = 0; i < vertical.size(); i++) {
    types[vertical[i].position] = VIEW_TYPE_VERTICAL;
}

for(int i = 0; i < horizontal.size(); i++) {
    types[horizontal[i].position] = VIEW_TYPE_HORIZONTAL;
}

for(int i = 0; i < add.size(); i++) {
    types[add[i].position] = VIEW_TYPE_ADD;
}

// The type array has the view types for all elements now. 
// Now add the views accordingly in your items list and then pass it to your adapter.
ArrayList<Item> items = new ArrayList<Item>();
for(int i = 0; i < type.length; i++) {
    items.add(getItemBasedOnType(type[i]));
}

// Populate your adapter or pass new elements
adapter.addListItems(items);

getItemBasedOnType可以是这样的。

private Item getItemBasedOnType(int type) {
    if(type == VIEW_TYPE_VERTICAL) return vertical.remove(0);
    else if(type == VIEW_TYPE_HORIZONTAL) return horizontal.remove(0);
    else return add.remove(0);
}

希望能有所帮助。

答案 1 :(得分:1)

在实现RecyclerView.Adapter时,您应努力首先创建一个代表数据集的有序列表(或数组)。如果您可以成功建立此列表,则其他所有操作都将变得非常容易。

为了从您发布的JSON示例构建列表,我将考虑两种不同的策略。第一种是使用数组,以便您可以分配特定的索引:

Object[] items = new Object[vertical.size() + horizontal.size() + ad.size()];

for (VerticalItem item : vertical) {
    items[item.position] = item;
}

for (HorizontalItem item : horizontal) {
    items[item.position] = item;
}

for (AdItem item : ad) {
    items[item.position] = item;
}

第二个选项是使用List,然后使用Comparator对列表进行排序,以比较位置。这将要求您的三种商品类型中的每一种都具有一个与position字段相同的超类,或者实现一个暴露getPosition()等的接口。

List<PositionalItem> items = new ArrayList<>();
items.addAll(vertical);
items.addAll(horizontal);
items.addAll(ad);

Collections.sort(items, new Comparator<PositionalItem>() {
    @Override
    public int compare(PositionalItem lhs, PositionalItem rhs) {
        return Integer.compare(lhs.getPosition(), rhs.getPosition());
    }
});

这两种方法都会产生一个单独的排序列表,该列表中的项目以您想要显示的顺序排列。现在,我们可以使用它来实现Adapter的其余部分。

(此示例假定您将Object[]用于项目列表。如果使用List<PositionalItem>或类似的名称,显然语法会稍有不同。)

private class MyAdapter extends RecyclerView.Adapter {

    private final Object[] items;

    public MyAdapter(Object[] items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.length;
    }

    @Override
    public int getItemViewType(int position) {
        Object item = items[position];

        if (item instanceof VerticalItem) {
            return R.layout.newsfeed_vertical;
        } else if (item instanceof HorizontalItem) {
            return R.layout.newsfeed_horizontal;
        } else if (item instanceof AdItem) {
            return R.layout.newsfeed_ad;
        } else {
            throw new IllegalStateException("unexpected item type: " + item);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View itemView = inflater.inflate(viewType, parent, false);

        switch (viewType) {
            case R.layout.newsfeed_vertical: return new VerticalViewHolder(itemView);
            case R.layout.newsfeed_horizontal: return new HorizontalViewHolder(itemView);
            case R.layout.newsfeed_ad: return new AdViewHolder(itemView);
            default: throw new IllegalStateException("unexpected viewType: " + viewType);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        switch (holder.getItemViewType()) {
            case R.layout.newsfeed_vertical: verticalView((VerticalViewHolder) holder, position);
            case R.layout.newsfeed_horizontal: horizontalView((HorizontalViewHolder) holder, position);
            case R.layout.newsfeed_ad: adView((AdViewHolder) holder, position);
            default: throw new IllegalStateException("unexpected viewType: " + viewType);
        }
    }

    // ...
}

您会注意到,我对视图类型使用了一个技巧:使用布局资源ID代替int常量。无需定义自己的常量;资源框架已经为您创建了这些常量!

无论如何,我很高兴回答您的任何其他问题。但是这里的主要课程是担心首先先构建项目列表,然后再在所有Adapter方法中使用列表


与其使用ArrayList<Object>最好是为所有三种商品类型创建一个接口,然后使用该接口:

public interface NewsfeedItem {
    int getPosition();
}

现在,您可以将implements NewsfeedItem添加到每个类的声明中:

public class Vertical implements NewsfeedItem {
    // ...
}
public class Horizontal implements NewsfeedItem {
    // ...
}
public class Adfeed implements NewsfeedItem {
    // ...
}

只要您的每个类中都有一个int getPosition()方法,这将“正常工作”。看起来getPosition()可能现在正在返回String,因此您可以更改接口定义以返回String,也可以更改类以返回int。两种方法都可以,只要两者都匹配即可。

然后,一旦设置了此界面,就可以更改排序代码以使用它:

ArrayList<NewsfeedItem> itemsPos = new ArrayList<>();
itemsPos.addAll(listVertical);
itemsPos.addAll(listHorizontal);
itemsPos.addAll(adList);

Collections.sort(items, new Comparator<NewsfeedItem>() {
    @Override
    public int compare(NewsfeedItem o1, NewsfeedItem o2) {
        return Integer.compare(o1.getPosition(),o2.getPosition());
    }
});