如何在一个充气的布局中膨胀具有相同id的多个实例

27

我有一个包含多个嵌套LinearLayout和TextView的LinearLayout。

我的主要活动会填充主LinearLayout,

然后我从服务器加载数据,并根据接收到的数据,在一个占位符(LinearLayout)中添加多个布局。

这只是一个简单的新闻页面,我在最初为空的LinearLayout中加载与新闻相关联的图像。

每个图像都有以下信息:标题(TextView)、日期(TextView)、图像(ImageView),所以我实际上做的是:

*请注意,这仅是问题中必要的代码部分,我省略了所有的 try -> catch ... if/else .... 等

public void addImages(JSONArray images){
      ViewGroup vg = (ViewGroup) findViewById(R.id.imagesPlaceHolder);


      // loop on images
      for(int i =0;i<images.length;i++){

          View v = getLayoutInflater().inflate(R.layout.image_preview,vg);
          // then 
          I think that here is the problem 
          ImageView imv = (ImageView) v.findViewById(R.id.imagePreview);
          TextView dt = (TextView) v.findViewById(R.id.dateHolder);
          TextView ttl = (TextView) v.findViewById(R.id.title);
          // then 
          dt.setText("blablabla");
          ttl.setText("another blablabla");
          // I think the problem is here too, since it's referring to a single image
          imv.setTag( images.getJSONObject(i).getString("image_path").toString() );
          // then Image Loader From Server or Cache to the Image View

      }
}

上述代码适用于单个图像,但对于多个图像,图片加载器不起作用。我猜这是因为所有的ImageView(多次填充)具有相同的ID。


你为什么不使用ListView? - Robert Estivill
是的,我在ScrollView中有一个LinearLayout,在顶部有一些TextViews,然后有许多ImageViews和视频持有者,ListView将在ScrollView内部拥有自己的滚动条,这对可用性来说不太好(如果我没错的话)。 - Shehabic
4个回答

39
当您提供一个ViewGroup作为父级容器时,由inflate()返回的View是这个父级容器(在您的情况下是vg),而不是新创建的View。因此,v指向ViewGroup vg,而不是新创建的View,并且由于所有子视图具有相同的id,每次都会返回相同的子视图(imv, dt, ttl)。

两种解决方案。第一种是在完成它们后立即更改它们的id,在下一次迭代之前。 因此,在下一次迭代开始时,新创建的视图将与旧视图具有不同的ID,因为它们仍将使用在R中定义的旧常量。
另一个解决方案是在调用inflate()时向其添加false参数,以便新创建的视图不会附加到ViewGroup上,并且将由inflate()函数返回,而不是ViewGroup。然后,代码的其余部分将按预期工作,但需要注意的是,您必须在迭代结束时将它们附加到ViewGroup上。
请注意,仍然需要提供ViewGroup,因为它将用于确定LayoutParams的值。

我的上面的解决方案是有效的,但我担心的是:我如何知道在我使用1001到1000+x或2001到2000+x的范围内是否有自动生成的整数。 我注意到自动生成的ID通常具有非常大的值。 - Shehabic
1
使用findViewById()进行检查:如果没有所需整数值的ID,则它将返回null。然而,在您的情况下,重要的是确保值(1000 + i)与R.id.imagePreview不同。 - SylvainL
在迭代结束时更改“id”就解决了问题... :p 但仍然像是一个补丁 :p - nobalG
1
@Shehabix 如果您想使用未使用过的ID,则View.generateViewId()是您要寻找的内容。 “生成适合用于setId(int)的值。此值不会与aapt在构建时为R.id生成的ID值发生冲突。” - Sfseyhan
两种提出的解决方案似乎都不能很好地处理相对布局的膨胀。当ID被替换时,相对属性(例如toRightOf)似乎无法工作。如果在膨胀时指定true添加到rootView上,则布局也会混乱。 - AlanKley

22

我曾经也遇到过同样的问题,基于 @SylvainL 的回答,这里提供一个可行的解决方案:

// myContext is, e.g. the Activity.
// my_item_layout has a TextView with id='text'
// content is the parent view (e.g. your LinearLayoutView)
// false means don't add direct to the root
View inflated = LayoutInflater.from(myContext).inflate(R.layout.my_item_layout, content, false);

// Now, before we attach the view, find the TextView inside the layout.
TextView tv = (TextView) inflated.findViewById(R.id.text);
tv.setText(str);

// now add to the LinearLayoutView.
content.addView(inflated);

9

ImageView在layout XML中需要有一个ID的原因是什么?你能否从image_preview.xml布局中删除android:id属性,然后简单地迭代膨胀LinearLayout的子项?例如:

ViewGroup v = (ViewGroup)getLayoutInflater().inflate(R.layout.image_preview,vg);
ImageView imv = (ImageView) v.getChildAt(0);    
TextView dt = (TextView) v.getChildAt(1);
TextView ttl = (TextView) v.getChildAt(2);

是的,我的视图嵌套在<FrameLayout> <ImageView /> <TextViews> </FrameLayout>中。 - Shehabic

0

我使用动态方式膨胀XML布局并获取ID的文本

  private val onAddView = View.OnClickListener {
    val parent = viewForm.findViewById<LinearLayout>(R.id.layout_parent)
    LayoutInflater.from(activity).inflate(R.layout.layout_child, parent) // layout_child has id "tv_attribute"
  }

  private val onSave = View.OnClickListener {
    val parent = viewForm.findViewById<LinearLayout>(R.id.layout_parent)
    for (i in 0 until parent.childCount) {
        val getText = parent.getChildAt(i).findViewById<TextView>(R.id.tv_attribute).text
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接