当前位置: 首页 > 工具软件 > Card View > 使用案例 >

3.1.4_cardView原理解析

颜鸿云
2023-12-01

CardView继承自FrameLayout,对于不同的版本,有不同的实现类。

static {
    if (VERSION.SDK_INT >= 21) {
        IMPL = new CardViewApi21Impl();
    } else if (VERSION.SDK_INT >= 17) {
        IMPL = new CardViewApi17Impl();
    } else {
        IMPL = new CardViewBaseImpl();
    }

    IMPL.initStatic();
}

在onMeasure里面,对系统版本做了区分。
如果API>=21则不处理,否则会预留出阴影的空间。

接下来看一下API21的实现CardViewApi21Impl

public void initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
    RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);
    cardView.setCardBackground(background);
    View view = cardView.getCardView();
    view.setClipToOutline(true);
    view.setElevation(elevation);
    this.setMaxElevation(cardView, maxElevation);
}  

可以看到,获取了一个View,并对这个View进行了裁剪,设置了阴影大小和最大阴影大小。

public void setMaxElevation(CardViewDelegate cardView, float maxElevation) {
    this.getCardBackground(cardView).setPadding(maxElevation, cardView.getUseCompatPadding(), cardView.getPreventCornerOverlap());
    this.updatePadding(cardView);
}   

来看setMaxElevation中的updatePadding,可以看到

public void updatePadding(CardViewDelegate cardView) {
    if (!cardView.getUseCompatPadding()) {
        cardView.setShadowPadding(0, 0, 0, 0);
    } else {
        float elevation = this.getMaxElevation(cardView);
        float radius = this.getRadius(cardView);
        int hPadding = (int)Math.ceil((double)RoundRectDrawableWithShadow.calculateHorizontalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
        int vPadding = (int)Math.ceil((double)RoundRectDrawableWithShadow.calculateVerticalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
        cardView.setShadowPadding(hPadding, vPadding, hPadding, vPadding);
    }
}

这里的CardViewDelegate是在CardView的构造方法中初始化的,这里计算出相应的padding之后,会调用CardViewDelegate.setShadowPadding

public CardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.mContentPadding = new Rect();
    this.mShadowBounds = new Rect();
    this.mCardViewDelegate = new CardViewDelegate() {
        private Drawable mCardBackground;

        public void setCardBackground(Drawable drawable) {
            this.mCardBackground = drawable;
            CardView.this.setBackgroundDrawable(drawable);
        }

        public boolean getUseCompatPadding() {
            return CardView.this.getUseCompatPadding();
        }

        public boolean getPreventCornerOverlap() {
            return CardView.this.getPreventCornerOverlap();
        }

        public void setShadowPadding(int left, int top, int right, int bottom) {
            CardView.this.mShadowBounds.set(left, top, right, bottom);
            CardView.super.setPadding(left + CardView.this.mContentPadding.left, top + CardView.this.mContentPadding.top, right + CardView.this.mContentPadding.right, bottom + CardView.this.mContentPadding.bottom);
        }

        public void setMinWidthHeightInternal(int width, int height) {
            if (width > CardView.this.mUserSetMinWidth) {
                CardView.super.setMinimumWidth(width);
            }

            if (height > CardView.this.mUserSetMinHeight) {
                CardView.super.setMinimumHeight(height);
            }

        }

        public Drawable getCardBackground() {
            return this.mCardBackground;
        }

        public View getCardView() {
            return CardView.this;
        }
    };
    TypedArray a = context.obtainStyledAttributes(attrs, styleable.CardView, defStyleAttr, style.CardView);
    ColorStateList backgroundColor;
    if (a.hasValue(styleable.CardView_cardBackgroundColor)) {
        backgroundColor = a.getColorStateList(styleable.CardView_cardBackgroundColor);
    } else {
        TypedArray aa = this.getContext().obtainStyledAttributes(COLOR_BACKGROUND_ATTR);
        int themeColorBackground = aa.getColor(0, 0);
        aa.recycle();
        float[] hsv = new float[3];
        Color.colorToHSV(themeColorBackground, hsv);
        backgroundColor = ColorStateList.valueOf(hsv[2] > 0.5F ? this.getResources().getColor(color.cardview_light_background) : this.getResources().getColor(color.cardview_dark_background));
    }

    float radius = a.getDimension(styleable.CardView_cardCornerRadius, 0.0F);
    float elevation = a.getDimension(styleable.CardView_cardElevation, 0.0F);
    float maxElevation = a.getDimension(styleable.CardView_cardMaxElevation, 0.0F);
    this.mCompatPadding = a.getBoolean(styleable.CardView_cardUseCompatPadding, false);
    this.mPreventCornerOverlap = a.getBoolean(styleable.CardView_cardPreventCornerOverlap, true);
    int defaultPadding = a.getDimensionPixelSize(styleable.CardView_contentPadding, 0);
    this.mContentPadding.left = a.getDimensionPixelSize(styleable.CardView_contentPaddingLeft, defaultPadding);
    this.mContentPadding.top = a.getDimensionPixelSize(styleable.CardView_contentPaddingTop, defaultPadding);
    this.mContentPadding.right = a.getDimensionPixelSize(styleable.CardView_contentPaddingRight, defaultPadding);
    this.mContentPadding.bottom = a.getDimensionPixelSize(styleable.CardView_contentPaddingBottom, defaultPadding);
    if (elevation > maxElevation) {
        maxElevation = elevation;
    }

    this.mUserSetMinWidth = a.getDimensionPixelSize(styleable.CardView_android_minWidth, 0);
    this.mUserSetMinHeight = a.getDimensionPixelSize(styleable.CardView_android_minHeight, 0);
    a.recycle();
    IMPL.initialize(this.mCardViewDelegate, context, backgroundColor, radius, elevation, maxElevation);
}

可以看到setShadowPadding中,直接调用了CardView的setPadding(CardView没有重写这个方法,所以实际是调用View.setPadding)

小结

CardView的源码主要就是针对阴影和类边距效果在不同的系统版本上面做了不同的处理。

在使用CardView的时候,为了保持cardview在不同的版本中的一致性,一般情况下,都要加

    app:cardPreventCornerOverlap="false"
    app:cardUseCompatPadding="true"  

app:cardUseCompatPadding boolean 在Android 5.0及以上平台中,设置是否要添加padding,5.0以下默认添加padding。默认值为false
app:cardPreventCornerOverlap boolean 是否给content添加padding,来阻止与圆角重叠,默认值为true

 类似资料: