美文网首页菜菜AndroidAndroid开发知识集Android开发随笔
【ImageView】自定义ImageView系列(四)——一个

【ImageView】自定义ImageView系列(四)——一个

作者: 亦枫 | 来源:发表于2015-12-21 14:50 被阅读3872次

    在上一篇文章【ImageView】自定义ImageView系列(三)——一个功能强大的圆角图片库(上) 中,介绍了原作者对于开源项目 SelectableRoundedImageView 的需求出现背景和技术实现思路。这篇文章,通过对核心自定义View源码的分析,学习一下它的实现原理。

    SelectableRoundedImageView.png

    Java源码:

        
    package com.joooonho;
    
    import com.joooonho.R;
    
    import android.content.Context;
    import android.content.res.ColorStateList;
    import android.content.res.Resources;
    import android.content.res.Resources.NotFoundException;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.Config;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.ColorFilter;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.PixelFormat;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.graphics.Shader;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.Drawable;
    import android.graphics.drawable.LayerDrawable;
    import android.net.Uri;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.widget.ImageView;
    
    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) {
                    Log.w(TAG, "Unable to find resource: " + mResource, 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 {
                        Log.w(TAG, "Failed to create bitmap from drawable!");
                    }
                }
                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;
            }
        }
    
    }
    

    attrs.xml资源:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <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>
    
    </resources>
    

    为了便于扩展,实现通过代码和xml属性自由控制各个属性,所以源码中的getter和setter方法较多,导致源码较长。通过阅读源码,鸟哥总结了以下几个要点部分,帮助大家理解源码。

    • 消失的onDraw方法
      看完整个源码,会发现,没有自定义View经典的onDraw绘制方法(将导致在布局中无法预览自定义界面效果,这也是这个开源库的一个缺陷吧,大家感兴趣的话可以在源码的基础上进行修改实现)。而是通过自定义Drawable类——SelectableRoundedCornerDrawable,对ImageView的drawable进行圆角和圆形处理,再通过ImageView的setImageDrawable等系统API传递给ImageView。

    • BitmapShader给画笔着色
      自定义View实现圆形或圆角的方式有多种,常见的比如 Paint.setXfermodePaint.setShader(BitmapShader) ,鸟哥前面介绍几篇文章如【ImageView】自定义ImageView系列(一)——简单圆形图片中用到的是前者,本文中介绍的 SelectableRoundedImageView 用到的是后者。使用着色器BitmapShader给绘制图片的画笔着色,通过画笔展示图片。

    • 闭合的圆形或圆角Path
      在自定义Drawable内部类中,重写 draw(Canvas canvas) 方法,通过 canvas.drawPath 方法使用被BitmapShader着色器着色过的Paint绘制闭合路径。源码中使用一个布尔型变量mOval控制是否绘制圆形图片,使用 Path.addOval(RectF oval, Direction dir) 添加圆形路径或者 Path.addRoundRect(RectF rect, float[] radii, Direction dir) 方法添加圆角矩形路径。

    了解以上几点,基本上能掌握 SelectableRoundedImageView 核心的实现原理。通过缩放方式和一些几何运算,便可以计算出边框的宽度、圆角的半径、图片的大小等,这里不细作分析,查看源码即可。

    在布局中使用:

    <com.joooonho.SelectableRoundedImageView
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/image"
            android:src="@drawable/photo1"
            android:scaleType="centerCrop"
            app:sriv_left_top_corner_radius="16dip"
            app:sriv_right_top_corner_radius="0dip"
            app:sriv_left_bottom_corner_radius="48dip"
            app:sriv_right_bottom_corner_radius="16dip"
            app:sriv_border_width="2dip"
            app:sriv_border_color="#008fea"
            app:sriv_oval="true" />
    

    在代码中使用:

    SelectableRoundedImageView sriv = new SelectableRoundedImageView(context);
    sriv.setScaleType(ScaleType.CENTER_CROP);
    sriv.setCornerRadiiDP(4, 4, 0, 0);
    sriv.setBorderWidthDP(4);
    sriv.setBorderColor(Color.BLUE);
    sriv.setImageDrawable(drawable);
    sriv.setOval(true);
    

    需要注意的一点是,如果使用Android图片加载开源框架 Android-Universal-Image-Loader ,确保在配置 DisplayImageOptions 参数时使用 SimpleBitmapDisplayer 或者 FadeInBitmapDisplayer,而不是 RoundedBitmapDisplayer 或者 RoundedVignetteBitmapDisplayer,如:

    options = new DisplayImageOptions.Builder()
                    .showImageOnLoading(R.drawable.ic_stub)
                    .showImageForEmptyUri(R.drawable.ic_empty)
                    .showImageOnFail(R.drawable.ic_error)
                    .cacheInMemory(true)
                    .cacheOnDisk(true)
                    .considerExifParams(true)
    //              .displayer(new RoundedBitmapDisplayer(20))
    //              DO NOT USE RoundedBitmapDisplayer. Use SimpleBitmapDisplayer!
                    .displayer(new SimpleBitmapDisplayer())
                    .build();
    

    欢迎关注鸟哥微信公众号【技术鸟】,一起分享,一起学习!

    微信公众号【技术鸟】.gif

    相关文章

      网友评论

      本文标题:【ImageView】自定义ImageView系列(四)——一个

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