使用 CardView 作为复合视图基础

Using CardView as Compound View base

我正在尝试创建一个 CardView 布局,它将被重新用于在个人资料页面和主页上的 RecyclerView 中显示推文。不同之处在于 RecyclerView 会扩充布局本身,而个人资料页面将使用自定义视图。

但是,当在个人资料页面上使用自定义视图时,它会在其后面创建一个额外的 CardView。我最好的猜测是,发生这种情况是因为自定义视图从 CardView class 扩展,而 RecyclerView 当然不会扩展此 class.

所以我最终得到的是一个 CardView,里面有我的布局,如下图所示: layout in CardView

虽然在 RecyclerView 中看起来应该如此: layout inflated in RecyclerView

我还尝试使用 LinearLayoutFrameLayout 来扩展自定义视图 class 而不是 CardViewCardView 仍然作为根tweet.xml。这部分解决了这个问题,但即使使用 setClipToPadding(false)setClipChilderen(false) 它仍然会切掉 CardView 的阴影边框的一部分。

我尝试从 CardView 扩展,同时让 tweet.xml 中的根成为 LinearLayoutFrameLayout 并在 Java 中进行样式设置。 但这导致另一个 'flaw' 当使用带有 setRadius(R.dimen.card_corner_radius) 的卡片半径维度值(即 8dp)时。最后我得到了 this.

那么如何将 CardView 作为我的 xml 文件的根,同时将它用作自定义视图?

tweet.xml Preview

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="2dp"
    app:cardCornerRadius="@dimen/card_corner_radius"
    app:contentPaddingBottom="8dp"
    android:foreground="?attr/selectableItemBackground"
    android:layout_marginStart="@dimen/card_spacing_horizontal"
    android:layout_marginEnd="@dimen/card_spacing_horizontal"
    android:layout_marginTop="@dimen/card_spacing_vertical"
    android:clickable="true"
    android:focusable="true"
    style="@style/CardView">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="128dp"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_default_image"
            android:foreground="?attr/selectableItemBackground"
            android:visibility="visible" />

        <!-- Rest of layout -->

    </android.support.constraint.ConstraintLayout>

</android.support.v7.widget.CardView>

TweetView.java

public class TweetView extends CardView {

    public TweetView(Context context) {
        this(context, null /* attrs */);
    }

    public TweetView(Context context, AttributeSet attrs) {
        this(context, attrs, R.style.AppTheme);
    }

    public TweetView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        LayoutInflater inflater = LayoutInflater.from(context);
        inflater.inflate(R.layout.tweet, this);
    }
}

fragment_profile.xml Preview

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragments.ProfileFragment">

    <nl.robinpfeiffer.parrot.twitter.views.TweetView
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"/>

</LinearLayout>

编辑

我尝试像@Eminem 建议的那样更改 TweetView.java 的代码。但是 CardView 仍然保持不变;嵌套在另一个 CardView.

TweetView.java 已更新

LayoutInflater mInflater;

public TweetView(Context context) {
    super(context);
    mInflater = LayoutInflater.from(context);
    init();
}

public TweetView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mInflater = LayoutInflater.from(context);
    init();
}

public TweetView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mInflater = LayoutInflater.from(context);
    init();
}

private void init() {
    View v = mInflater.inflate(R.layout.tweet, this, true);
}

编辑 2:

当java样式失败时我没有太注意,忘记了它应该是setRadius(getResources().getDimension(R.dimen.card_corner_radius));而不是setRadius(R.dimen.card_corner_radius);,因为后者使用资源整数半径的 id 值。因此 方式 太圆角了。

所以我终于像@Eminem 建议的那样用 <merge> 标签修复了它。因为我是从 CardView 扩展我的 TweetView class,它会自动创建一个 CardView。在 TweetView 中,布局 tweet.xml 通过 inflate(getContext, R.layout.tweet, this) 得到扩展 现在通过创建自定义卡片样式在 styles.xml 文件中处理样式。在使用 TweetView class.

的地方需要应用此样式

我生成的布局和 class 文件如下:

tweet.xml

    <?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:parentTag="android.support.v7.widget.CardView">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:foreground="?attr/selectableItemBackground"
        android:paddingBottom="8dp"
        android:focusable="true">

        <!-- Rest of internal layout -->

    </android.support.constraint.ConstraintLayout>

</merge>

TweetView.java

public class TweetView extends CardView implements Observer {

public TweetView(Context context) {
    super(context);
    init();
}

public TweetView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public TweetView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init() {
    inflate(getContext(), R.layout.tweet, this);
}

styles.xml

   <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="android:navigationBarColor">@color/colorPrimaryDark</item>
        <!-- Base theme items -->
    </style>

    <style name="CardTweet" parent="CardView">
        <!-- Custom card items -->
        <item name="cardCornerRadius">@dimen/card_corner_radius</item>
    </style>

最后这就是我在标准 FragmentRecyclerView 中使用 class 的方式:

fragment_profile.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragments.ProfileFragment">

        <nl.robinpfeiffer.parrot.twitter.views.TweetView
            android:layout_width="250dp"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            style="@style/CardTweet"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

fragment_tweet.xml 这是一件 RecyclerView 物品,在 RecyclerViewAdapter

中膨胀了
<?xml version="1.0" encoding="utf-8"?>
<nl.robinpfeiffer.parrot.twitter.views.TweetView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/CardTweet"
    android:layout_marginEnd="@dimen/card_spacing_horizontal"
    android:layout_marginStart="@dimen/card_spacing_horizontal"
    android:layout_marginTop="@dimen/card_spacing_vertical" />

Fixed card, normal layout

Fixed card, RecyclerView