Android Study Material Design 十一 之CardView窥探

LZ-Says:每件事情,总是在经历后,才会收获更多。有时候我们要的其实很简单,只是在追逐过程中渐渐忘记了初衷。愿大家在前行的路上,勿忘初心,加油前行~!!!

Android Study Material Design 十一 之CardView窥探_第1张图片


前言

Material Design,谷歌良心之作,深入其中,妙趣横生~

今天为大家带来CardView,一起来看~

嘿嘿嘿,起来准备开车了~


CardView简述

CardView,字如其名,卡片式View,类似小卡片一样,简单举个小例子~

Android Study Material Design 十一 之CardView窥探_第2张图片

是不是感觉炫彩了许多?

首先介绍下有关CardView基本属性:

Android Study Material Design 十一 之CardView窥探_第3张图片

先简单介绍到这里。


CardView实践

第一步:引入依赖

implementation 'com.android.support:cardview-v7:26.0.2'

第二步:添加CardView 并为其设置Content

首先,我们先实现第一个效果,也就是CardView内填充一个TextView,下面附上源码:

    .support.v7.widget.CardView
        android:layout_width="300dp"
        android:layout_height="180dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="30dp"
        app:cardBackgroundColor="#80f"
        app:cardCornerRadius="15dp"
        app:cardElevation="15dp"
        app:contentPadding="15dp">

        "wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="CardView测试"
            android:textColor="#fff"
            android:textSize="16sp" />

    .support.v7.widget.CardView>

效果如下:

Android Study Material Design 十一 之CardView窥探_第4张图片

接着,着手实现第二个效果,CardView设置ImageView,如下:

    .support.v7.widget.CardView
        android:layout_width="300dp"
        android:layout_height="180dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        app:cardBackgroundColor="#fff"
        app:cardCornerRadius="15dp"
        app:cardElevation="15dp">

        "match_parent"
            android:layout_height="match_parent"
            android:background="@drawable/img"
            android:scaleType="centerCrop" />

    .support.v7.widget.CardView>

而效果如下:

Android Study Material Design 十一 之CardView窥探_第5张图片

最后,我们实现第三张图的效果:

    .support.v7.widget.CardView
        android:layout_width="300dp"
        android:layout_height="180dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        app:cardBackgroundColor="#fff"
        app:cardCornerRadius="15dp"
        app:cardElevation="15dp"
        app:contentPadding="15dp">

        "match_parent"
            android:layout_height="match_parent"
            android:background="@drawable/img"
            android:scaleType="centerCrop" />

    .support.v7.widget.CardView>

效果嘛,如下喽~

Android Study Material Design 十一 之CardView窥探_第6张图片

使用easy,看看源码是否也很nice~


CardView源码初窥

首先,我们先看下CardView继承自何方神圣~

public class CardView extends FrameLayout

所以说,为什么我们能在CardView中放置控件,一个合理的解释完美摆在面前。

接着往下看:

    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();
    }

首先,静态块中进行当前系统版本判断,根据不同的状态去实例化不同的实例,如下:

1. 如果Android API 基于21以上,实例化CardViewApi21Impl;

2. 如果Android API 基于17以下,实例化CardViewApi17Impl;

3. 如果Android API 基于17以下,实例化CardViewBaseImpl。

最后,进行一个IMPL初始化。

那么这三个都分别是什么呢?

我们一起来看看~

CardViewApi21Impl 初窥

    @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);
    }

实例化内容如上,首先是实例化RoundRectDrawable一个对象,那么这个东西又是什么呢,它的作用何为?一起来看。

    RoundRectDrawable(ColorStateList backgroundColor, float radius) {
        mRadius = radius;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        setBackground(backgroundColor);

        mBoundsF = new RectF();
        mBoundsI = new Rect();
    }

看到这块内容,大家是不是很熟悉,丫的,这不就是自定义View之绘制部分。

接收设置圆角弧度值,初始化画笔、设置背景颜色以及矩形区域,一气呵成。

而关于CardViewApi17Impl,又是如何的呢?

CardViewApi17Impl 初窥

    @Override
    public void initStatic() {
        RoundRectDrawableWithShadow.sRoundRectHelper =
                new RoundRectDrawableWithShadow.RoundRectHelper() {
                    @Override
                    public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
                            Paint paint) {
                        canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
                    }
                };
    }

看到这里,大家会有个疑问,为什么不同系统版本下,初始化不一样呢?

如果大家看过LZ上一篇关于沉浸式兼容性开发的博文后就会明白,我们兼容原因何在。而下面,我们要一起来看下api17上又是如何实现的呢?

    RoundRectDrawableWithShadow(Resources resources, ColorStateList backgroundColor, float radius,
            float shadowSize, float maxShadowSize) {
        mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color);
        mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color);
        mInsetShadow = resources.getDimensionPixelSize(R.dimen.cardview_compat_inset_shadow);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        setBackground(backgroundColor);
        mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        mCornerShadowPaint.setStyle(Paint.Style.FILL);
        mCornerRadius = (int) (radius + .5f);
        mCardBounds = new RectF();
        mEdgeShadowPaint = new Paint(mCornerShadowPaint);
        mEdgeShadowPaint.setAntiAlias(false);
        setShadowSize(shadowSize, maxShadowSize);
    }

而这些内容则是设置一些相关属性,例如颜色、弧度以及颜色等。

