27

I have a LinearLayout with many nested LinearLayouts and TextViewss

My main activity inflates the main LinearLayout,

Then I load data from a server and based on the data received, I add multiple Layouts in a place holder (LinearLayout)

This is simple a news page where I load Images associated with the news and place it inside an initially empty LinearLayout.

Each Image has the following info: Title(TextView), Date(TextView), Image(ImageView) so what I actually do is the following:

*Please notice that this is only the essential coded in the question I elemenated all the try -> catch ... if/else ....etc

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

      }
}

The code above works good for a single image

But for multiple images the Image Loader doesn't work I guess it's because all ImageViews (Inflated multiple times) have the same ID

Shehabic
  • 6,787
  • 9
  • 52
  • 93
  • is there any reason why you are not using a listview ? – Robert Estivill Feb 03 '13 at 02:01
  • Yes, I have a LinearLayout inside a Scrollview where I have some TextViews at the top, then I have many ImageViews, and Even Video Holders, ListView will have its own scroll inside a ScrollView, it'll be bad for usability (if I'm right) – Shehabic Feb 03 '13 at 02:10

4 Answers4

39

When you provide a ViewGroup to be used as the parent, the View returned by inflate() is this parent (vg in your case) and not the newly created View. Therefore, v points toward the ViewGroup vg and not toward the newly created View and as all of your children have the same id, the same subviews (imv, dt, ttl) are returned each time.

Two solutions. The first one is to change their id right after you are finished with them, before the next iteration. Therefore, on the next creation at the beginning of the next iteration, the newly created Views will have a different IDs from the older Views because they will still use the old constant defined in R.

The other solution would be to add the parameter false to the call to inflate() so that the newly created view will not be attached to the ViewGroup and will then be returned by the inflate() function instead of the ViewGroup. The rest of your code will then works as attended with the exception that you will have to attach them to the ViewGroup at the end of the iteration.

Notice that you still need to provide a ViewGroup because it will be used to determine the value of the LayoutParams.

SylvainL
  • 3,926
  • 3
  • 20
  • 24
  • Well my solution above works, but what I'm afraid of is: how do I know if there are auto-generated integers in the range I'm using 1001 to 1000+x, or 2001 to 2000 + x. I've noticed that the Auto generated ids usually have very large value. – Shehabic Feb 03 '13 at 13:11
  • 1
    Check it with findViewById(): it will return null if there is no ID with the required integer value. However, the important thing in your case is simply that the value (1000 + i) is different from R.id.imagePreview. – SylvainL Feb 03 '13 at 13:40
  • Changing the 'id' at the end of iteration did the trick.... :p still its like a patch :p – nobalG Dec 08 '15 at 11:04
  • 1
    @Shehabix if you want to use unused ids, View.generateViewId() is what you are looking for. "Generate a value suitable for use in setId(int). This value will not collide with ID values generated at build time by aapt for R.id." – Sfseyhan Apr 22 '16 at 08:49
  • Both proposed solutions don't appear to work well if inflating a relative layout. When the ids are replaced the relative attributes (ex. toRightOf) appear to not work. The layout is also messed up if specifying true to add to rootView on inflate. – AlanKley Sep 29 '16 at 16:56
22

I had the same problem, and based on the answer from @SylvainL, here'a a working solution:

// 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);
Ben Clayton
  • 80,996
  • 26
  • 120
  • 129
9

Is there a reason why the ImageView in the layout XML needs to have an ID? Could you erase the android:id attributes from the image_preview.xml layout and then simply iterate through the children of the inflated LinearLayout? For example:

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);
user697495
  • 590
  • 1
  • 3
  • 9
0

I inflate XML-Layout with dynnamic and get text of 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
    }
  }
Infomaster
  • 793
  • 7
  • 8