6

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.

4

2 回答 2

11

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));
于 2016-05-25T15:57:15.147 回答
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 = this@deflate.inflatedId
            layoutParams = this@deflate.layoutParams
        }
        viewParent.addView(viewStub, index)
        return viewStub
    } else {
        throw IllegalStateException("Inflated View has not a parent")
    }
}

Check out the gist

于 2018-06-14T10:03:49.580 回答