开源控件学习之-CircleImageView

在最近的项目中有用到这个组件,在github上的地址是:https://github.com/hdodenhof/CircleImageView

这里参考网上的一些文章。

首先、它是继承自ImageView,给ImageView设置图片的方法就是setImageXXX,不管你是用JAVA代码设置,还是在XML里面用src,它最终还是调用JAVA的setImageXXX方法,在这些个方法的最后一定有一个invalidate的方法,让其调用onDraw。


CircleImageView的原理也是这样,声明一个mBitmap这个就是要带到CircleImageView 里面的Bitmap,它的赋值都是在setImageXXX里面做的:


[java]  view plain copy
  1. @Override  
  2.    public void setImageBitmap(Bitmap bm) {  
  3.        super.setImageBitmap(bm);  
  4.        mBitmap = bm;  
  5.        System.out.println("setImageBitmap -- setup");  
  6.        setup();  
  7.    }  
  8.   
  9.    @Override  
  10.    public void setImageDrawable(Drawable drawable) {  
  11.        super.setImageDrawable(drawable);  
  12.        mBitmap = getBitmapFromDrawable(drawable);  
  13.        System.out.println("setImageDrawable -- setup");  
  14.        setup();  
  15.    }  
  16.   
  17.    @Override  
  18.    public void setImageResource(int resId) {  
  19.        super.setImageResource(resId);  
  20.        mBitmap = getBitmapFromDrawable(getDrawable());  
  21.        System.out.println("setImageResource -- setup");  
  22.        setup();  
  23.    }  
  24.   
  25.    @Override  
  26.    public void setImageURI(Uri uri) {  
  27.        super.setImageURI(uri);  
  28.        mBitmap = getBitmapFromDrawable(getDrawable());  
  29.        System.out.println("setImageURI -- setup");  
  30.        setup();  
  31.    }  



它们在设置完图片之后必定会调用setup方法,其实这个方法就是一个初始化操作而已,但是在setup的结束地方也调用了invalidate

它巧妙的用这个mBitmap为空,如果这个View还没有被设置图片则不会执行下面的方法 ,在setup里面有
[java]  view plain copy
  1. private void setup() {  
  2.        if (!mReady) {  
  3.            mSetupPending = true;  
  4.            return;  
  5.        }  
  6.        //这个mBitmap比较妙,只有当有图片被设置以后才会开始执行下面的方法。  
  7.        if (mBitmap == null) {  
  8.            return;  
  9.        }  
  10.   
  11.        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  
  12.   
  13.        mBitmapPaint.setAntiAlias(true);  
  14.        mBitmapPaint.setShader(mBitmapShader);  
  15.   
  16.        mBorderPaint.setStyle(Paint.Style.STROKE);  
  17.        mBorderPaint.setAntiAlias(true);  
  18.        mBorderPaint.setColor(mBorderColor);  
  19.        mBorderPaint.setStrokeWidth(mBorderWidth);  
  20.   
  21.        mBitmapHeight = mBitmap.getHeight();  
  22.        mBitmapWidth = mBitmap.getWidth();  
  23.   
  24.        //整个图像的显示区域:即全部的View大小区域。  
  25.        mBorderRect.set(00, getWidth(), getHeight());  
  26.        //Border的半径为长宽中取小的那一边,android中drawCircle的半径是内圆的半径,不是外圆的半径。  
  27.        mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);  
  28.   
  29.        //图片显示的区域:即View的大小区域减去边界的大小。  
  30.        mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);  
  31.        //图片的半径大小取图片小边。  
  32.        mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);  
  33.   
  34.        updateShaderMatrix();  
  35.        invalidate();  
  36.    }  




这个方法应当是在onMeasure之后调用的,我们知道图片设置成功之后,一定是在onMeasure之后,
所以它并没有逃脱自定义控件的的逻辑(1、重写onMeasure计算或者设置一些高度、宽度等数据,2、重写onDraw 3、重写onTouch做一些事件处理或者计算),它只是通过一个mBitmap的防空来判断它是在绘制成功之后的做的事情。

再看看onDraw就更加明白了。

这个onDraw也是利用如果图片不存在就不画它,这是一个十分巧妙的判断。

[java]  view plain copy
  1. @Override  
  2.    protected void onDraw(Canvas canvas) {  
  3.     System.out.println("onDraw before setup");  
  4.        if (getDrawable() == null) {  
  5.            return;  
  6.        }  
  7.        System.out.println("onDraw after setup");  
  8.        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);  
  9.        if (mBorderWidth != 0) {  
  10.            canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);  
  11.        }  
  12.          
  13.        /**********图片延迟加载时间的各个方法的生命周期(模仿网络图片的加载情况)*****************/  
  14.        //onSizeChanged --> onDraw --> setImageResource --> onDraw  
  15.    }  



整体来看思路是这样,这个View显示到页面时,如果没有图片资源其实是一个空画,就是虽然执行了onDraw但是并没有执行实际内容,只有当执行了setImageXXX,这里面是真正有内容了才去onDraw。


