Dynamically hide a viewstub when a new one is created - android

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?

Related

How to specify which view is clicked in layout added multiple times

I have a layout which takes user's children information. Let's say that this layout takes info about the children's first name, last name, and age. I am taking inputs with EditText. And there is a spinner which shows children Allergy type, on selecting 1 Allergy type it has to fetch its details from webservice and to fill those details in a textview.
So in this way the User can add as many children as he wants. The problem occurs there. Let's say the user has added that layout 4 times now, he selects the spinner of children 1 and service gets called and it fetches the information and fills it in the last layout textview that was added.
where as it is expected the details should fill in each textview of each layout added accordingly.
Confusions :
How can I exactly get which view is clicked and then how to take action in the same layout of that view not the one which is added recently?
I am inflating layout which has the specific set of fields specified above. So I am maintaining the Array List of each layout added , so Is there a work around to get exactly the same view and its corresponding view in that layout ??
UPDATE 1: Some Idea of my question
There is a main activity it has 1 button named "Add More Children". When user click on this the layout which contains the children info adds in the specified area in the ScrollView, so that user can add as many children as much he want.
so basically I have drawn some views below where as there are too many views in the following layout named children layout.But this layout shows what type of work is need to be done . so see below and have some idea
For demonstration you can see there are different edit text and spinners. Spinners get updated from Webs service and each children may have different data loaded in spinner from webservice. this whole layout inflates into the mainactivity. on button click named Add Children . so on that button I am adding this in the scrollview
private View addChildLayout(int childLayoutid, LinearLayout Targetlayout) {
//where childLayoutid is a layout resourse id of childern layout
// where Target layout is a scrollview in my fragment
LayoutInflater inflater = LayoutInflater.from(getActivity());
View inflatedLayout1 = inflater.inflate(childLayoutid, null, false);
inflatedLayout1.setId(numOfChildAdded);// numOfChildAdded is int number of children added by user so far
Targetlayout.addView(inflatedLayout1, 0);
numOfAddedChildLayout.add(inflatedLayout1);//this is an array list i am creating to keep track of each layout added by user on add button click
return inflatedLayout1;
}
after adding this in layout another method I call that finds the ids of this layout and sets the click listener of each views i.e spinners or edit text or whatever is needed. Now suppose User has added one children. and clicks on the spinnerAllergy types that fetches sub category and populate spinnerSubCatogry, and spinnerSubcategory when clicked the web service again gets called and gets details of allergy and fills in the Textview (the large box shown in picture)
Now the main problems comes in when user added Children no 2. now let suppose user has added 2 childs , but he clicks on the spinnerAllergy of Child1 , the child1 spinners runs the web service but populates the spinnerSubcategory of Child2.
It Looks like that when the new child is added the click listener refers to new layout which is newly added .
so that is the main problem . I want each view in each layout work accordingly in its views and boundaries. I mean child1 layout views click listener should populate its views not the currently added layout views.
I think that is enough information to get some clear idea.
Please help me with this, I've been stuck here for 2 days. Where as all my design is working good.
Well as you stated above that you are keeping track (List of ) every single view you have added. I will suggest you to use that
Here are the lines you are using and setting the id so its mean each parent has the different id where as their child views has the same id ,
View inflatedLayout1 = inflater.inflate(childLayoutid, null, false);
inflatedLayout1.setId(numOfChildAdded);
As you are adding each layout with different ID why dont you simple get the parent and then again get the child with the specific id , for demonstration
Suppose there is a textview in you layout , and that under the Linear layout where as that linear layout has a relative layout as a root/main layout , and every main layout has different id as you have done above. so this is how you will go to the top(parent ID)
ViewGroup row = (ViewGroup) yourTextView.getParent().getParent;
TextView textView = (TextView) row.findViewById(R.id.tvClassLevels); // the next view you can find
so here I will suggest to do this with each view and then set click listener.
this may not be more efficient but this would work. I have done that once When I was inflating my custom layout but keep that in mind every time inflating though may be quick but it would be costly , you need to implement some logic near to getview (as we have its implementation in adapter)
Why I am doing getParent().getParent() twice
As I said that my textview is in linearlayout and that linear layout lies in the main/root layout and hence as we want to approach the mainlayout because we know it is the only one which has different Id , so we are doing getParent() twice as Textview has LinearLayout as first Parent and then the root layout comes, so in this way if you have a view in another layout , you need to dig it by yourself.
Again I am saying , it may be not a cool or best implementation , but it works . At least it worked for me.
I think the problem is : Different views in your layout have SAME id since you're inflating from the same xml layout.
You have different approaches to solve this:
Keep track of a variable and increment the count whenever you add a Child View. Once the inflation is done, you call setId on the newly created View(just as mentioned in Abdul Salam Ali's answer). Although view ids in android are generated at compile time(correct me if I'm wrong), this should work perfectly in practice.
Create a Ids resource file in res/values folder and pre-define a few Id values for future use. It's guaranteed that all id values will be distinct. But as you say, users can add as many child info as they want, this may not be the best choice.
Use Random to generate different ids.
Please note that even if you have same Ids in your layout, your code logic would be correct as long as the childs of ViewGroup on which you call findViewById(or sth similar to this) has a unique id. See this
For some suggestion to your design.
Use the listview and adapter design instead of generating the view in programming.
a. Add the new item
Used floating action button. https://material.google.com/components/buttons-floating-action-button.html#buttons-floating-action-button-floating-action-button
B. Remove the item
Used some list control or multi selector to remove the undesired items.https://material.google.com/components/lists-controls.html#lists-controls-types-of-list-controls
Use listview to show your item instead of generating layout in your code.
https://material.google.com/components/lists.html#lists-usage
To edit the specific child information, suggest to use the full screen dialog for editing.
https://material.google.com/components/dialogs.html#dialogs-full-screen-dialogs
The above is my suggestions toward your design, since it would be difficult if you want to get the clicked view's parent to find your actual position in the layout, and then send to to listen the corresponding on click event. It would be hard to maintain and your code will be hard to debug if there is any nested view in your parent. Try to separate each part to help your debug what is wrong in your code.

