我对android开发还不熟悉,觉得这是一个很小的问题,但我不能很好地在网上找到解决方案,所以我不妨在这里提问。
我的目标是创建一个可重用的组件,它本质上是一个可扩展的卡,就像这里描述的那样:https://material.io/design/components/cards.html#behavior
为此,我创建了一个扩展CardView的自定义视图:

public class ExpandableCardView extends CardView {

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

    public ExpandableCardView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // get custom attributes
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ExpandableCardView, 0, 0);
        String heading = array.getString(R.styleable.ExpandableCardView_heading);
        array.recycle();

        // inflate the layout
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.expandable_card_view, this, true);

        // set values
        TextView headingTextView = findViewById(R.id.card_heading);
        headingTextView.setText(heading.toUpperCase());

        // set collapse/expand click listener
        ImageView collapseExpandButton = findViewById(R.id.collapse_expand_card_button);
        collapseExpandButton.setOnClickListener((View v) -> toggleCardBodyVisibility());
    }

    private void toggleCardBodyVisibility() {
        LinearLayout description = findViewById(R.id.card_body);
        ImageView imageButton = findViewById(R.id.collapse_expand_card_button);

        if (description.getVisibility() == View.GONE) {
            description.setVisibility(View.VISIBLE);
            imageButton.setImageResource(R.drawable.ic_arrow_up);
        } else {
            description.setVisibility(View.GONE);
            imageButton.setImageResource(R.drawable.ic_arrow_down);
        }
    }
}

布局:
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/expandable_card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="16dp"
    android:animateLayoutChanges="true"
    app:cardCornerRadius="4dp">

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/card_header"
        android:padding="12dp"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/card_heading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:textColor="@color/colorPrimary"
            android:layout_alignParentLeft="true"
            android:text="HEADING"/>

        <ImageView
            android:id="@+id/collapse_expand_card_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            app:srcCompat="@drawable/ic_arrow_down"/>
    </RelativeLayout>

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/card_body"
        android:padding="12dp"
        android:layout_marginTop="28dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:visibility="gone" >
    </LinearLayout>
</androidx.cardview.widget.CardView>

最终,我希望能够像这样在我的活动中使用它,通常每个活动有多个实例:
<xx.xyz.yy.customviews.ExpandableCardView
    android:id="@+id/card_xyz"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    custom_xxx:heading="SOME HEADING" >

    <SomeView></SomeView>

</xx.xyz.yy.customviews.ExpandableCardView>

其中someview是任何文本、图像、布局或其他自定义视图,通常具有从活动绑定的数据。
如何使它呈现卡体内部的某个视图?我想获取自定义视图中定义的任何子结构,并在展开时将其显示在卡体中。希望我能让你明白。

最佳答案

我认为更好的方法是在单独的文件中定义将插入CardView(“someview”)的布局,并使用如下自定义属性引用它:

<xx.xyz.yy.customviews.ExpandableCardView
    android:id="@+id/card_xyz"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    custom_xxx:heading="SOME HEADING"
    custom_xxx:expandedView="@layout/some_view"/>

我将在最后解释我的理由,但让我们看看你的问题的答案。
您可能在代码中看到的是SomeViewexpandable_card_view同时出现在布局中。这是因为SomeView是用CardView隐式充气的,然后通过显式充气添加expandable_card_view。由于直接使用布局xml文件很困难,我们将让隐式膨胀发生,这样定制的CardView只包含SomeView
然后我们将从布局中移除SomeView,将其保存,并将expandable_card_view插入其位置。完成后,SomeView将重新插入到具有idLinearLayoutcard_body中。所有这些都必须在初始布局完成后完成。要在初始布局完成后获得控制,我们将使用ViewTreeObserver.OnGlobalLayoutListener。这是更新的代码。(为了简化示例,我删除了一些内容。)
android - 具有 subview 的自定义可扩展卡-LMLPHP
可扩展卡片视图
public class ExpandableCardView extends CardView {

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

    public ExpandableCardView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // Get control after layout is complete.
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                // Remove listener so it won't be called again
                getViewTreeObserver().removeOnGlobalLayoutListener(this);

                // Get the view we want to insert into the LinearLayut called "card_body" and
                // remove it from the custom CardView.
                View childView = getChildAt(0);
                removeAllViews();
                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                inflater.inflate(R.layout.expandable_card_view, ExpandableCardView.this, true);

                // Insert the view into the LinearLayout.
                ((LinearLayout) findViewById(R.id.card_body)).addView(childView);

                // And the rest of the stuff...
                TextView headingTextView = findViewById(R.id.card_heading);
                headingTextView.setText("THE HEADING");

                // set collapse/expand click listener
                ImageView collapseExpandButton = findViewById(R.id.collapse_expand_card_button);
                collapseExpandButton.setOnClickListener((View v) -> toggleCardBodyVisibility());
            }
        });
    }

    private void toggleCardBodyVisibility() {
        LinearLayout description = findViewById(R.id.card_body);
        ImageView imageButton = findViewById(R.id.collapse_expand_card_button);

        if (description.getVisibility() == View.GONE) {
            description.setVisibility(View.VISIBLE);
            imageButton.setImageResource(R.drawable.ic_arrow_up);
        } else {
            description.setVisibility(View.GONE);
            imageButton.setImageResource(R.drawable.ic_arrow_down);
        }
    }
}

可扩展卡视图.java
CardView标记更改为merge,以避免CardView直接嵌套在CardView中。
<merge
    android:id="@+id/expandable_card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="16dp"
    android:animateLayoutChanges="true"
    app:cardCornerRadius="4dp">

    <RelativeLayout
        android:id="@+id/card_header"
        android:padding="12dp"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/card_heading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:textColor="@color/colorPrimary"
            android:layout_alignParentLeft="true"
            android:text="HEADING"/>

        <ImageView
            android:id="@+id/collapse_expand_card_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            app:srcCompat="@drawable/ic_arrow_down"/>
    </RelativeLayout>

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/card_body"
        android:padding="12dp"
        android:layout_marginTop="28dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:visibility="gone" >

    </LinearLayout>
</merge>

活动\主.xml
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.example.customcardview.ExpandableCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ImageView
                android:id="@+id/imageView"
                android:layout_width="100dp"
                android:layout_height="100dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_android" />


            <TextView
                android:id="@+id/childView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Say my name."
                android:textSize="12sp"
                android:textStyle="bold"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/imageView" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </com.example.customcardview.ExpandableCardView>

</LinearLayout>

那么,为什么我建议您使用自定义属性在布局中包含SomeView,正如我在开头所指出的那样?如上所述,SomeView将始终充气,并且有一些努力来切换布局,尽管SomeView可能永远不会显示。例如,如果您在aCardViews中有很多自定义RecyclerView,那么这将非常昂贵。通过使用自定义属性引用外部布局,您只需在显示时对其进行充气,代码就会简单得多,更容易理解。只有我的两分钱,这可能并不重要,这取决于你打算如何使用自定义视图。

07-24 09:47
查看更多