I have the following hierarchy in my layout:
ScrollView
RadioGroup
RelativeLayout
RadioButton
ImageView
RelativeLayout
RadioButton
ImageView
...
Now the point is, that it looks fine in the XML editor where RadioButtons and ImageViews have default values (placeholders) defined, but when I launch an activity and call removeAllViews() on the RadioGroup, all ImageViews disappear. What's interesting, all buttons get new values, only ImageViews are not updated anyhow (setting new source images gives no results).
So, my question is: does removeAllViews() erase the child views completely (like they never existed in a layout XML file) or just removes some values leaving the views' arguments to be defined (like setting new source image or new button description)?
From the official documentation, removeAllViews():
Call this method to remove all child views from the ViewGroup.
Calling this method will set all the childs view to null, so it will remove the child views from itself, and this child becomes not valid (or not considered as child), but not like it never existed in XML file.
This is removeAllViews() code:
public void removeAllViews() {
removeAllViewsInLayout();
requestLayout();
invalidate();
}
As you can see in removeAllViewsInLayout() method, it set the child value to null:
children[i] = null;
Related
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.
On my activity's creation I have set an AlphaAnimation in order to perform some visual effects. Now I have set a LayoutAnimationListener and overriden the onAnimationEnd() method. The Animation is executed correctly and the callback to onAnimationEnd() is working as well. Within the onAnimationEnd() method I want to perform another animation on two child objects of the Activity's layout. These are two EditTexts that are defined in XML with the property android:visibility="gone". For starters, what I want to do is to have them set as VISIBLE.
I've tried getting a reference from their parent, change their setVisibility(View.VISIBLE); and in the end invalidate the parent view.
This attempt does not seem to work. You will find the code attached below:
#Override
public void onAnimationEnd(Animation animation) {
Log.d(TAG, "onAnimationEnd()");
// TODO move icon up, make edittext's appear.
RelativeLayout layout = (RelativeLayout) View.inflate(
getApplicationContext(), R.layout.splash_dialog_layout,
null);
LinearLayout linear = (LinearLayout) layout.getChildAt(0);
Log.d(TAG, "Children" + linear.getChildCount());
email = (EditText) linear.findViewById(R.id.splash_Email);
email.setVisibility(View.VISIBLE);
pass = (EditText) linear.findViewById(R.id.splash_password);
pass.setVisibility(View.VISIBLE);
linear.invalidate();
layout.invalidate();
Splash_Activity.layout.invalidate();
}
};
I've tried to removeAllViews from a parent and add them one by one and invalidate again but that doesn't seem to work either.
There is probably a misunderstanding on my behalf as to how view inflation operates, shouldn't this work?
Thanks for your time.
Inflating a layout creates a new view hierarchy, which means the views you're obtaining are actually different from the ones displayed on screen even though they have the same IDs.
You can obtain on-screen views in several ways, but the easiest would probably be to query the containing Activity.
Activity.this.findViewById(R.id.splash_Email);
In an activity I am changing one of the ViewGroup content runtime: buttons action, other events.
At a specific case I need to check if a child is in this layout or not ( in this case the child is another RelativeLayout, which hold other views)
How can I check runtime, programmatically if child_1_RelativeLayout is there or is already removed from view tree, his parent is parentRelativeLayout
The getParent() is usefully? - not much explanation how to use it, thanks.
In case you have stored your views you can use getParent() to check if view is a direct child for other view. After removing view its parent field will be cleared. For example:
ViewGroup parent = ...;
View child = ...;
assert(child.getParent() == parent); // <-- true
parent.removeView(child);
assert(child.getParent() == parent); // <-- false
Not really sure if I understand your question correctly, but:
when you add the relative layout, be sure to first give it an id (either in your layout.xml file, or from code)
to check existence of the relative layout within the ViewGroup, use ViewGroup's findViewById() method (inherited from View) and pass it the id you've given to the relative layout
if it returns null, the relative layout is not there, otherwise findViewById() will find it
So in short, findViewById() is not only defined for an Activity, but you can call this on any view you would like to use as a starting point for your search.
Short Story:
I have a layout "layout.xml", which gets replaced by another layout "success.xml" after a successful web request. Both layouts have an ImageView that provides the backgrounds to the layouts. These 2 backgrounds both need to be the same, and both are dependent on a user preference.
Longer Story: This all happens in a Fragmnet with an AsyncTask replacing the contentView with "success.xml" in onPostExecute after the web request. This happens as follows:
View view = getView();
view = null;
view = View.inflate(context, R.layout.success, null);
What I tried to do is give both ImageViews the following android:id="#+id/background_image" and then call
ImageView background = (ImageView)view.findViewById(R.id.background_image);
background.setImageResource(R.drawable.bg1);
This background-setting works for the initial view (layout.xml), but on trying to change to "success.xml", I get a NullPointException because background is null.
I've checked and the View's id is set to -1 while the original view's background_image id is set to something sensible and valid.
I've also tried setting the second view's background id like this: android:id="#id/background_image", i.e. without the '+', but still no luck.
The added complication is that it's not just 2 layouts, but about 5 that I need to do this for, so it would be really handy to recycle view id's.
Any help would be greatly appreciated.
Your code for replacing the fragment's view will not do what you want, the original view will remain the same as you change only a reference to that view and not the actual object.
To replace the view of the fragment with the new layout you could have another ViewGroup(for example a FrameLayout) in the basic layout (layout.xml) wrapping your current content(don't forget to give it an id) of layouts.xml(as I understand this is the basic layout). Then, when it's time to replace the layout you could simply do:
// remove the current content
((ViewGroup) getView().findViewById(R.id.yourWrapperLayout)).removeAllViews();
// add the new content
View.inflate(context, R.layout.success, ((ViewGroup) getView().findViewById(R.id.yourWrapperLayout)));
You could avoid adding an extra layout if, by any chance, all your five layouts have the same type for the root view(like a LinearLayout etc). In this case you would use the same code as above but you'll modify the other layouts file to use a merge tag. Also, you'll be looking for the id of the root in the layout.xml layout into which you'll add the content of the other files.
Then you could have the same ids, but you'll have to reinitialize any reference to the views(meaning that you'll have to search for the view again if you store a reference to the view(like a Button field in the fragment class)).
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....
}