CardView

作者: 一江碎月 | 来源:发表于2017-09-20 20:32 被阅读0次

参考

CardView 源码解析

常用属性

CardView 的阴影的实现方式是通过为 CardView 设置 backgroundDrawable,drawable 自身带有阴影部分。

cardPreventCornerOverlap

是否防止角落重叠。默认时该属性值为 true。在 5.0 以下的手机将该值设置 true 和 false 的效果分别如下:

上图为true,下图为false上图为true,下图为false

其中的外层红色边框就是为 CardView 设置的背景 drawable。

从中可以看出:

  • 为 true 时,内容不会覆盖住圆角。因为 drawable 会自动添加 padding 值——该值通过 cornerRadius 进行计算而得——这导致边框与内容之意有间隔。

  • 为 false 时,内部内容会覆盖在圆角上。因为不会为 drawable 添加 padding 值;

  • 内容是否覆盖住圆角,主要是通过为 drawable 添加 padding 实现,而 cardPreventCornerOverlap 则控制着是否会为该 drawable 添加 padding ,true 时添加,false 不添加。

cardUseCompatPadding

设置内边距,控制 API 21及以上的版本是否和之前的版本具有一样的计算方式。默认值为 false。

在 API 21 之前的版本中,为了避免内容与覆盖住圆角,CardView 会添加 padding 。但 API 21 及以后版本中,系统会对内容进行裁剪以达到不覆盖圆角的效果。因此,同样的设置在 21 以后和以前的系统中显示的效果不一样。

  • 为 true 时,在 21 及以上的版本中,也会类似于 21 以前的版本一样,添加一个 padding 值。

  • 为 false 时,不会添加 padding 值。

常用类

  1. CardView : 主类。外界在使用时,就使用的是该类。

  2. CardViewImpl : 一个接口类,会根据手机版本的不同,采用不同的方式为 CardView 设置阴影效果

  3. CardViewGingerbread、CardViewJellybeanMr1 与 CardViewApi21:CardViewImpl 的实现类。其中 CardViewJellybeanMr1 继承于 CardViewGingerbread,但只重写了 initStatic() 方法。

  4. CardViewDelegate :CardView 代理类,用于 CardView 与 CardViewImple 之间通信,其具体子类在 CardView 内部。

  5. RoundRectDrawableWithShadow 与 RoundRectDrawable:都继承于 Drawable,CardViewImpl 不同的子类根据自己的需要生成两个类的实例,然后设置为 CardView 的 backgroundDrawable

    • CardViewApi21 使用 RoundRectDrawable

    • CardViewGingerbread 与 CardViewJellybeanMr1 使用RoundRectDrawableWithShadow 。

思路解析

静态代码块

CardView 首先在静态代码块中根据版本初始化 CardViewImpl 对象,并赋值给 IMPL 属性,同时调用 CardViewImpl #initStatic() 方法:

private static final CardViewImpl IMPL;

static {
    if (Build.VERSION.SDK_INT >= 21) {
        IMPL = new CardViewApi21();
    } else if (Build.VERSION.SDK_INT >= 17) {
        IMPL = new CardViewJellybeanMr1();
    } else {
        IMPL = new CardViewGingerbread();
    }
    IMPL.initStatic();
}

其中 CardViewApi21#initStatic() 方法是空实现,不进行任何操作;而剩余两个会为
RoundRectDrawableWithShadow#sRoundRectHelper 属性进行赋值。

构造函数

CardView 的构造函数中会调用 CardView#initialize() 方法 。该方法会调用 CardViewImpl#initialize() 方法:

CardViewGingerbread#initialize() 如下:


    @Override
    public void initialize(CardViewDelegate cardView, Context context,
            ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
        RoundRectDrawableWithShadow background = createBackground(context, backgroundColor, radius,
                elevation, maxElevation);
        background.setAddPaddingForCorners(cardView.getPreventCornerOverlap());
        // 其实际上调用了 CardView#setBackgroundDrawable() 方法
        cardView.setCardBackground(background);
        updatePadding(cardView);
    }

    private RoundRectDrawableWithShadow createBackground(Context context,
                    ColorStateList backgroundColor, float radius, float elevation,
                    float maxElevation) {
        return new RoundRectDrawableWithShadow(context.getResources(), backgroundColor, radius,
                elevation, maxElevation);
    }

可以看出,只是创建了一个 RoundRectDrawableWithShadow 对象,并设置给 CardView作为 background。

CardViewApi21的initialize() 如下:

    public void initialize(CardViewDelegate cardView, Context context,
                ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
        final RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);
         // 也是生成一个 drawable,然后设置给 CardView 作为 backgroundDrawable
        cardView.setCardBackground(background);

        View view = cardView.getCardView();
        view.setClipToOutline(true); // api 21以上才有的功能
        view.setElevation(elevation);// api 21 以上才有的功能
        setMaxElevation(cardView, maxElevation);
    }

因为 API 21 以上,系统已经现实了 elevation 属性,所以不需要像 21 以前一样通过画出阴影模拟高度,而是可以简单的调用了 View#setElevation() 方法。

相关文章

网友评论

      本文标题:CardView

      本文链接:https://www.haomeiwen.com/subject/pmtasxtx.html