版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一、使用
1.demo
CardView 的使用很简单,直接上个 demo 看一下。
activity_main.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="com.xiaoyue.cardview.MainActivity">
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="15dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/a"/>
</android.support.v7.widget.CardView>
</android.support.constraint.ConstraintLayout>
直接在布局文件中使用即可。
效果1:
这是在安卓 5.0 以上的系统,在安卓 5.0 以下的系统显示的效果不一样。
效果2:
在布局文件中,给 CardView 设置 app:cardCornerRadius (圆角)属性,但是实际生效的是 CardView 下的子控件 ImageView。
2.使用文字
对比上面两个效果图,可以发现,在 5.0 以下系统,CardView 下边沿有一个默认的 Padding,在 5.0 以上是没有的。
这时候如果使用文字的话会导致文字部分被遮挡住。
5.0 以上效果:
5.0 以下效果:
这时候就需要对 CardView 使用 contentPadding 进行边距的设置。
activity_main.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="com.xiaoyue.cardview.MainActivity">
<android.support.v7.widget.CardView
android:layout_width="400dp"
android:layout_height="200dp"
app:cardCornerRadius="25dp"
app:contentPadding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textSize="20dp"/>
</android.support.v7.widget.CardView>
</android.support.constraint.ConstraintLayout>
效果:
3.其他属性
● android:cardCornerRadius
○ 在xml文件中设置card圆角的大小
● CardView.setRadius
○ 在代码中设置card圆角的大小
● android:cardBackgroundColor
○ 在xml文件中设置card背景颜色
● android:elevation
○ 在xml文件中设置阴影的大小
● card_view:cardElevation
○ 在xml文件中设置阴影的大小
● card_view:cardMaxElevation
○ 在xml文件中设置阴影最大高度
● card_view:cardCornerRadius
○ 在xml文件中设置卡片的圆角大小
● card_view:contentPadding
○ 在xml文件中设置卡片内容于边距的间隔
● card_view:contentPaddingBottom
○ 在xml文件中设置卡片内容于下边距的间隔
● card_view:contentPaddingTop
○ 在xml文件中设置卡片内容于上边距的间隔
● card_view:contentPaddingLeft
○ 在xml文件中设置卡片内容于左边距的间隔
● card_view:contentPaddingRight
○ 在xml文件中设置卡片内容于右边距的间隔
● card_view:contentPaddingStart
○ 在xml文件中设置卡片内容于边距的间隔起始
● card_view:contentPaddingEnd
○ 在xml文件中设置卡片内容于边距的间隔终止
● card_view:cardUseCompatPadding
○ 在xml文件中设置内边距,V21+的版本和之前的版本仍旧具有一样的计算方式
● card_view:cardPreventConrerOverlap
○ 在xml文件中设置内边距,在V20和之前的版本中添加内边距,这个属性为了防止内容和边角的重叠
二、源码
public class CardView extends FrameLayout {
private static final int[] COLOR_BACKGROUND_ATTR = {android.R.attr.colorBackground};
private static final CardViewImpl IMPL;
static {
if (Build.VERSION.SDK_INT >= 21) {
IMPL = new CardViewApi21Impl();
} else if (Build.VERSION.SDK_INT >= 17) {
IMPL = new CardViewApi17Impl();
} else {
IMPL = new CardViewBaseImpl();
}
IMPL.initStatic();
}
...
private final CardViewDelegate mCardViewDelegate = new CardViewDelegate() {
...
}
}
首先,CardView 继承于 FrameLayout ,拥有 FrameLayout 的特性。CardView 跟 AppCompat 使用了一样的机制,根据版本进行判断,去实例化不同的实现类,以及使用代理进行对象的操作。
这边从设置圆角开始分析,为什么属性添加在 CardView 却作用于 CardView 下的子控件。
CardView 的构造函数调用了初始化方法 initialize()。
initialize:
private void initialize(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardView, defStyleAttr,
R.style.CardView);
...
IMPL.initialize(mCardViewDelegate, context, backgroundColor, radius,
elevation, maxElevation);
}
initialize 进行了 xml 设置的属性获取,最终调用 CardViewImpl 的 initialize 方法,上面说了,CardViewImpl 会根据版本去实例化不同的实现类。
CardViewApi21Impl
class CardViewApi21Impl implements CardViewImpl {
@Override
public void initialize(CardViewDelegate cardView, Context context,
ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
final RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);
cardView.setCardBackground(background);
View view = cardView.getCardView();
view.setClipToOutline(true);
view.setElevation(elevation);
setMaxElevation(cardView, maxElevation);
}
...
}
在安卓 5.0 以上的时候,实例化了 CardViewApi21Impl 对象,CardViewApi21Impl 的 initialize 方法有一个 RoundRectDrawable ,并把这个设置为 CardView 的 Background。我们看一下 RoundRectDrawable 。
RoundRectDrawable :
class RoundRectDrawable extends Drawable {
...
@Override
public void draw(Canvas canvas) {
final Paint paint = mPaint;
final boolean clearColorFilter;
if (mTintFilter != null && paint.getColorFilter() == null) {
paint.setColorFilter(mTintFilter);
clearColorFilter = true;
} else {
clearColorFilter = false;
}
canvas.drawRoundRect(mBoundsF, mRadius, mRadius, paint);
if (clearColorFilter) {
paint.setColorFilter(null);
}
}
...
/**
* Ensures the tint filter is consistent with the current tint color and
* mode.
*/
private PorterDuffColorFilter createTintFilter(ColorStateList tint, PorterDuff.Mode tintMode) {
if (tint == null || tintMode == null) {
return null;
}
final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
return new PorterDuffColorFilter(color, tintMode);
}
}
RoundRectDrawable 的 draw 方法设置了一个 setColorFilter,这个 ColorFilter 是通过 createTintFilter 获取到的,然后调用 canvas.drawRoundRect 就会跟原先的背景进行混合,tintMode 就是混合模式 PorterDuff.Mode.SRC_IN。所以会产生图片是圆角的效果。
(混合模式具体链接:http://blog.csdn.net/qq_18983205/article/details/72810681)
再查看 CardViewApi17Impl ,这个实现类没有 RoundRectDrawable 这个东西,所以不能使图片变成圆角。