I have a View that inherits from LinearLayout that inflates itself. After it has been created and inflated I wanted the ability to inflate a different layout. However, calling inflate() a second time seems to have no effect. Only if I close the activity and open it again is the state reflected in the UI.
Is it possible to call inflate multiple times to dynamically change the layout?
My inflate code is pretty simple:
layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.my_layout, this, true);
Is it possible to call inflate
multiple times to dynamically change
the layout?
Try removing your child views first. Right now, you are appending the new stuff after the old stuff, AFAICT.
Related
ViewSwitcher can be used only for two childs in it. But for a group of textviews to be converted to Edittexts what can be the ways?
It's easy enough to replace views for other views but converting - I don't think so.
To replace views, you can simply do something like the following:
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.edit_texts_layout, null);
viewSwitcher.removeAllViews();
viewSwitcher.addView(view.findViewById(R.id.first_edit_text));
viewSwitcher.addView(view.findViewById(R.id.second_edit_text));
So what's going on here?
1) You create a layout called edit_texts_layout which contains two EditText's with the ID's: first_edit_text and second_edit_text.
2) You then inflate this in your Activity or Fragment.
3) You then remove all of the existing views in your ViewSwitcher since, as you rightfully said, there can only ever be two views in a ViewSwitcher.
4) You then add the two EditText's inside that ViewSwitcher by view.findViewById()
Also...
It would be wise to have another layout called text_views_layout and do the exact same thing so that you can switch the EditText's with the TextView's.
But why have the EditText's inside a layout and not create them programmatically?
Good question. Views should live in layouts and live separately from code when possible. It makes life easy where you can work with a view in a layout since you have XML autocomplete and the preview screen so you know exactly what the view looks like - saving you from any nasty surprises later on.
Are there any other alternatives to this?
There's always more than one way to skin a cat and this situation is no different. You could possibly create TWO ViewSwitcher's and with different child views. What you'd have to do then is toggle the visibility to View.GONE and View.VISIBLE alternatively. The caveat? It'll be troublesome to maintain both in memory and to perform any operations on the child views as you'd have to remember which one is visible and which one isn't. With my original answer, you won't have to worry.
I have an ImageView that I attach to a MenuItem as its ActionView (the item appears in the ActionBar). The layout for this view comes from XML. I'm inflating it like so:
ImageView actionView = (ImageView) layoutInflater.inflate(
R.layout.action_view_layout, null);
This appears to work fine. However; passing null for root in the call to inflate() makes Lint yell at me:
Avoid passing null as the view root (need to resolve layout
parameters on the inflated layout's root element)
I can seemingly manage without a root in my specific case, but I'd rather have the code be as correct as possible. The problem is, I'm not sure which View should be used as the root here. This answer says it should be "the widget that is surrounding the view objects that you want to inflate." But what does that mean here? The one for the action bar? The activity? Something else entirely?
Update: Reading the answers has made me suspect me the right thing to do is:
Get the ActionBar View corresponding to the MenuItem
Get its root
Cast the root to a ViewGroup
Pass the result to the inflater
This seems to work. Can anyone confirm or deny whether this is what should be done?
I would simply do it like this:
menuItem.setActionView(R.layout.action_view_layout);
Let Android inflate the view for you.
If you need to do some extra changes on this ImageView call
ImageView imageView = (ImageView) menuItem.getActionView();
Update
In order to cater to your curiosity. That is what folks from Google do under the hood:
public MenuItem setActionView(int resId) {
final Context context = mMenu.getContext();
final LayoutInflater inflater = LayoutInflater.from(context);
setActionView(inflater.inflate(resId, new LinearLayout(context), false));
return this;
}
You have look on this. it nicely explains the Layout Inflator as well.
There are two usable versions of the inflate() method for a standard application:
inflate(int resource, ViewGroup root)
inflate(int resource, ViewGroup root, boolean attachToRoot)
The first parameter points to the layout resource you want to inflate. The second parameter is the root view of the hierarchy you are inflating the resource to attach to. When the third parameter is present, it governs whether or not the inflated view is attached to the supplied root after inflation.
It is these last two parameters that can cause a bit of confusion. With the two parameter version of this method, LayoutInflater will automatically attempt to attach the inflated view to the supplied root. However, the framework has a check in place that if you pass null for the root it bypasses this attempt to avoid an application crash.
Many developers take this behavior to mean that the proper way to disable attachment on inflation is by passing null as root; in many cases not even realizing that the three parameter version of inflate() exists.
More on Layout Inflation
You generally want to pass whatever (ViewGroup sub-class) you're going to be adding actionView to in to inflate. in order to get actionView back from the inflate call and not the parent you'll want to add a 3rd parameter, false, so that it won't add the inflated view to the parent.
ImageView actionView =
(ImageView)layoutInflater.inflate(R.layout.action_view_layout, parent, false);
// .. do whatever you like with actionView and then add it to it's parent
menuItem.addActionView(actionView)
There's a pretty good tutorial here that goes about things a little differently. It's specifying action_view_layout as part of menu.xml with something like:
android:actionLayout="#layout/action_view_layout"
That may also work for you provided you're always using the same layout. if you go that route you'd be able to get the ActionView by doing
ImageView actionView = menu.findItem(R.id.whatever).getActionView();
I made a viewstub that is inflated and created again many times. The layout it uses includes a "delete" button that hides the viewstub (or deflates it).
When I'm creating a new viewstub I call this code:
ViewStub eventStub = new ViewStub(this);
eventStub.setLayoutResource(R.layout.event_container);
eventContainerMain.addView(eventStub);
eventStub.inflate();
Problem is, in the viewstub layout the delete button is created with the new viewstub. So how do I make the delete button hide the viewstubthats inside?
Most importantly, How do I make other methods affect only the view its inside?
You aren't quite using view stubs correctly...
First, point your view to the layout you desire for the view you want to duplicate by using this:
ViewStub stub = new ViewStub(this);
stub.setLayoutResource(R.layout.viewStubLayout);
stub.inflate();
Next, go into your XML and make sure the buttons on your layout have the android:onClick option using the correct method, which is declared in your class.
For deleting view stubs, you don't actually delete them... you're supposed to use .setVisibility(GONE) or .setVisibility(VISIBLE) to manage if users can see it or not. This would be used in the method in which onClick is directed to. Also, using "this" when referring to the view stub your objects are in will allow you to manage what happens in the specified view the user clicks in.
You do not delete view stubs in the way that you want them to be deleted. When inflated, ViewStubs simply disappear from the parent and are replaced with a View object. If you want to use methods on the view that is created you can use the android:inflatedId attribute in the ViewStub xml file.
You say that the layout you are using has a "delete" button that "deflates" the ViewStub, but in reality what is happening is that you are deleting the View that is created when you inflate the ViewStub. Once a ViewStub is inflated it is deleted from the parent automatically and so after inflation there wouldn't be a Viewstub in existence to delete.
What it sounds like you want is to dynamically delete the View created by the inflated ViewStub. For this I suggest looking at Add and Remove Views in Android Dynamically?
I would like to create instances of all my views in application once and during execution pass proper view to Activity.setContentView() - by reference not by id.
So - how to properly create view knowing its id?
Additional question:
How is view instance created when I pass id into Activity.setContentView?
Every time I call setContentView new instance is created?
regards for all
Why do you want to do this? My disclaimer is that holding instances for ALL your views in one activity is excessive and unnecessary. You should probably split the functionality of your code into multiple activities and have one layout associated with each.
If you must use multiple views within the same activity, this type of behavior is probably better done within your layout file (putting multiple views in your layout, then showing/hiding them in your code).
That being said, what you'll need is the LayoutInflater class. Inflate the layout and do what you want with it. If you're looking for a specific view within this layout, use the findViewByID() method on that inflated layout.
LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE);
LinearLayout ll = inflater.inflate(R.layout.your_layout, null);
View v = ll.findViewByID(R.id.view_name);
//Do stuff with view or layout
I have created a compound control that I am reusing in multiple activities. This control contains a bunch of TextViews and Buttons, and most importantly a ListView. I define the XML in a layout file and in the constructor to this compound control, I inflate the XML as such:
String service = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(service);
inflater.inflate(R.layout.map_menu, this, true);
The layout XML contains a ListView, and also in the constructor this compound control will handle setting up the adapter (my adapter extends ArrayAdapter) and array for it, like so:
ListView tableOfContentsList = (ListView) findViewById(R.id.tocListView);
_layerAdapter = new LayerAdapter(context, R.layout.toc_layer_item, _layers);
tableOfContentsList.setAdapter(_layerAdapter);
This compound control is used in two activites - one of these activities calls another. No relation between the two activities is intended.
Here is my problem. When the compound control is created in the initial activity, the above code is called to set the adapter of this control. Then, when the second activity is created and navigated to, the constructor is called again on this second instance of the control. This seems to have a side effect on the first control located in the initial activity. The second control seems to overwrite parts of the adapter from the first control - because basically the first adapter will not be functional once the constructor to the second control is called.
It is my guess that since I am referencing the resource ID of the ListView in both controls, Android is removing the adapter from the first ListView when the second ListView is created - because it sees both ListViews as having the same resource ID? Is this possible?
I have had trouble before in this exact same case - where multiple compound controls are used in different activities (and multiple times in a single activity) - and the problem was due to inflating from XML layout. My solution to that prior problem was to get rid of the inflating from layout, and instead creating the objects through code. This was acceptable because those compound controls were much simpler and contained only two views - however I feel in the above ListView case, where my compound control has at least ten views in it, it is not an acceptable solution to define each view in code. I need the layout XML.
Has anyone ever experienced this sort of clashing behavior when using custom compound controls that are inflated from XML, and re-used in multiple instances?
From my understanding Android should create a new instance of the widgets each time you inflate the xml. Do you have any static members in you compound widget class?