而最终,都会在回调中调用Canvas类中的drawRoundRect()方法进行绘制,如下:

    /**
     * Draw the specified round-rect using the specified paint. The roundrect will be filled or
     * framed based on the Style in the paint.
     *
     * @param rect The rectangular bounds of the roundRect to be drawn
     * @param rx The x-radius of the oval used to round the corners
     * @param ry The y-radius of the oval used to round the corners
     * @param paint The paint used to draw the roundRect
     */
    public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
        super.drawRoundRect(rect, rx, ry, paint);
    }

而关于CardViewBaseImpl,则如下。

CardViewBaseImpl 初窥

    @Override
    public void initStatic() {
        // Draws a round rect using 7 draw operations. This is faster than using
        // canvas.drawRoundRect before JBMR1 because API 11-16 used alpha mask textures to draw
        // shapes.
        RoundRectDrawableWithShadow.sRoundRectHelper =
                new RoundRectDrawableWithShadow.RoundRectHelper() {
            @Override
            public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
                    Paint paint) {
                final float twoRadius = cornerRadius * 2;
                final float innerWidth = bounds.width() - twoRadius - 1;
                final float innerHeight = bounds.height() - twoRadius - 1;
                if (cornerRadius >= 1f) {
                    // increment corner radius to account for half pixels.
                    float roundedCornerRadius = cornerRadius + .5f;
                    mCornerRect.set(-roundedCornerRadius, -roundedCornerRadius, roundedCornerRadius,
                            roundedCornerRadius);
                    int saved = canvas.save();
                    canvas.translate(bounds.left + roundedCornerRadius,
                            bounds.top + roundedCornerRadius);
                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
                    canvas.translate(innerWidth, 0);
                    canvas.rotate(90);
                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
                    canvas.translate(innerHeight, 0);
                    canvas.rotate(90);
                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
                    canvas.translate(innerWidth, 0);
                    canvas.rotate(90);
                    canvas.drawArc(mCornerRect, 180, 90, true, paint);
                    canvas.restoreToCount(saved);
                    //draw top and bottom pieces
                    canvas.drawRect(bounds.left + roundedCornerRadius - 1f, bounds.top,
                            bounds.right - roundedCornerRadius + 1f,
                            bounds.top + roundedCornerRadius, paint);

                    canvas.drawRect(bounds.left + roundedCornerRadius - 1f,
                            bounds.bottom - roundedCornerRadius,
                            bounds.right - roundedCornerRadius + 1f, bounds.bottom, paint);
                }
                // center
                canvas.drawRect(bounds.left, bounds.top + cornerRadius,
                        bounds.right, bounds.bottom - cornerRadius , paint);
            }
        };
    }

    @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.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);
    }

    @Override
    public void updatePadding(CardViewDelegate cardView) {
        Rect shadowPadding = new Rect();
        getShadowBackground(cardView).getMaxShadowAndCornerPadding(shadowPadding);
        cardView.setMinWidthHeightInternal((int) Math.ceil(getMinWidth(cardView)),
                (int) Math.ceil(getMinHeight(cardView)));
        cardView.setShadowPadding(shadowPadding.left, shadowPadding.top,
                shadowPadding.right, shadowPadding.bottom);
    }

首先,在initStatic()方法中,依旧实例化RoundRectDrawableWithShadow对象并在回调中设置针对此系统版本做单独设置,最终都是调用canvas.drawRect实现最后效果。

里面便是进行弧度的判断以及当开发者未添加弧度默认值设定,这里就不一一细说了。

而initialize()方法中则是重点针对于background以及padding值设置而且更新即可。

而最后,我们要查看下在首次最后实例化内容将会是什么?

CardViewImpl 初窥

/**
 * Interface for platform specific CardView implementations.
 */
interface CardViewImpl {
    void initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor,
            float radius, float elevation, float maxElevation);

    void setRadius(CardViewDelegate cardView, float radius);

    float getRadius(CardViewDelegate cardView);

    void setElevation(CardViewDelegate cardView, float elevation);

    float getElevation(CardViewDelegate cardView);

    void initStatic();

    void setMaxElevation(CardViewDelegate cardView, float maxElevation);

    float getMaxElevation(CardViewDelegate cardView);

    float getMinWidth(CardViewDelegate cardView);

    float getMinHeight(CardViewDelegate cardView);

    void updatePadding(CardViewDelegate cardView);

    void onCompatPaddingChanged(CardViewDelegate cardView);

    void onPreventCornerOverlapChanged(CardViewDelegate cardView);

    void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color);

    ColorStateList getBackgroundColor(CardViewDelegate cardView);
}

这里面便是一些关键的对外接口等等内容,同时做法也比较贴合我们开发实际。

最最最后,我们回归我们的CardView。

揭开CardView神秘面纱

    public CardView(Context context) {
        super(context);
        initialize(context, null, 0);
    }

    public CardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize(context, attrs, 0);
    }

    public CardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize(context, attrs, defStyleAttr);
    }

自定义View,实例化。没啥可说的。

到此结束~!!!


GitHub查看地址

https://github.com/HLQ-Struggle/MaterialDesignStudy


结束

东西Easy,还是要多去看看,只要看了,慢慢就会有收获~

伙计们,一起加油~

你可能感兴趣的:(Android,Love,and,Hatred,Material,Design,Study)