Android ViewStub change layouts programmatically
Asked Answered
S

2

7

This is my use case:

I want to change my inflated layout at run time, say first I inflate layout a, then after some time I want to show layout B, then layout C etc.

I read somewhere that rather than including layouts in main layout and then hiding/unhiding I should use viewstub and inflate.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ViewStub
        android:id="@+id/layout_stub"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

My issue now is when I inflate the first layout it works fine but the next time when I try to inflate the second layout I get the stub null.

ViewStub stub = (ViewStub) findViewById(R.id.layout_stub);
        stub.setLayoutResource(layoutId);
        View inflated = stub.inflate();

My understanding is the Viewstub is a container in which the layouts are being loaded, if so why am I not getting the ViewStub when trying to load the second layout? (So this means when I inflated the first layout (A) the layout in which the ViewStub was placed was removed completely?)

I'm looking for any pointers to implementing my usecase with Viewstub or alternatives.

Skipp answered 25/5, 2016 at 15:12 Comment(0)
P
12

A ViewStub is a placeholder, which is replaced by an inflated layout as soon as ViewStub.inflate() is called. It doesn't make sense to call inflate a second time, as the ViewStub will no longer be in the hierarchy. Instead, you should obtain a reference to your LinearLayout, remove its views, and add your second layout as a child.

ViewStub stub = (ViewStub) findViewById(R.id.layout_stub);
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
stub.setLayoutResource(layoutId);
stub.inflate(); // inflate 1st layout

ll.removeAllViews(); // remove previous view, add 2nd layout
ll.addView(LayoutInflater.from(context).inflate(secondLayoutId, ll, false));
Polite answered 25/5, 2016 at 15:57 Comment(3)
where the reference 'll' comes from ?. Is it the parent of ViewStub ?Systole
@Rakesh L it is the base view of the layout you inflated in the viewstub to begin with, you are removing all the views contained in that layout and replacing them with the hierarchy of your next layout...Floor
By this approach, We don't need ViewStub. Just add view and remove view is main thing here. isn't it?Wohlen
U
5

Yes, I think you can easily replace it with another ViewStub and lazily inflate your new layout in this way:

for Java

 public static ViewStub deflate(View view) {
    ViewParent viewParent = view.getParent();
    if (viewParent != null && viewParent instanceof ViewGroup) {
        int index = ((ViewGroup) viewParent).indexOfChild(view);
        int inflatedId = view.getId();
        ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
        ((ViewGroup) viewParent).removeView(view);
        Context context = ((ViewGroup) viewParent).getContext();
        ViewStub viewStub = new ViewStub(context);
        viewStub.setInflatedId(inflatedId);
        viewStub.setLayoutParams(layoutParams);
        ((ViewGroup) viewParent).addView(viewStub, index);
        return viewStub;
    } else {
        throw new IllegalStateException("Inflated View has not a parent");
    }
}

or Kotlin with an extension

fun ViewStub.deflate(view: View): ViewStub {
    val viewParent = view.parent

    if (viewParent != null && viewParent is ViewGroup) {
        val index = viewParent.indexOfChild(view)
        viewParent.removeView(view)
        val viewStub = ViewStub(context).apply {
            inflatedId = [email protected]
            layoutParams = [email protected]
        }
        viewParent.addView(viewStub, index)
        return viewStub
    } else {
        throw IllegalStateException("Inflated View has not a parent")
    }
}

Check out the gist

Unsaid answered 14/6, 2018 at 10:3 Comment(2)
I have a separate layout file and inflate that using: val view: View = inflater.inflate(R.layout.bottom_navigation_view, null) and then use that as parameter it doesn't work, because it's parent is null. How exactly does the view parameter look like? Can you give an example?Loch
@AdeDyas here the example gist.github.com/sbelloz/…Unsaid

© 2022 - 2024 — McMap. All rights reserved.