I am adding multiple XML Views programmatically. I'm using a layout inflater to add them and there are no problems with that.
But I'm not able to modify the TextView in each of them.
For example, consider I am adding a LinearLayout three times in my final View. I have a TextView in that linear layout. I extract it using findViewById and if I setText("hello"); it is being reflected in the first layout, but not the second and third.
Will the inflater create new ids dynamically when adding multiple XML elements?
To answer part of your question, no: Ids are not dynamically generated by LayoutInflater.
When you say you are adding the views, where are you adding them? You mention a final View.
You don't include your code, but I assume you are calling Activity.findViewById. If you call this.findViewById from your Activity, you are traversing the entire View hierarchy and finding the first view with such an Id.
What you need to do, is iterate through all of the LinearLayouts that contain your TextViews and call findViewById on each of them.
for (LinearLayout layout : <fill in>) {
((TextView) layout.findViewById(R.id.yourid)).setText("hello");
}
As for the <fill in>. Hard to tell how to fill it in without your code. It seems like you are adding these into a parent view right? Let's assume we have a parent View parent = some view;.
You just have to get all of its children and iterate. There are a few ways you can do it depending on which subclass of View the parent is. Let's keep it simple and assuming the parent is just another LinearLayout.
In that case the for loop changes to something like:
for (int i = 0; i < parent.getChildCount(); i++) {
final LinearLayout layout = (LinearLayout) parent.getChildAt(i);
((TextView) layout.findViewById(R.id.yourid)).setText("hello");
}
Related
I use a for statement to create and add linearlayouts to a pre-existing vertical linearlayout. When creating them I use the for statement variable to assign an id to each one so the first layout has an id of 0 and the last has an id of 4.
for(int i = 0; i < 5; i++){
create layout
layout.setID(i);
}
I know how to change a view that's created like
Linearlayout linearLayout = new LinearLayout();
linearLayout.makeSomeChanges;
But how do I reference the layouts created in my for statement to make changes to them?
You can do it with findViewById() function. Suppose we have vertical linear layout called container. Now we can inflate there 4 items (in my case this items are linear layout with TextView inside). See the code below:
LinearLayout container = findViewById(R.id.container);
for (int i = 0; i < 5; i++) {
View child = getLayoutInflater().inflate(R.layout.item, null);
child.setId(i);
container.addView(child);
}
After adding child layouts we can access their views by ids and change any view inside them like I did with TextView:
for (int i = 0; i < 5; i++) {
View view = container.findViewById(i); // here we get child linear layout
// now we can access any view inside child linear layout and change it,
// or change some parameters of the child layout itself.
TextView textView = view.findViewById(R.id.text);
textView.setText(String.format("Changed Text %d", i));
}
Id should be ideally in resource file to avoid any coflict with existing IDs. In your case if the views are dynamic, it's not a good idea either to put those IDs in resource files.
I would recommend to use setTag() method instead of setId(). Later you can do getViewWithTag() to get the view associated with that tag.
If you are creating View (or layout) objects yourself and adding them to the screen you will have to keep track of them yourself, perhaps by a ArrayList or HashMap or any other way. note that: using setID() you may run into conflicts (two Views with same id), to avoid this you can use View.generateViewId() and keep track of all IDs you get and map them to the View.
I have a custom XML file. I want to repeat this in a layout (say Relative) n number of times, dynamically (obviously).
I have seen many posts, but none helped. I am not looking for a ListView or Adapters or so. It's as simple as - A RelativeLayout. Inside it, adding the custom XML one above another. Any number of times.
With a static LinearLayout (Vertical orientation), adding the view dynamically results in rendering it once, not one below another. Don't know why. Although a TextView or so do repeat one below the other in a loop inside a LinearLayout (Vertical).
Then I dynamically created the layout (Relative), and inflated the custom XML. Displayed one. When I tried for another below the first it told me to remove child's parent first (Exception). If I do that and add again, its as good as removing the first rendered view and adding it again.
So how can I get multiple views in same layout?
A rough presentation of what I've attempted:
mainLayout = (RelativeLayout)findViewById(R.id.mainlay); //Mainlayout containing some views already
params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW,R.id.sideLayout); //sideLayout is an existing LinearLayout within the main layout.
View child = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
RelativeLayout r1 = new RelativeLayout(this);
r1.setLayoutParams(params);
r1.addView(child);
mainLayout.addView(r1);
mainLayout.setLayoutParams(params);
mainLayout.addView( child);
/* r2 = new RelativeLayout(this);
r2.setLayoutParams(params);
r2.addView(contentLayout); [Gives exception] */
This is how it worked out for me...
Before that, the issue with android is:
If you add dynamic views inside a LinearLayout (Horizontal), they will appear horizontally with new created instances, added to the view.
However, shockingly, it's not the same in case of LinearLayout (Vertical orientation). Hence the whole mess.
Solution:
The RelativeLayout layout file was binded with the variable, somewhat like this:
customLay = (RelativeLayout) mainLay.findViewById(R.id.dynamicCustomLayout);
Then, a Dynamic RelativeLayout was created within which the former variable is added/wrapped.
customLayout = new RelativeLayout(this);
customLayout.addView(customLay);
Every layout is assigned an id:
customLayout.setId(i);
And then a loop is run (2 if conditions for i=0 and i>0)
for i>0 (indicates the 2nd dynamic layout, to be added below the first), LayoutParameter is created:
params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
And then for i>0, using the ids of dynamic views, they are added one below the other:
//Following code below used id to place these views below each other to attain list type arrangement of views//
// i==0 for first view on top//
if (i == 0) {
params.addRule(RelativeLayout.BELOW, R.id.sideLayout);
customLayout.setLayoutParams(params);
}
// i>0 for views that will follow the first top//
else {
params.addRule(RelativeLayout.BELOW, i - 1);
customLayout.setLayoutParams(params);
}
Then added to main root layout, where all these views or cards need to be displayed:
includeLayout.addView(customLayout);
Ofcourse, the code is not just this. I have written the essential points that helped me achieve the target and that may help others in future.
So the main essence was ---
using a Dynamic RelativeLayout, to
bind the static RelativeLayout, and
assigning ids to the Dynamic RelativeLayout wrappers, and
on basis of ids use RelativeLayoutParameters to place the following
ids below the previous ones.
You have to instanciate every child by itself
View child = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
r1.addView(child);
View child2 = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
r1.addView(child2);
//ok, i do a analog thing in obne of my apps. here is the code:
public class FlxForm extends LinearLayout {
public FlxForm(Context context) {
super(context);
inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.flxform, this);
this.setPadding(0, 0, 0, 0);
container = (LinearLayout) this.findViewById(R.id.flxform);
this.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
//here is my funtion to calculate the items i want to add, its a little bit too complicated, but in the end it works like:
for(int i=0;i<10;i++){
View x = inflater.inflate(R.layout.dynamiccustomlayout,null);
container.addview(x);
}
}
}
XML for the Form
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/flxform"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:background="#android:color/transparent"
android:orientation="vertical" >
</LinearLayout>
Then you can instantiate a "Form" Objekt and add it into a ScrollView
For doing this You would have to nest your RelativeLayout inside a ScrollView and Manage all the Scrolling, items adding, memory management, etc manually.
So the simple solution for adding n Number of Custom Views is to use a RecyclerView, ListView, GridView, etc with a neat CustomAdapter and Your Custom View.
Here is a nice example of using RecyclerView with custom Adapter :
http://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
I hope this Helps.
Is there a way to create an array containing actual instances of views. For example, if I have one LinearLayout called Container that has within it 3 other LinearLayouts with the same Tag attribute and I wanted to get a list containing all 3 LinearLayouts so I can loop through and handle each.
Tried:
LinearLayout[] layouts = (LinearLayout[]) Container.FindViewWithTag(tag);
and
List<LinearLayout> layouts = (List<LinearLayout>) Container.FindViewWithTag(tag);
and
foreach(LinearLayout layouts in Container.FindViewWithTag(tag))
None of these have been acceptable to Android so far. Another acceptable way to handle my situation would be to just be able to assign each LinearLayout a Parent. But I haven't found a way to programatically set a view's parent, only how to get a view's parent.
What I have understood from your question that you want the child of Linear Layout. Yes, you can get the child of linear layout by container.getChild(index)
for(int i=0;i<container.getChildCount();i++){
View child=container.getChildAt(i);
//your processing....
}
For an Android App, I'm using a GridView and extending BaseAdapter to organize its contents. For the function getView that I override in my extended BaseAdapter class, I create a LinearLayout, which I attach an ImageView and 3 TextViews to. Now, I need to implement convertView, but because I created my views programmatically, I didn't think I can use findViewById to find these child views to change their properties (like text and bitmaps).
I had the idea of assigning a unique ID pertaining to different types of views for each one when I create them (example: give my name textview the id of 1, description textview the id of 2, etc), but I was not sure if the ids have to be unique among every view, whether they're the same kind of view or not.
Ultimately, how do I find a child view that's part of a linearlayout view which were all created programmatically?
You can get children of LinearLayout via getChildAt() but it's hard to distinguish what child exactly you get.
I think assigning IDs is better way. You can assign IDs to your views and later get views via findViewById(). IDs doesn't need to be unique for each LinearLayout.
Something like this:
// Give more sensible names to ID's
int IMAGEVIEW_ID = 0;
int TEXTVIEW1_ID = 1;
int TEXTVIEW2_ID = 2;
int TEXTVIEW3_ID = 3;
imageView.setId(IMAGEVIEW_ID);
textView1.setId(TEXTVIEW1_ID);
textView2.setId(TEXTVIEW2_ID);
textView3.setId(TEXTVIEW3_ID);
...
// somewhere later
ImageView imageView = (ImageView) convertView.findViewById(IMAGEVIEW_ID);
I am trying to create a simple game. I would like to populate a LinearLayout which I have defined in an XML with several RelativeLayouts. Each time the user presses a button I would like a child View to be added to the LinearLayout. Each RelativeLayout would change slightly based on the users input (which buttons were pressed, etc).
I essentially want to create a new RelativeLayout based on an XML layout file. I then want to manipulate some attributes of the RelativeLayout's child Views (specifically the src of some ImageViews) and add it to a LinearLayout. This in itself is not particularly difficult. I can get at each of the RelativeLayout's children using findViewById but I start having problems when I want to create n number of RelativeLayouts based on the same XML. I'm pretty sure duplicate IDs are causing crashes. Can a RelativeLayout work without using IDs? Should I try and find a different way of constructing the interface using different ViewGroups?
I'm not sure if what I am asking is even possible but I know that creating these items on-the-fly using the code alternative to the XML layouts is a horrible thought.
Thank you in advance.
EDIT: I lost my train of thought halfway through, the previous example had some bugginess. I've updated.
pedr0 has the right idea, but to clarify, you could try something to this effect (assuming you have a RelativeLayout defined in relative.xml). This isn't tested, but the general idea should be valid. You don't even have to do a separate method, you can do it inline in a click handler, or whatever, but I just did the addChildView method for the sake of example:
LayoutInflater inflater;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//get the LinearLayout that you plan to add rows to
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.linear_layout);
//get the LayoutInflater from the system
inflater = getSystemService(LAYOUT_INFLATER_SERVICE);
//call getNewChildView with whatever drawable id you want to place
//as the source. If you want to pass in other types, just change
//the parameters (e.g. Drawable, Bitmap)
linearLayout.addView(getNewChildView(R.drawable.my_image));
}
public RelativeLayout getNewChildView(int resId) {
//inflates a copy of your RelativeLayout template
RelativeLayout rl = (RelativeLayout)inflater.inflate(R.layout.relative, null);
//this assumes an ImageView in your RelativeLayout with an id of image
ImageView img = (ImageView)rl.findViewById(R.id.image);
img.setBackgroundResource(resId);
return rl;
}
EDIT: I couldn't even work out how to use the code tags properly sigh
Thank you both for your quick responses. I had something very similar in mind and in fact was on the same track and using the LayoutInflator. I simplified your example a little as I do not need to pass the id of a drawable around.
LayoutInflater inflater;
private void drawGuess() {
// The top level LinearLayout to add to
LinearLayout topLevel = (LinearLayout) findViewById(R.id.guessList);
// get the inflater`
inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
// add the view (based on stored data elsewhere)
topLevel.addView(getLatestGuess());
}
private RelativeLayout getLatestGuess() {
//inflates a copy of your RelativeLayout template
RelativeLayout rl = (RelativeLayout)inflater.inflate(R.layout.guess_layout, null);
//this assumes an ImageView in your RelativeLayout with an id of image
ImageView guessOne = (ImageView)rl.findViewById(R.id.guess1);
guessOne.setImageResource(R.drawable.red);
}
Running this code works, the catch is it only works the first time you call it. The second time you call getLatestGuess() it crashes. Removing the IDs in the XML (e.g. guess1) from all the child views of the RelativeLayout results in no more crashing. Unfortunately now I have a fairly useless RelativeLayout that I can no longer get at the child views of as they do not have IDs. Presumably, IDs are required to be unique.
I think you have to create an root element, and then add the new child.
Like this:
RelatoveLayout root = ....;
RelativeLayout toadd = inflate(your XML);
apply by code change depending of users input, for example:
toadd.setBackgroundResource(....);
and finally add the result to the root :
root.addView(toadd).
I really hope to help you!
Bye!