Inflate ViewStub with a native widget - android

Is it possible to inflate a ViewStub with a native android widget?
For example:
<LinearLayout...>
<TextView...>
<TextView...>
<ViewStub...>
</LinearLayout>
I want to replace/inflate the ViewStub with a Switch or a Checkbox. Is that possible?

To set the layout to inflate into the ViewStub either:
Set the layout via XML:
<ViewStub
android:id="#+id/stub"
android:inflatedId="#+id/myLayout"
android:layout="#layout/myLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Or, set the layout at runtime:
viewStub.setLayoutResource(R.layout.myLayout);
Then, you can inflate the ViewStub.
View inflatedView = viewStub.inflate();
You could call this inflate() method from where you want, such as a Switch or Checkbox being clicked (as you asked).

Currently there is no way to replace the StubView with a View object. You must create a layout representing the view you want to replace. So for example, if you want to switch it with a single CheckBox, you need to create a layout like this
<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_checkbox"
android:text="Click me"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Then calling
viewStub.setLayoutResource(R.layout.my_checkbox);
viewStub.inflate();
If you need a reference to the inflated view you can use this instead
CheckBox checkInflated = (CheckBox)viewStub.inflate();

Related

Reuse a rendered layout in some other layout at run time

I am using a RecylerView inside a layout l1.xml. I am including this l1.xml inside l2.xml using include tag.
I update this RecyclerView after an api call but l2.xml is not showing the updated RecyclerView.
Is there a way to forcibly ask the parent to refresh?
invalidate(), refreshDrawableState(); on the parent layout didn't help?
Is there a smarter way to use a rendered layout in multiple places?
l1.xml
...
...
<LinearLayout
android:id="#+id/feed"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4"
android:background="#color/light_primary_background">
<include layout="#layout/events_list"/>
</LinearLayout>
...
...
events_list.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/events_recycler_view"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
I update the events_recycler_view after an API call and the events_list.xml is updated but the include in l1.xml is not updated
Yes you can always use LayoutInflater to inflate a view, but the view must have the ids and type matching the id and type defined in your java code.
Check out this link for how to use layout inflater
http://www.programcreek.com/java-api-examples/android.view.LayoutInflater

StackOverflowError when inflating custom view which contains its own views

I am having a Fragment, where I inflate "fragment_board.xml":
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<view
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.app.BoardView"
android:id="#+id/view_board"/>
</FrameLayout>
As you can see, fragment_board contains a custom view "BoardView", from where I want to load the following "view_board.xml":
<com.app.BoardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_vertical">
<android.widget.HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_horizontal"
android:foregroundGravity="left">
</android.widget.HorizontalScrollView>
</com.app.BoardView>
My custom view contains two scroll views (I use it for panning), and I want to be able to re-use it in other layouts. The BoardView extends the outer (vertical) scroll view like this:
public class BoardView extends ScrollView
When I use it stand-alone, it inflates fine and I can findViewById() both scroll views in the inflated layout. But when I use it in a layout tree (such as a fragment), I run into problems.
In the fragment, I inflate the layout tree like this:
View view = inflater.inflate(R.layout.fragment_board, container, false)
From the fragment, I can findViewById() the outer (vertical) scroll view, but findViewById() returns null for the inner (horizontal) scroll view.
In BoardView, I inflate like this:
View view = inflate(getContext(), R.layout.view_board, this);
As said, I can find both scroll views fine when inflated by itself, but when inflated as part of the fragment, I get StackOverflowError.
It is clear why: I inflate in the fragment first, and then I inflate the same XML in the view again, which triggers another inflation of the view and so on. I get a cyclic reference here. Problem I can't figure out how I can add the inner (horizontal) scroll view to the already existing layout. I think I need to merge somehow or inflate layouts manually, but I can't figure out how.
Here are some refs (which didn't help in my case):
Android - Writing a custom (compound) component
How to inflate XML-Layout-File correctly inside Custom ViewGroup?
Create a custom View by inflating a layout?
InvocationTargetException on inflating an xml - android
Android: StackOverFlowError with InvocationTargetException when inflating layout
Can anybody suggest what I should do. If possible, I'd want BoardView to work as a generic component, which I can plug into a layout tree where needed.
As Android [according to one of the refs above] does not officially support composite components, would my best option be to drop XML layouts for the inner views and add them in code?
Please check if something like this fixes the issue:
fragment_board.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.app.BoardView
android:id="#+id/view_board"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
view_board.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_vertical">
<android.widget.HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_horizontal"
android:foregroundGravity="left">
</android.widget.HorizontalScrollView>
</merge>
More information on using MERGE and INCLUDE

Different results when using the LayoutInflator's inflate methods

I want to know how the LayoutParams will work on LayoutInflator. And what is difference between:
LinearLayout childLayout=(LinearLayout)inflater.inflate(R.layout.childitemlayout, null); //FIRST WAY
LinearLayout childLayout=(LinearLayout)inflater.inflate(R.layout.childitemlayout, container,false); //SECOND WAY
Because, both methods gives me different result.
Actually second inflate method are gives me correct result for both child layout change, but First method will gives me different result.
Here is my code:
MainActivity.Java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout mainLayout=(LinearLayout)findViewById(R.id.mainLayout);
LayoutInflater inflater=LayoutInflater.from(getApplicationContext());
for(int i=0;i<10;i++){
LinearLayout childLayout=(LinearLayout)inflater.inflate(R.layout.childitemlayout, null); //First WAY
// LinearLayout childLayout=(LinearLayout)inflater.inflate(R.layout.childitemlayout, mainLayout,false); //SECOND WAY
mainLayout.addView(childLayout);
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
childitemlayout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="80dp"
android:layout_height="80dp"
android:orientation="vertical"
android:background="#525f67">
<TextView android:id="#+id/btn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Button"
android:gravity="center"
/>
</LinearLayout> <!-- Both ways gives different result -->
<!--
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#525f67">
<TextView android:id="#+id/btn"
android:layout_width="80dp"
android:layout_height="80dp"
android:text="Button"
android:gravity="center"
/>
</LinearLayout> Both method gives SAME result -->
The main difference between the two inflate() methods is the second parameter(the ViewGroup parameter) and its use in setting the proper LayoutParams for the root view of the inflated layout file. This is important because the LayoutParams keep various layout attributes of the view(like width, height, positioning rules etc) and are required so the parent of that view can properly show the view.
The first method basically says: build the hierarchy view from this layout file but don't assign LayoutParams to the root of the inflated hierarchy(maybe because the parent isn't know yet), also don't attach the inflated view to a parent.
The second inflate method says: build the hierarchy view from this layout file and also assign the proper LayoutParams(based on the second parameter given to the inflate method) to the root of the inflated hierarchy, also don't attach the inflated view to a parent.
In the first case, the root of the inflated layout file(R.layout.childitemlayout) will not have any LayoutParams set on it(the inflate method didn't assign any because the second parameter is null and it doesn't know which type of LayoutParams to generate), so your fixed width/height values are lost. Later when you'll do mainLayout.addView(childLayout); the mainLayout will check the LayoutParams of the childLayout, see that those are null and will automatically set an instance of the LayoutParams(using its generateDefaultLayoutParams() method). This method, in the particular case of a horizontal LinearLayout, will return an instance of LayoutParams where the width/height will be set to WRAP_CONTENT. So your childLayout will end up with WRAP_CONTENT as its size instead of the fixed values you set on them.
In the second case, the inflate method sees that you suggested the LinearLayout mainLayout as the ViewGroup used for generating the LayoutParams. This means that the fixed values(that you used for the width/height) retrieved from the layout file can be stored in a proper instance of the LayoutParams. When you'll do mainLayout.addView(childLayout);, mainLayout will see that childLayout has the proper LayoutParams instance(which has the values used in the layout file) and doesn't call its generateDefaultLayoutParams().

Android Layout Templating [duplicate]

This question already has an answer here:
Custom Layout in android
(1 answer)
Closed 2 years ago.
I'm looking to a simple way to do layout templating in android.
I already check include and merge techniques without success.
(I think that it's possible creating custom Layouts and defining by code this behavior, but i wondered if that could be done by xml)
I want to define something like this:
[globalLayout]
<linearLayout params=xxx>
<linearLayout params=yyy>
<?yied ?>
</linearLayout>
</linearLayout>
[customView1]
<Linearlayout>
<ImageView />
<Button/>
</LinearLayout>
[customView2]
<Linearlayout>
<Button/>
<Button/>
<Button/>
</LinearLayout>
(these 3 xml should be reusable)
[HomeLayout]
<?include globalLayout >
<?include customView1 />
</include>
[ParamsLayout]
<?include globalLayout >
<?include customView2 />
</include>
The thing is that i want to have a reusable layout, if a perform a small change, it will affect all dependent views. somethink linked to "partial views or templating" in other languages.
Could anyone help me?
I have done something like this before by using view stub.
You can inflate any view you like inside that view.
<GlobalLayout>
<ViewStub>
<GlobalLayout>
Use LayoutInflater to do something like this:
On the Activity's onCreate:
super.onCreate(savedInstanceState);
setContentView(new TemplateInflater(this).apply(R.layout.products)
.onViewGroup(R.id.replace_here).ofTemplate(R.layout.template));
An implementation snippet:
public View ofTemplate(int templateId) {
LayoutInflater inflater = LayoutInflater.from(context);
View root = inflater.inflate(templateId, null);
View content = inflater.inflate(contentId, null);
((ViewGroup)root.findViewById(viewGroupId)).addView(content);
return root;
}
An example of a working code is in my git: https://github.com/erichegt/AndroidTemplating
I think this code will solve your problem, but you should use Fragments instead. You can have one Activity associated with a template and a Fragment to inflate it.
ViewStub is pretty straightforward and can cover basic layout templating needs.
It serves as a placeholder for some other layout which you can specify and inflate at runtime and then:
The inflated View is added to the ViewStub's parent with the ViewStub's layout parameters.
Here is example from one of my projects. In my layout template I have:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout ... >
...
<ViewStub
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/button_1_stub"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/split_guideline"
app:layout_constraintLeft_toRightOf="#+id/primary_left_guideline"
app:layout_constraintRight_toLeftOf="#+id/primary_right_guideline">
</ViewStub>
....
</android.support.constraint.ConstraintLayout>
... then, when I inflate it I am setting actual button layout that I need and inflate stub:
View contentView = inflater.inflate(R.layout.activity_main_template, null);
ViewStub button1Stub = contentView.findViewById(R.id.button_1_stub);
button1Stub.setLayoutResource(R.layout.work_button);
button1Stub.inflate();
... which inserts layout from R.layout.work_button instead of stub, imposing layout constraints I defined on the R.id.button_1_stub.

Not able to access ViewStub'child

I am trying to use VIEWSTUB inside the merge tag.and its working well.I'm able to catch onclicklistenr of ViewStub's parent button.But i want to access the button that is inside the viewstub.
1.Main xml:
<merge>
<LinearLayout>
<Button></Button>
<ViewStub></ViewStub>
</LinearLayout>
</merge>
2.view stub layout
<Button android:id="#+id/button_cancel" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:minWidth="100dip"
android:text="Next" />
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/imageView"
android:background="#drawable/golden_gate"
/>
</LinearLayout>
I am inflating view stub in an activity...here i want to fire click event on button cancel.How it will be possible
Let's suppose your ViewStub ID is view_stub. You need to do the following in the activity:
ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);
View inflatedView = viewStub.inflate();
Button button = (Button) inflatedView.findViewById(R.id.button_cancel);
Now you can do whatever you want with the button :) That is, the inflate method returns the stub layout which contains the actual elements from the XML file.
Of course, you can always have the onClick XML attribute...
As for removing the ViewStub - the question is two-fold (check http://developer.android.com/resources/articles/layout-tricks-stubs.html):
before inflation of the ViewStub - you cannot actually remove it. There's no need, though, since ViewStub "has no dimension, it does not draw anything and does not participate in the layout in any way".
after inflation - you just take the View returned by the ViewStub.inflate() method and do whatever you want with it - for example, hide it.

Categories

Resources