RecyclerView 中视图的动态位置
Dynamic position of views in RecyclerView
我的问题基于之前关于我的 的问题 - 它是一个具有不同视图的新闻源,可以垂直或水平滚动或显示各种内容(基本上是广告)
- 垂直视图
- 水平视图
- 广告视图
现在我在 Ben P 的帮助下,在我之前的 post 中使用以下代码组织了我的观点:
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 中使用位置元素来组织我的视图,如下所示:
{
"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-array
中的 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()。那么如何在我的比较器中访问它们呢?
实际上,无论如何,您需要构建一个项目列表,以便在您的适配器中传递以显示在 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);
}
希望对您有所帮助。
在实施 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
常量。无需定义自己的常量;资源框架已经为您创建了这些常量!
无论如何,我很乐意回答您的任何其他问题。但这里的主要教训是担心构建项目列表首先,然后在所有适配器方法中使用列表。
与其使用 ArrayList<Object>
,不如为所有三种项目类型创建一个接口来实现,并使用它:
public interface NewsfeedItem {
int getPosition();
}
现在您可以将 implements NewsfeedItem
添加到每个 类:
的声明中
public class Vertical implements NewsfeedItem {
// ...
}
public class Horizontal implements NewsfeedItem {
// ...
}
public class Adfeed implements NewsfeedItem {
// ...
}
这将 "just work" 只要您的每个 类 中都有一个 int getPosition()
方法。看起来 getPosition()
return 现在可能是一个字符串,所以您可以将接口定义更改为 return String
或者您可以将 类 更改为 return 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());
}
});
我的问题基于之前关于我的
- 垂直视图
- 水平视图
- 广告视图
现在我在 Ben P 的帮助下,在我之前的 post 中使用以下代码组织了我的观点:
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 中使用位置元素来组织我的视图,如下所示:
{
"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-array
中的 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()。那么如何在我的比较器中访问它们呢?
实际上,无论如何,您需要构建一个项目列表,以便在您的适配器中传递以显示在 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);
}
希望对您有所帮助。
在实施 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
常量。无需定义自己的常量;资源框架已经为您创建了这些常量!
无论如何,我很乐意回答您的任何其他问题。但这里的主要教训是担心构建项目列表首先,然后在所有适配器方法中使用列表。
与其使用 ArrayList<Object>
,不如为所有三种项目类型创建一个接口来实现,并使用它:
public interface NewsfeedItem {
int getPosition();
}
现在您可以将 implements NewsfeedItem
添加到每个 类:
public class Vertical implements NewsfeedItem {
// ...
}
public class Horizontal implements NewsfeedItem {
// ...
}
public class Adfeed implements NewsfeedItem {
// ...
}
这将 "just work" 只要您的每个 类 中都有一个 int getPosition()
方法。看起来 getPosition()
return 现在可能是一个字符串,所以您可以将接口定义更改为 return String
或者您可以将 类 更改为 return 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());
}
});