Android ViewStub如何通过编程改变布局

7
这是我的使用场景:
我希望在运行时更改充气布局,例如,首先我会充气布局A,然后过一段时间我想显示布局B,然后是布局C等。
我在某个地方读到,与其在主布局中包含布局,再隐藏/取消隐藏,不如使用ViewStub和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>

目前的问题是当我膨胀第一个布局时,它可以正常工作,但下一次当我尝试膨胀第二个布局时,我得到了存根为空的错误。

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

我的理解是ViewStub是一个容器,用于加载布局。那么为什么我在尝试加载第二个布局时没有得到ViewStub呢?(这意味着当我填充第一个布局(A)时,包含ViewStub的布局被完全移除了吗?)
我正在寻找使用ViewStub或其他替代方案实现我的用例的任何指针。
2个回答

12

ViewStub是一个占位符,只要调用ViewStub.inflate(),它就会被充气的布局取代。反复调用inflate并没有意义,因为ViewStub将不再存在于层次结构中。相反,您应该获取到您的LinearLayout的引用,删除其视图,并将第二个布局添加为子项。

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));

'll' 的引用来自哪里?它是 ViewStub 的父类吗? - Rakesh L
@Rakesh L 这是您在ViewStub中膨胀的布局的基本视图,您正在删除该布局中包含的所有视图,并用下一个布局的层次结构替换它们... - me_
3
通过这种方法,我们不需要使用ViewStub。在这里添加视图和移除视图是主要的事情,对吧? - iamcrypticcoder

5
是的,我认为您可以轻松地用另一个ViewStub替换它,并以这种方式懒惰地充气新布局:

对于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");
    }
}

或者使用 Kotlin 和一个扩展。
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")
    }
}

请查看这个“gist”:链接

我有一个单独的布局文件,使用以下代码进行填充:val view: View = inflater.inflate(R.layout.bottom_navigation_view, null),然后将其作为参数使用时会出现问题,因为它的父级为空。那么视图参数具体是什么样子的呢?你能给个例子吗? - Jim Clermonts
1
@AdeDyas 这里是示例 https://gist.github.com/sbelloz/2f5504974b7bfc7d947801a86f8725da#gistcomment-3608925 - leon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接