(三十六)CardView 使用及源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、使用

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 这个东西,所以不能使图片变成圆角。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值