MaterialDesign系列文章(九)CardView的使用及適配

今天改需求改的我頭疼,最可氣的是,測試悠哉遊哉的跑過來,丟下一句"喲,又寫BUG呢?",我一想我也不是慣孩子人啊?當時我就回了一句,"正寫着呢?要不晚上一塊兒加班啊?請你吃好吃的",這貨竟然說"行啊!"吃貨的世界真的不懂!可是真心說一句,改需求能不能不這麼從容,每次都是立刻上線了,淡定的丟下一句,不行改改吧!個人內心是這樣的。。。 android

原本都準備好下班打卡出去浪的。只能坐在那裏改、改、改。總說咱們程序員是單身汪,能不單身嗎?而後每次改好了需求等測試反饋仍是這樣的!!!

好吧!!!原諒個人放縱,被公司看見會不會被打死???我要去看個人祕籍了。。。程序員

一位具備多年開發經驗的人推薦

好了不扯淡了!canvas

本文知識點

  • CardView是什麼?
  • CardView有哪些屬性能夠設置?
  • CardView的簡單使用?
  • CardView的適配問題?

1.CardView是什麼?

CardView是google在5.0中提供帶圓角和陰影的佈局,繼承自FrameLayout。bash

2.CardView的一些屬性設置

  • android:cardBackgroundColor 設置背景
  • android:cardCornerRadius 設置圓角
  • app:cardElevation 設置陰影大小
  • app:cardMaxElevation 設置陰影的最大高度
  • app:contentPadding 內容距離邊界的距離
  • app:contentPaddingXXX 設置局部的內邊距,替換Padding的,在CardView中設置Padding是不起做用的。
  • app:cardUseCompatPadding 若是您須要將CardView與其餘視圖對齊,可能在21如下,能夠將此標誌設置爲true,CardView將在21以後的平臺上添加相同的填充值。
  • app:cardPreventCornerOverlap 是否裁剪邊界以防止重疊

能設置的屬性就這麼多了,沒有什麼好說的。可是最後兩個屬性會在後面的時候詳細講解,涉及到相應的適配!markdown

3. CardView的簡單使用

其實在平時的開發中,若是不考慮適配的話,CardView使用起來是很簡單,可是提及適配的話,感受真心頭疼。我第一次用CardView的時候,弄適配弄了兩個多消失,當時以爲天都是黑的!!!先看簡單的效果吧。。。app

<android.support.v7.widget.CardView
        android:id="@+id/cv1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        app:cardBackgroundColor="#c4c6c8"
        app:cardCornerRadius="10dp"
        app:cardElevation="10dp"
        app:cardPreventCornerOverlap="true"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/heard_1" />
    </android.support.v7.widget.CardView>
複製代碼

效果是這個樣子的。。。ide

效果仍是能夠的

注意啊這個是5.0以上的手機拍的,它默認幫咱們把圓角裁剪了(若是你設置了圓角的話)。按照上面的屬性進行設置就能夠了!佈局

4.CardView的適配問題

這個纔是本片文章的重點問題!若是沒有作過適配的人,會以爲CardView很簡單的。可是若是讓你適配的話,你就。。。先來看一張照片!測試

重點的地方,我都標記出來了,若是大家產品和美工能容忍的話。那隻能說你跟他們有一腿!this

4.1 關於內邊距的問題!

圖片上下左右的邊距比較好處理,由於上面提到了兩個屬性

app:cardUseCompatPadding="true"
    app:cardPreventCornerOverlap="false"
複製代碼
  • 屬性1: 這個屬性若是你設置成true的話,那麼就能保證全部版本的Padding都設置相同的Padding,默認是false,就像上面這張圖同樣,會留出必定的邊距!
  • 屬性2: 是否裁剪邊角,以保證邊角是圓形的!在21以上的版本,默認是幫你裁剪好圓角的,可是21以前的版本是不會幫你裁剪的,會出現下面這種!

內邊距是不見了,可是圓角的處理就成了接下來的問題了。

4.2 關於圓角的處理

最開始我覺得設置上面兩個屬性就能輕鬆的設置圓角了呢?太相信谷歌了。而後個人想法就是找一個處理圓角的控件,可是找了很久,基本上都是處理四個圓角的,沒有單個圓角的處理,當時產品的意思是隻處理左邊的兩個圓角。好吧,繼續找,最後中於找到了。當時給你們發個福利了!哈哈,我真的不知道怎麼上傳相應的文件。原諒個人無知!

public class SelectableRoundedImageView extends ImageView {

    public static final String TAG = "SelectableRoundedImageView";