Android: Changing layout in same activity

In my application's main layout I have a button of Search task. Now when user clicks that button I want to change the layout xml file to search_layout.xml.
I don't want to create a new activity for Search task and want to use my activity_main.java class for its coding. So how do I inflate search_layout from existing activity_main.xml .
My activity_main layout is Coordinating layout with Collapsing toolbar and Recycler view.
In my search_layout.xml I have a Simple relative layout.
So how to deal with this ? I searched for this but everywhere I get how to inflate view adding to an existing view. But in my case I want to totally change view.
Thanks
You can simply call setContentView(R.layout.your_search_layout) in the click listener of your button.
Example -
yourButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
setContentView(R.layout.your_search_layout);
}
});
However, it is always good practice to make your code modular, hence you should consider using Fragments to achieve this.
You can have a look at this question, but, this is not a good practice to follow and you should consider using Fragment or any other mechanism. By doing this you are cluttering your Activity's logic with search logic.
There are two ways to do it: one is make frame layout the parent tag for your layout and then create two layouts: one that you have already created and and one for search layout framed over it and make visibility for search layout GONE and when you click search make its visibility visible.
The second method is to create the new XML layout file for search layout and inflate it like this:
View v = getLayoutInflater().inflate(R.layout.YOUR_LAYOUT_ID, null);
and elements of this layout will go like this:
Button button=(Button)v.findviewbyid(R.id.your_button_id);
Changing visibility of view will help in your case where you totally want to change the views
I think in your case, you should fragments to handle this situation.
Read this for more details on how to replace Fragments using FragmentManager.
Here's a nice example on how to change fragments in android :

Android Compound Controls - Inflate from XML

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?

Adding a view on runtime to a ViewFlippers

I have defined two views ExampleView1, ExampleView2, ExampleView3 and ExampleView4 in resources.
In my Activity I have an empty ViewFlipper. Based on doing some logic I want to add either ExampleView1 and ExampleView2 to the ViewFlipper and show the view.
Later I want to add based on internal logic either ExampleView3 and ExampleView4.
How do I do this? Is there some tutorial or can someone help me with example code?
Just use the addView method, which ViewFlipper inherits from ViewGroup. If your views are custom ones, you will have something like this:
flipper.addView(new ExampleView1());
On the other hand, if the views are defined inside an XML layout, you will have to inflate them first:
View view = LayoutInflater.from(context).inflate(R.layout.your_view, null);
flipper.addView(view);

When do I need inflaters?

I'm reading the book 'Hello, Android'. In the Sudoku example, it uses a options menu. It needs a MenuInflater that we use to read the menu definition from XML and turns it into a real view.
To use button, textview, or many other views, I don't need to inflate them.
My question is, in what situations, I need inflaters? Why doesn't Android treat menus like other views?
You need an inflater at every place that you want to dynamically create a view out of an XML file.
Activity layouts are automatically inflated when you call setContentView() as they're always required.
But when the menu is required — which is only when the user first presses the Menu button — the XML-defined layout needs to be manually inflated.
Similarly, if you have a ListView, you don't know in advance what rows will exist, so we have to inflate a View from XML for each row in the list, as they're required.
Inflaters are mainly used for parsing Xml layout into view objects. As mentioned above the inflation is needed for making a link between the UI defined in the Xml for manipulating and making developer.
Whenever UI Updation is needed we need inflation and UI Updation is done through view object and developer can dynamically create view and add to existing view.
Hence inflation helps developer to change behaviour of UI in xml layout according to specified condition in a program.
With inflation, we are able define controllers in MVC for each xml layout where xml is view.
Menu is also a view it has to inflated In certain code such setContentView(specifiedLayout) includes inflation
But in earlier version it was not like this it was like setContextView(getInflater().inflate(specifiedLayout))
for ease of programming,android developers have incorporated inflation in setContentview() and there are lot scenarios like add view to layout addView(),etc..In most cases inflation has incorporated in code that why most of beginner does know inflation concept and has difficulties in understanding inflation in android.

Categories

Resources