创建样式化的 LinearLayout 自定义视图

Create a Styled LinearLayout Custom View

想象一下,您将通过应用程序使用以下卡片设计。标题、按钮、分隔符,然后 space 用于蓝色框指示的动态内容。我们可以在蓝色区域内添加任何我们需要的东西,但用于保存内容的框架是一致的。例如:

Card with Placeholder Region

如果我要在里面放两个 TextView,它可能看起来像这样,下面是(修剪过的)视图布局:

Card with Two TextViews

<com.google.android.material.card.MaterialCardView>
    <LinearLayout android:orientation="vertical">

        <LinearLayout android:orientation="horizontal">
            <TextView android:text="My Title" />
            <com.google.android.material.button.MaterialButton android:text="ACTION" />
        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#000" />

        <LinearLayout android:id="+@id/contentGoesHere">
            <TextView android:text="First element" />
            <TextView android:text="Second element" />
        </LinearLayout>

    </LinearLayout>
</com.google.android.material.card.MaterialCardView>

理想情况下,我想要一个自定义视图,这样开发人员就可以执行以下操作并获得一致的样式,或者以编程方式添加到视图中:

<com.customview.CustomView>
    <TextView android:text="First element" />
    <TextView android:text="Second element" />
</com.customview.CustomView>

我的问题是,仅仅扩展 LinearLayout 并制作自定义视图是行不通的 - 它会创建布局,但无法表明内部 LinearLayout 是我想要的视图添加到,因此 XML 中添加的任何子视图都将被忽略。

我是否需要制作自定义 ViewGroup 并手动为 LinearLayout 的自定义支架充气(求助!onMeasure 和 onLayout!?)或者是否有更简单的方法制作自定义 LinearLayout 使用此样式框架查看?

My problem is that just extending LinearLayout and making a custom view wont work - it'll create the layout but there's no way to indicate that the inner LinearLayout is what I want the views to be added to, so any subviews added in the XML are ignored.

您可以手动将子视图移入嵌套 LinearLayout

例如:

public class CustomView extends LinearLayout {

    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        final ViewGroup container = inflate(getContext(), R.layout.card_layout, this)
                .findViewById(R.id.inner_content_container);
        while (getChildCount() > 1) {
            final View child = getChildAt(0);
            removeView(child);
            container.addView(child, child.getLayoutParams());
        }
    }
}

card_layout.xml

<com.google.android.material.card.MaterialCardView>
    <LinearLayout android:orientation="vertical">

        <LinearLayout android:orientation="horizontal">
            <TextView android:text="My Title" />
            <com.google.android.material.button.MaterialButton android:text="ACTION" />
        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#000" />

        <LinearLayout
            android:id="@+id/inner_content_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <!-- content goes here -->
        </LinearLayout>

    </LinearLayout>
</com.google.android.material.card.MaterialCardView>

...

<com.customview.CustomView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView android:text="First element" />
    <TextView android:text="Second element" />
</com.customview.CustomView>