    private int mResource = 0;

    private static final ScaleType[] sScaleTypeArray = {
        ScaleType.MATRIX,
        ScaleType.FIT_XY,
        ScaleType.FIT_START,
        ScaleType.FIT_CENTER,
        ScaleType.FIT_END,
        ScaleType.CENTER,
        ScaleType.CENTER_CROP,
        ScaleType.CENTER_INSIDE
    };

    // Set default scale type to FIT_CENTER, which is default scale type of
    // original ImageView.
    private ScaleType mScaleType = ScaleType.FIT_CENTER;

    private float mLeftTopCornerRadius = 0.0f;
    private float mRightTopCornerRadius = 0.0f;
    private float mLeftBottomCornerRadius = 0.0f;
    private float mRightBottomCornerRadius = 0.0f;

    private float mBorderWidth = 0.0f;
    private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
    private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);

    private boolean isOval = false;

    private Drawable mDrawable;

    private float[] mRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };

    public SelectableRoundedImageView(Context context) {
        super(context);
    }

    public SelectableRoundedImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SelectableRoundedImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.SelectableRoundedImageView, defStyle, 0);

        final int index = a.getInt(R.styleable.SelectableRoundedImageView_android_scaleType, -1);
        if (index >= 0) {
            setScaleType(sScaleTypeArray[index]);
        }

        mLeftTopCornerRadius = a.getDimensionPixelSize(
                R.styleable.SelectableRoundedImageView_sriv_left_top_corner_radius, 0);
        mRightTopCornerRadius = a.getDimensionPixelSize(
                R.styleable.SelectableRoundedImageView_sriv_right_top_corner_radius, 0);
        mLeftBottomCornerRadius = a.getDimensionPixelSize(
                R.styleable.SelectableRoundedImageView_sriv_left_bottom_corner_radius, 0);
        mRightBottomCornerRadius = a.getDimensionPixelSize(
                R.styleable.SelectableRoundedImageView_sriv_right_bottom_corner_radius, 0);
        
        if (mLeftTopCornerRadius < 0.0f || mRightTopCornerRadius < 0.0f
                || mLeftBottomCornerRadius < 0.0f || mRightBottomCornerRadius < 0.0f) {
            throw new IllegalArgumentException("radius values cannot be negative.");
        }
        
        mRadii = new float[] { 
                mLeftTopCornerRadius, mLeftTopCornerRadius,
                mRightTopCornerRadius, mRightTopCornerRadius, 
                mRightBottomCornerRadius, mRightBottomCornerRadius, 
                mLeftBottomCornerRadius, mLeftBottomCornerRadius };

        mBorderWidth = a.getDimensionPixelSize(
                R.styleable.SelectableRoundedImageView_sriv_border_width, 0);
        if (mBorderWidth < 0) {
            throw new IllegalArgumentException("border width cannot be negative.");
        }

        mBorderColor = a
                .getColorStateList(R.styleable.SelectableRoundedImageView_sriv_border_color);
        if (mBorderColor == null) {
            mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
        }

        isOval = a.getBoolean(R.styleable.SelectableRoundedImageView_sriv_oval, false);
        a.recycle();
        
        updateDrawable();
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        invalidate();
    }

    @Override
    public ScaleType getScaleType() {
        return mScaleType;
    }

    @Override
    public void setScaleType(ScaleType scaleType) {
        super.setScaleType(scaleType);
        mScaleType = scaleType;
        updateDrawable();
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        mResource = 0;
        mDrawable = SelectableRoundedCornerDrawable.fromDrawable(drawable, getResources());
        super.setImageDrawable(mDrawable);
        updateDrawable();
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        mResource = 0;
        mDrawable = SelectableRoundedCornerDrawable.fromBitmap(bm, getResources());
        super.setImageDrawable(mDrawable);
        updateDrawable();
    }

    @Override
    public void setImageResource(int resId) {
        if (mResource != resId) {
            mResource = resId;
            mDrawable = resolveResource();
            super.setImageDrawable(mDrawable);
            updateDrawable();
        }
    }

    @Override
    public void setImageURI(Uri uri) {
        super.setImageURI(uri);
        setImageDrawable(getDrawable());
    }

    private Drawable resolveResource() {
        Resources rsrc = getResources();
        if (rsrc == null) {
            return null;
        }

        Drawable d = null;

        if (mResource != 0) {
            try {
                d = rsrc.getDrawable(mResource);
            } catch (NotFoundException e) {
                // Don't try again. mResource = 0; } } return SelectableRoundedCornerDrawable.fromDrawable(d, getResources()); } private void updateDrawable() { if (mDrawable == null) { return; } ((SelectableRoundedCornerDrawable) mDrawable).setScaleType(mScaleType); ((SelectableRoundedCornerDrawable) mDrawable).setCornerRadii(mRadii); ((SelectableRoundedCornerDrawable) mDrawable).setBorderWidth(mBorderWidth); ((SelectableRoundedCornerDrawable) mDrawable).setBorderColor(mBorderColor); ((SelectableRoundedCornerDrawable) mDrawable).setOval(isOval); } public float getCornerRadius() { return mLeftTopCornerRadius; } /** * Set radii for each corner. * * @param leftTop The desired radius for left-top corner in dip. * @param rightTop The desired desired radius for right-top corner in dip. * @param leftBottom The desired radius for left-bottom corner in dip. * @param rightBottom The desired radius for right-bottom corner in dip. * */ public void setCornerRadiiDP(float leftTop, float rightTop, float leftBottom, float rightBottom) { final float density = getResources().getDisplayMetrics().density; final float lt = leftTop * density; final float rt = rightTop * density; final float lb = leftBottom * density; final float rb = rightBottom * density; mRadii = new float[] { lt, lt, rt, rt, rb, rb, lb, lb }; updateDrawable(); } public float getBorderWidth() { return mBorderWidth; } /** * Set border width. * * @param width * The desired width in dip. */ public void setBorderWidthDP(float width) { float scaledWidth = getResources().getDisplayMetrics().density * width; if (mBorderWidth == scaledWidth) { return; } mBorderWidth = scaledWidth; updateDrawable(); invalidate(); } public int getBorderColor() { return mBorderColor.getDefaultColor(); } public void setBorderColor(int color) { setBorderColor(ColorStateList.valueOf(color)); } public ColorStateList getBorderColors() { return mBorderColor; } public void setBorderColor(ColorStateList colors) { if (mBorderColor.equals(colors)) { return; } mBorderColor = (colors != null) ? colors : ColorStateList .valueOf(DEFAULT_BORDER_COLOR); updateDrawable(); if (mBorderWidth > 0) { invalidate(); } } public boolean isOval() { return isOval; } public void setOval(boolean oval) { isOval = oval; updateDrawable(); invalidate(); } static class SelectableRoundedCornerDrawable extends Drawable { private static final String TAG = "SelectableRoundedCornerDrawable"; private static final int DEFAULT_BORDER_COLOR = Color.BLACK; private RectF mBounds = new RectF(); private RectF mBorderBounds = new RectF(); private final RectF mBitmapRect = new RectF(); private final int mBitmapWidth; private final int mBitmapHeight; private final Paint mBitmapPaint; private final Paint mBorderPaint; private BitmapShader mBitmapShader; private float[] mRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 }; private float[] mBorderRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 }; private boolean mOval = false; private float mBorderWidth = 0; private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR); // Set default scale type to FIT_CENTER, which is default scale type of // original ImageView. private ScaleType mScaleType = ScaleType.FIT_CENTER; private Path mPath = new Path(); private Bitmap mBitmap; private boolean mBoundsConfigured = false; public SelectableRoundedCornerDrawable(Bitmap bitmap, Resources r) { mBitmap = bitmap; mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); if (bitmap != null) { mBitmapWidth = bitmap.getScaledWidth(r.getDisplayMetrics()); mBitmapHeight = bitmap.getScaledHeight(r.getDisplayMetrics()); } else { mBitmapWidth = mBitmapHeight = -1; } mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight); mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBitmapPaint.setStyle(Paint.Style.FILL); mBitmapPaint.setShader(mBitmapShader); mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR)); mBorderPaint.setStrokeWidth(mBorderWidth); } public static SelectableRoundedCornerDrawable fromBitmap(Bitmap bitmap, Resources r) { if (bitmap != null) { return new SelectableRoundedCornerDrawable(bitmap, r); } else { return null; } } public static Drawable fromDrawable(Drawable drawable, Resources r) { if (drawable != null) { if (drawable instanceof SelectableRoundedCornerDrawable) { return drawable; } else if (drawable instanceof LayerDrawable) { LayerDrawable ld = (LayerDrawable) drawable; final int num = ld.getNumberOfLayers(); for (int i = 0; i < num; i++) { Drawable d = ld.getDrawable(i); ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d, r)); } return ld; } Bitmap bm = drawableToBitmap(drawable); if (bm != null) { return new SelectableRoundedCornerDrawable(bm, r); } else { } } return drawable; } public static Bitmap drawableToBitmap(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap; int width = Math.max(drawable.getIntrinsicWidth(), 2); int height = Math.max(drawable.getIntrinsicHeight(), 2); try { bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); } catch (IllegalArgumentException e) { e.printStackTrace(); bitmap = null; } return bitmap; } @Override public boolean isStateful() { return mBorderColor.isStateful(); } @Override protected boolean onStateChange(int[] state) { int newColor = mBorderColor.getColorForState(state, 0); if (mBorderPaint.getColor() != newColor) { mBorderPaint.setColor(newColor); return true; } else { return super.onStateChange(state); } } private void configureBounds(Canvas canvas) { // I have discovered a truly marvelous explanation of this, // which this comment space is too narrow to contain. :) // If you want to understand what's going on here,
            // See http://www.joooooooooonhokim.com/?p=289
            Rect clipBounds = canvas.getClipBounds();
            Matrix canvasMatrix = canvas.getMatrix();

            if (ScaleType.CENTER == mScaleType) {
                mBounds.set(clipBounds);
            } else if (ScaleType.CENTER_CROP == mScaleType) {
                applyScaleToRadii(canvasMatrix);
                mBounds.set(clipBounds);
            } else if (ScaleType.FIT_XY == mScaleType) {
                Matrix m = new Matrix();
                m.setRectToRect(mBitmapRect, new RectF(clipBounds), Matrix.ScaleToFit.FILL);
                mBitmapShader.setLocalMatrix(m);
                mBounds.set(clipBounds);
            } else if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType
                    || ScaleType.FIT_CENTER == mScaleType || ScaleType.CENTER_INSIDE == mScaleType) {
                applyScaleToRadii(canvasMatrix);
                mBounds.set(mBitmapRect);
            } else if (ScaleType.MATRIX == mScaleType) {
                applyScaleToRadii(canvasMatrix);
                mBounds.set(mBitmapRect);
            }
        }

        private void applyScaleToRadii(Matrix m) {
            float[] values = new float[9];
            m.getValues(values);
            for (int i = 0; i < mRadii.length; i++) {
                mRadii[i] = mRadii[i] / values[0];
            }
        }

        private void adjustCanvasForBorder(Canvas canvas) {
            Matrix canvasMatrix = canvas.getMatrix();
            final float[] values = new float[9];
            canvasMatrix.getValues(values);

            final float scaleFactorX = values[0];
            final float scaleFactorY = values[4];
            final float translateX = values[2];
            final float translateY = values[5];

            final float newScaleX = mBounds.width()
                    / (mBounds.width() + mBorderWidth + mBorderWidth);
            final float newScaleY = mBounds.height()
                    / (mBounds.height() + mBorderWidth + mBorderWidth);

            canvas.scale(newScaleX, newScaleY);
            if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType
                    || ScaleType.FIT_XY == mScaleType || ScaleType.FIT_CENTER == mScaleType
                    || ScaleType.CENTER_INSIDE == mScaleType || ScaleType.MATRIX == mScaleType) {
                canvas.translate(mBorderWidth, mBorderWidth);
            } else if (ScaleType.CENTER == mScaleType || ScaleType.CENTER_CROP == mScaleType) {
                // First, make translate values to 0
                canvas.translate(
                        -translateX / (newScaleX * scaleFactorX), 
                        -translateY / (newScaleY * scaleFactorY));
                // Then, set the final translate values.
                canvas.translate(-(mBounds.left - mBorderWidth), -(mBounds.top - mBorderWidth));
            } 
        }

        private void adjustBorderWidthAndBorderBounds(Canvas canvas) {
            Matrix canvasMatrix = canvas.getMatrix();
            final float[] values = new float[9];
            canvasMatrix.getValues(values);

            final float scaleFactor = values[0];

            float viewWidth = mBounds.width() * scaleFactor;
            mBorderWidth = (mBorderWidth * mBounds.width()) / (viewWidth - (2 * mBorderWidth));
            mBorderPaint.setStrokeWidth(mBorderWidth);

            mBorderBounds.set(mBounds);
            mBorderBounds.inset(- mBorderWidth / 2, - mBorderWidth / 2);
        }

        private void setBorderRadii() {
            for (int i = 0; i < mRadii.length; i++) {
                if (mRadii[i] > 0) {
                    mBorderRadii[i] = mRadii[i];
                    mRadii[i] = mRadii[i] - mBorderWidth;
                }
            }
        }

        @Override
        public void draw(Canvas canvas) {
            canvas.save();
            if (!mBoundsConfigured) {
                configureBounds(canvas);
                if (mBorderWidth > 0) {
                    adjustBorderWidthAndBorderBounds(canvas);
                    setBorderRadii();
                }
                mBoundsConfigured = true;
            }

            if (mOval) {
                if (mBorderWidth > 0) {
                    adjustCanvasForBorder(canvas);
                    mPath.addOval(mBounds, Path.Direction.CW);
                    canvas.drawPath(mPath, mBitmapPaint);
                    mPath.reset();
                    mPath.addOval(mBorderBounds, Path.Direction.CW);
                    canvas.drawPath(mPath, mBorderPaint);
                } else {
                    mPath.addOval(mBounds, Path.Direction.CW);
                    canvas.drawPath(mPath, mBitmapPaint);
                }
            } else {
                if (mBorderWidth > 0) {
                    adjustCanvasForBorder(canvas);
                    mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);
                    canvas.drawPath(mPath, mBitmapPaint);
                    mPath.reset();
                    mPath.addRoundRect(mBorderBounds, mBorderRadii, Path.Direction.CW);
                    canvas.drawPath(mPath, mBorderPaint);
                } else {
                    mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);
                    canvas.drawPath(mPath, mBitmapPaint);
                }
            }
            canvas.restore();
        }

        public void setCornerRadii(float[] radii) {
            if (radii == null)
                return;

            if (radii.length != 8) {
                throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
            }

            for (int i = 0; i < radii.length; i++) {
                mRadii[i] = radii[i];
            }
        }

        @Override
        public int getOpacity() {
            return (mBitmap == null || mBitmap.hasAlpha() || mBitmapPaint.getAlpha() < 255) ? PixelFormat.TRANSLUCENT
                    : PixelFormat.OPAQUE;
        }

        @Override
        public void setAlpha(int alpha) {
            mBitmapPaint.setAlpha(alpha);
            invalidateSelf();
        }

        @Override
        public void setColorFilter(ColorFilter cf) {
            mBitmapPaint.setColorFilter(cf);
            invalidateSelf();
        }

        @Override
        public void setDither(boolean dither) {
            mBitmapPaint.setDither(dither);
            invalidateSelf();
        }

        @Override
        public void setFilterBitmap(boolean filter) {
            mBitmapPaint.setFilterBitmap(filter);
            invalidateSelf();
        }

        @Override
        public int getIntrinsicWidth() {
            return mBitmapWidth;
        }

        @Override
        public int getIntrinsicHeight() {
            return mBitmapHeight;
        }

        public float getBorderWidth() {
            return mBorderWidth;
        }

        public void setBorderWidth(float width) {
            mBorderWidth = width;
            mBorderPaint.setStrokeWidth(width);
        }

        public int getBorderColor() {
            return mBorderColor.getDefaultColor();
        }

        public void setBorderColor(int color) {
            setBorderColor(ColorStateList.valueOf(color));
        }

        public ColorStateList getBorderColors() {
            return mBorderColor;
        }

        /**
         * Controls border color of this ImageView.
         * 
         * @param colors
         *            The desired border color. If it's null, no border will be * drawn. * */ public void setBorderColor(ColorStateList colors) { if (colors == null) { mBorderWidth = 0; mBorderColor = ColorStateList.valueOf(Color.TRANSPARENT); mBorderPaint.setColor(Color.TRANSPARENT); } else { mBorderColor = colors; mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR)); } } public boolean isOval() { return mOval; } public void setOval(boolean oval) { mOval = oval; } public ScaleType getScaleType() { return mScaleType; } public void setScaleType(ScaleType scaleType) { if (scaleType == null) { return; } mScaleType = scaleType; } } } 複製代碼

自定義屬性

<declare-styleable name="SelectableRoundedImageView">
        <attr name="sriv_left_top_corner_radius" format="dimension"/>
        <attr name="sriv_right_top_corner_radius" format="dimension"/>
        <attr name="sriv_left_bottom_corner_radius" format="dimension"/>
        <attr name="sriv_right_bottom_corner_radius" format="dimension"/>
        <attr name="sriv_border_width" format="dimension"/>
        <attr name="sriv_border_color" format="color"/>
        <attr name="sriv_oval" format="boolean"/>
        <attr name="android:scaleType"/>
    </declare-styleable>
複製代碼

也忘記是哪位大神些的了,在這裏謝謝做者的開源精神!使用的時候直接設置相應的邊角就能夠了!仍是很方便的。。。今天就到這裏吧,我可把我壓箱底的東西都拿出來了,但願對你有幫助!好了,今天就到這裏了,See You!!!

相關文章
相關標籤/搜索