BitmapShader:使用一张位图作为纹理对某一区域进行填充,例如我们drawCircle,它就会对这个circle里面进行填充,它的两个参数就是对X,Y轴进行填充的一些规则。



[java]  view plain copy
  1. package de.hdodenhof.circleimageview;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.TypedArray;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.BitmapShader;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Color;  
  9. import android.graphics.Matrix;  
  10. import android.graphics.Paint;  
  11. import android.graphics.RectF;  
  12. import android.graphics.Shader;  
  13. import android.graphics.drawable.BitmapDrawable;  
  14. import android.graphics.drawable.ColorDrawable;  
  15. import android.graphics.drawable.Drawable;  
  16. import android.net.Uri;  
  17. import android.util.AttributeSet;  
  18. import android.widget.ImageView;  
  19.   
  20. public class CircleImageView extends ImageView {  
  21.   
  22.     private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;  
  23.   
  24.     private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;  
  25.     private static final int COLORDRAWABLE_DIMENSION = 2;  
  26.   
  27.     private static final int DEFAULT_BORDER_WIDTH = 0;  
  28.     private static final int DEFAULT_BORDER_COLOR = Color.BLACK;  
  29.   
  30.     private final RectF mDrawableRect = new RectF();  
  31.     private final RectF mBorderRect = new RectF();  
  32.   
  33.     private final Matrix mShaderMatrix = new Matrix();  
  34.     private final Paint mBitmapPaint = new Paint();  
  35.     private final Paint mBorderPaint = new Paint();  
  36.   
  37.     private int mBorderColor = DEFAULT_BORDER_COLOR;  
  38.     private int mBorderWidth = DEFAULT_BORDER_WIDTH;  
  39.   
  40.     private Bitmap mBitmap;  
  41.     private BitmapShader mBitmapShader;  
  42.     private int mBitmapWidth;  
  43.     private int mBitmapHeight;  
  44.   
  45.     private float mDrawableRadius;  
  46.     private float mBorderRadius;  
  47.   
  48.     private boolean mReady;  
  49.     private boolean mSetupPending;  
  50.   
  51.     public CircleImageView(Context context) {  
  52.         super(context);  
  53.   
  54.         init();  
  55.     }  
  56.   
  57.     public CircleImageView(Context context, AttributeSet attrs) {  
  58.         this(context, attrs, 0);  
  59.     }  
  60.   
  61.     public CircleImageView(Context context, AttributeSet attrs, int defStyle) {  
  62.         super(context, attrs, defStyle);  
  63.   
  64.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);  
  65.   
  66.         mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);  
  67.         mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);  
  68.   
  69.         a.recycle();  
  70.   
  71.         init();  
  72.     }  
  73.   
  74.     private void init() {  
  75.         super.setScaleType(SCALE_TYPE);  
  76.         mReady = true;  
  77.   
  78.         if (mSetupPending) {  
  79.             System.out.println("init -- setup");  
  80.             setup();  
  81.             mSetupPending = false;  
  82.         }  
  83.     }  
  84.   
  85.     @Override  
  86.     public ScaleType getScaleType() {  
  87.         return SCALE_TYPE;  
  88.     }  
  89.   
  90.     @Override  
  91.     public void setScaleType(ScaleType scaleType) {  
  92.         if (scaleType != SCALE_TYPE) {  
  93.             throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));  
  94.         }  
  95.     }  
  96.   
  97.     @Override  
  98.     public void setAdjustViewBounds(boolean adjustViewBounds) {  
  99.         if (adjustViewBounds) {  
  100.             throw new IllegalArgumentException("adjustViewBounds not supported.");  
  101.         }  
  102.     }  
  103.   
  104.     @Override  
  105.     protected void onDraw(Canvas canvas) {  
  106.         System.out.println("onDraw before setup");  
  107.         if (getDrawable() == null) {  
  108.             return;  
  109.         }  
  110.         System.out.println("onDraw after setup");  
  111.         canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);  
  112.         if (mBorderWidth != 0) {  
  113.             canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);  
  114.         }  
  115.           
  116.         /**********图片延迟加载时间的各个方法的生命周期(模仿网络图片的加载情况)*****************/  
  117.         //onSizeChanged --> onDraw --> setImageResource --> onDraw  
  118.     }  
  119.   
  120.     @Override  
  121.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  122.         super.onSizeChanged(w, h, oldw, oldh);  
  123.         System.out.println("onSizeChanged -- setup");  
  124.         setup();  
  125.     }  
  126.   
  127.     public int getBorderColor() {  
  128.         return mBorderColor;  
  129.     }  
  130.   
  131.     public void setBorderColor(int borderColor) {  
  132.         if (borderColor == mBorderColor) {  
  133.             return;  
  134.         }  
  135.   
  136.         mBorderColor = borderColor;  
  137.         mBorderPaint.setColor(mBorderColor);  
  138.         invalidate();  
  139.     }  
  140.   
  141.     public int getBorderWidth() {  
  142.         return mBorderWidth;  
  143.     }  
  144.   
  145.     public void setBorderWidth(int borderWidth) {  
  146.         if (borderWidth == mBorderWidth) {  
  147.             return;  
  148.         }  
  149.   
  150.         mBorderWidth = borderWidth;  
  151.         System.out.println("setBorderWidth -- setup");  
  152.         setup();  
  153.     }  
  154.   
  155.     @Override  
  156.     public void setImageBitmap(Bitmap bm) {  
  157.         super.setImageBitmap(bm);  
  158.         mBitmap = bm;  
  159.         System.out.println("setImageBitmap -- setup");  
  160.         setup();  
  161.     }  
  162.   
  163.     @Override  
  164.     public void setImageDrawable(Drawable drawable) {  
  165.         super.setImageDrawable(drawable);  
  166.         mBitmap = getBitmapFromDrawable(drawable);  
  167.         System.out.println("setImageDrawable -- setup");  
  168.         setup();  
  169.     }  
  170.   
  171.     @Override  
  172.     public void setImageResource(int resId) {  
  173.         super.setImageResource(resId);  
  174.         mBitmap = getBitmapFromDrawable(getDrawable());  
  175.         System.out.println("setImageResource -- setup");  
  176.         setup();  
  177.     }  
  178.   
  179.     @Override  
  180.     public void setImageURI(Uri uri) {  
  181.         super.setImageURI(uri);  
  182.         mBitmap = getBitmapFromDrawable(getDrawable());  
  183.         System.out.println("setImageURI -- setup");  
  184.         setup();  
  185.     }  
  186.   
  187.     private Bitmap getBitmapFromDrawable(Drawable drawable) {  
  188.         if (drawable == null) {  
  189.             return null;  
  190.         }  
  191.   
  192.         if (drawable instanceof BitmapDrawable) {  
  193.             return ((BitmapDrawable) drawable).getBitmap();  
  194.         }  
  195.   
  196.         try {  
  197.             Bitmap bitmap;  
  198.   
  199.             if (drawable instanceof ColorDrawable) {  
  200.                 bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);  
  201.             } else {  
  202.                 bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);  
  203.             }  
  204.   
  205.             Canvas canvas = new Canvas(bitmap);  
  206.             drawable.setBounds(00, canvas.getWidth(), canvas.getHeight());  
  207.             drawable.draw(canvas);  
  208.             return bitmap;  
  209.         } catch (OutOfMemoryError e) {  
  210.             return null;  
  211.         }  
  212.     }  
  213.   
  214.     private void setup() {  
  215.         if (!mReady) {  
  216.             mSetupPending = true;  
  217.             return;  
  218.         }  
  219.         //这个mBitmap比较妙,只有当有图片被设置以后才会开始执行下面的方法。  
  220.         if (mBitmap == null) {  
  221.             return;  
  222.         }  
  223.   
  224.         mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  
  225.   
  226.         mBitmapPaint.setAntiAlias(true);  
  227.         mBitmapPaint.setShader(mBitmapShader);  
  228.   
  229.         mBorderPaint.setStyle(Paint.Style.STROKE);  
  230.         mBorderPaint.setAntiAlias(true);  
  231.         mBorderPaint.setColor(mBorderColor);  
  232.         mBorderPaint.setStrokeWidth(mBorderWidth);  
  233.   
  234.         mBitmapHeight = mBitmap.getHeight();  
  235.         mBitmapWidth = mBitmap.getWidth();  
  236.   
  237.         //整个图像的显示区域:即全部的View大小区域。  
  238.         mBorderRect.set(00, getWidth(), getHeight());  
  239.         //Border的半径为长宽中取小的那一边,android中drawCircle的半径是内圆的半径,不是外圆的半径。  
  240.         mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);  
  241.   
  242.         //图片显示的区域:即View的大小区域减去边界的大小。  
  243.         mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);  
  244.         //图片的半径大小取图片小边。  
  245.         mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);  
  246.   
  247.         updateShaderMatrix();  
  248.         invalidate();  
  249.     }  
  250.   
  251.     private void updateShaderMatrix() {  
  252.         float scale;  
  253.         float dx = 0;  
  254.         float dy = 0;  
  255.   
  256.         mShaderMatrix.set(null);  
  257.   
  258.         if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {  
  259.             scale = mDrawableRect.height() / (float) mBitmapHeight;  
  260.             dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;  
  261.         } else {  
  262.             scale = mDrawableRect.width() / (float) mBitmapWidth;  
  263.             dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;  
  264.         }  
  265.           
  266.         System.out.println("scale "+scale);  
  267.         mShaderMatrix.setScale(scale, scale);  
  268.         mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);  
  269.   
  270.         mBitmapShader.setLocalMatrix(mShaderMatrix);  
  271.     }  
  272.   
  273. }  

你可能感兴趣的:(android,开源,图片,imageview,控件)