使用 CardView 作为复合视图基础
Using CardView as Compound View base
我正在尝试创建一个 CardView
布局,它将被重新用于在个人资料页面和主页上的 RecyclerView
中显示推文。不同之处在于 RecyclerView
会扩充布局本身,而个人资料页面将使用自定义视图。
但是,当在个人资料页面上使用自定义视图时,它会在其后面创建一个额外的 CardView
。我最好的猜测是,发生这种情况是因为自定义视图从 CardView
class 扩展,而 RecyclerView
当然不会扩展此 class.
所以我最终得到的是一个 CardView
,里面有我的布局,如下图所示:
layout in CardView
虽然在 RecyclerView
中看起来应该如此:
layout inflated in RecyclerView
我还尝试使用 LinearLayout
和 FrameLayout
来扩展自定义视图 class 而不是 CardView
但 CardView
仍然作为根tweet.xml
。这部分解决了这个问题,但即使使用 setClipToPadding(false)
和 setClipChilderen(false)
它仍然会切掉 CardView
的阴影边框的一部分。
我尝试从 CardView
扩展,同时让 tweet.xml
中的根成为 LinearLayout
或 FrameLayout
并在 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>
最后这就是我在标准 Fragment
和 RecyclerView
中使用 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
我正在尝试创建一个 CardView
布局,它将被重新用于在个人资料页面和主页上的 RecyclerView
中显示推文。不同之处在于 RecyclerView
会扩充布局本身,而个人资料页面将使用自定义视图。
但是,当在个人资料页面上使用自定义视图时,它会在其后面创建一个额外的 CardView
。我最好的猜测是,发生这种情况是因为自定义视图从 CardView
class 扩展,而 RecyclerView
当然不会扩展此 class.
所以我最终得到的是一个 CardView
,里面有我的布局,如下图所示:
layout in CardView
虽然在 RecyclerView
中看起来应该如此:
layout inflated in RecyclerView
我还尝试使用 LinearLayout
和 FrameLayout
来扩展自定义视图 class 而不是 CardView
但 CardView
仍然作为根tweet.xml
。这部分解决了这个问题,但即使使用 setClipToPadding(false)
和 setClipChilderen(false)
它仍然会切掉 CardView
的阴影边框的一部分。
我尝试从 CardView
扩展,同时让 tweet.xml
中的根成为 LinearLayout
或 FrameLayout
并在 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>
最后这就是我在标准 Fragment
和 RecyclerView
中使用 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