每个项目视图都可以选择最多 8 个图像,这些图像周围有边框或无边框,最多 4 个带有条纹、实线、无边框、剪贴画的文本框 >每个都可以选择不同的颜色查看项目。每个项目也有不同的 > 尺寸和不同的位置。物品外观的可能性几乎是无穷无尽的
我们实际上可以将其简化为
我有最多 8 个图像和 4 个文本的视图,可以有不同的
风格和位置。
那么只有 45 种可能性来选择将有多少图像和文本(计算没有图像或文本的可能性)。
所以如果我用足够多的元素制作简单的RelativeLayout,然后根据需要移动它们......
Hovewer 说 45 种可能的类型都很好...
太多了。你看到让任何适配器携带太多的项目类型不是很聪明,简单地说,当适配器设置为回收器视图(或 ListView)时,它所做的第一件事就是创建“小”视图池(RecyclerViewPool),它基本上是simple sparse array,这将保留任何已删除的 ViewHolder,直到它被重用的时间到来,如果堆太大,它将被删除。也就是说理论上,在实践中,在指定时间可以有多少 scap 子项是有限制的(对于回收器,我认为它在 5 左右),所以如果我们要创建具有多种类型的视图持有者,我们希望大部分时间都在访问该缓存,并且由于我们的类型 createViewHolder 没有废料视图持有者,因此会被调用,实际上这感觉更像是我们根本没有视图持有者。
还有一件事。
然后我在包含回收器视图的活动中创建了一个视图列表>而不是传递对象列表来创建视图,我只是>传递了视图本身。由于我从来没有>每次都必须阅读每个项目并为每个项目创建一个自定义视图,因此效果要好得多。
Recycler 视图用于卸载不再需要的项目(视图),或者我应该说对用户不可见的项目(视图),并尽可能地重用旧视图以减少布局膨胀和搜索的数量为身份证。
并且通过一直加载它们,这个功能就被省略了。
如果我没记错的话,这可能比使用 LinearLayout(垂直)创建 ScrollView 并将所有视图放入其中要慢一点。
但是这里最大的缺点是你所有的视图总是被加载,如果它们很简单,我真的不明白为什么不使用 ScrollView + LinearLayout,但如果不是很好,这会变得复杂,在新手机上,内存不是什么大问题,有 500+ mb 的手机,但即便如此,并非每部手机都会为您的应用程序提供 300 mb。
我的答案
我猜想它会是你想听到的:)
减慢布局膨胀的东西,实际上并不是设置文本、背景或位置,而是加载资源来执行此操作,并搜索视图。
让我们从以下开始:
为您加载的资源使用缓存以尽可能多地重用它们,(我在最后添加了关于图像加载的一小部分)。
FindViewById 真的很慢,我的意思是,只需使用 viewholder 模式并删除对视图的搜索,您就可以获得很好的加速。
您不想为每个元素设置布局。
想法非常简单,根据您的主要层次结构视图(这次)图像和文本的数量创建布局组。在创建 ViewHolder 创建 RelativeLayout 时,推送足够的图像/文本以满足您对位置元素的需求。在绑定数据时移动元素并根据需要设置样式。
为了减少类型的数量,让我们使用四舍五入到 2 的图像/文本数量。
更新 LayoutParams 并不像大多数人认为的那样花费我们太多,它花费了一些,不要误会我的意思,这基本上会迫使父级进行额外的子级测量和布局更新,仅此而已,当视图将被显示时,它必须无论如何都要进行此测量和布局更新:)
假设我们有这两组
- 图片(最多 2 张)id:0
- 图片(最多 4 张)id:1
- 图片(最多 6 张)ID:2
- 图片(最多 8 张)ID:3
还有
- 文本(最多 2 个)id:0
- 文本(最多 4 个)ID:1
这就是我将如何“命名”类型
int type = (img_id)<<1 | (txt_id)
而总类型数实际上是
int type_count = (4 * 2) = 8
结束我们的适配器“基础”是这样的
public class MyAdapter<MyType extends MyAdapter.MyTypeBase> extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
Context context;
ArrayList<MyType> elements;
public MyAdapter(Context context, ArrayList<MyType> elements) {
this.context = context;
this.elements = elements;
}
public int getItemCount() { return elements.size(); }
public MyType get(int position) { return elements.get(position); }
public int getItemViewType(int position) {
int imgs = get(position).getImageCount();
int txts = get(position).getTextCount();
imgs = (imgs/2) * 2 + (imgs % 2 == 0 ? 0 : 1);
txts = (txts/2) * 2 + (txts % 2 == 0 ? 0 : 1);
return imgs << 1 | txts;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
return new ViewHolder(new RelativeLayout(context), position); // or use inflater to inflate some base layout
}
class ViewHolder extends RecyclerView.ViewHolder {
int textCount, imageCount;
RelativeLayout root;
ImageView [] imageViews;
TextView [] textViews;
public ViewHolder(RelativeLayout view, int position) {
super(view);
root = view;
textCount = (position & 1) * 2;
imageCount = (position >> 1) * 2;
textViews = new TextView[textCount];
imageViews = new ImageView[imageCount];
for (int i = 0; i < imageCount; i++) {
root.addView(imageViews[i] = new ImageView(context)); // or use inflater
}
for (int i = 0; i < textCount; i++) {
root.addView(textViews[i] = new TextView(context)); // or use inflater
}
}
}
interface MyTypeBase {
int getImageCount();
int getTextCount();
}
是的,有些东西不见了:)
public void onBindViewHolder(ViewHolder viewHolder, int position) {
MyTypeBase element = get(position);
vh.textViews[vh.textCount-1].setVisibility(element.getTextCount()%2 == 0? View.VISIBLE : View.GONE);
vh.imageViews[vh.imageCount-1].setVisibility(element.getImageCount()%2 == 0? View.VISIBLE : View.GONE);
// now this is your place to shine
}
因为我不知道你能得到什么作为元素的“数据”,所以在这里对你没有多大帮助。因此,在 bindViewHolder 中,您的 viewHolder 将有足够的图像和文本来满足您的元素需要,重新加载任何图像并移动它们。
最后的笔记
我还应该在这里和那里添加 fev 注释:)
懒惰。是的,我的意思是,当您想偷懒时,您会尝试使解决方案尽可能简单,使代码更易于阅读,并且不太可能出现重大错误:)
另外请使用库来显示和缓存图像,而不是每次都加载它们。很少有像 UniwersalImageLoader、Picasso 或 Volley 这样的好作品。我个人使用 UIL,所以这里举个例子。
// This might be done in helper class ^^
DisplayImageOptions OPTIONS_DISC_AND_CACHE = new DisplayImageOptions.Builder()
.cacheOnDisk(true)
.cacheInMemory(true)
.build();
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(context)
// .memoryCacheSize((int) (Runtime.getRuntime().maxMemory() / 1024) / 16) // if you want to reduce it some more ^^
.threadPoolSize(4)
.tasksProcessingOrder(QueueProcessingType.LIFO)
.build();
imageLoader.init(configuration);
// And thats how to use it
ImageLoader.getInstance().displayImage("MY URL", (ImageView) myImageView, Tools.OPTIONS_DISC_AND_CACHE);
以及为什么你应该使用这样的库,不知道...用于磁盘的内存和缓存允许更快的加载时间。
最后,英语不是我的主要语言,所以如果我拼错了什么,忘记了逗号,或者写错了句子,请务必纠正。
干杯。