[原创]android开源项目源码解析(一)----CircleImageView的源码解析

CircleImageView的代码很简洁,所以先将此工程做为源码解析系列的第一篇文章.android

解析说明都在代码里了。express

 1 /*
 2  * Copyright 2014 - 2015 Henning Dodenhof  3  *  4  * Licensed under the Apache License, Version 2.0 (the "License");  5  * you may not use this file except in compliance with the License.  6  * You may obtain a copy of the License at  7  *  8  * http://www.apache.org/licenses/LICENSE-2.0
 9  *  10  * Unless required by applicable law or agreed to in writing, software  11  * distributed under the License is distributed on an "AS IS" BASIS,  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  13  * See the License for the specific language governing permissions and  14  * limitations under the License.  15  */
 16 package de.hdodenhof.circleimageview;  17 
 18 import android.content.Context;  19 import android.content.res.TypedArray;  20 import android.graphics.Bitmap;  21 import android.graphics.BitmapShader;  22 import android.graphics.Canvas;  23 import android.graphics.Color;  24 import android.graphics.ColorFilter;  25 import android.graphics.Matrix;  26 import android.graphics.Paint;  27 import android.graphics.RectF;  28 import android.graphics.Shader;  29 import android.graphics.drawable.BitmapDrawable;  30 import android.graphics.drawable.ColorDrawable;  31 import android.graphics.drawable.Drawable;  32 import android.net.Uri;  33 import android.support.annotation.ColorInt;  34 import android.support.annotation.ColorRes;  35 import android.support.annotation.DrawableRes;  36 import android.util.AttributeSet;  37 import android.widget.ImageView;  38 
 39 public class CircleImageView extends ImageView {  40 
 41     private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;//这里限制了只要一种模式:CENTER_CROP
 42 
 43     private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;  44     private static final int COLORDRAWABLE_DIMENSION = 2;  45 
 46     private static final int DEFAULT_BORDER_WIDTH = 0;  47     private static final int DEFAULT_BORDER_COLOR = Color.BLACK;  48     private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;  49     private static final boolean DEFAULT_BORDER_OVERLAY = false;  50 
 51     private final RectF mDrawableRect = new RectF();  52     private final RectF mBorderRect = new RectF();  53 
 54     //用来变换 mBitmapShader,从而影响 mBitmapPaint的显示效果
 55     private final Matrix mShaderMatrix = new Matrix();  56     private final Paint mBitmapPaint = new Paint();  57     private final Paint mBorderPaint = new Paint();  58     private final Paint mFillPaint = new Paint();  59 
 60     private int mBorderColor = DEFAULT_BORDER_COLOR;  61     private int mBorderWidth = DEFAULT_BORDER_WIDTH;  62     private int mFillColor = DEFAULT_FILL_COLOR;  63 
 64     private Bitmap mBitmap;  65     private BitmapShader mBitmapShader;  66     private int mBitmapWidth;  67     private int mBitmapHeight;  68 
 69     private float mDrawableRadius;  70     private float mBorderRadius;  71 
 72     private ColorFilter mColorFilter;//滤镜效果,用在中间的bitmap上
 73 
 74     private boolean mReady;  75     private boolean mSetupPending;  76     private boolean mBorderOverlay;  77 
 78     public CircleImageView(Context context) {  79         super(context);  80  init();  81  }  82 
 83     public CircleImageView(Context context, AttributeSet attrs) {  84         this(context, attrs, 0);  85  }  86 
 87     //构造方法,获取属性文件中的定义,没必要细说
 88     public CircleImageView(Context context, AttributeSet attrs, int defStyle) {  89         super(context, attrs, defStyle);  90 
 91         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);  92 
 93         mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);  94         mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);  95         mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);  96         mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);  97 
 98  a.recycle();  99 
100  init(); 101  } 102 
103     //初始化方法,主要是设置ScaleType,ScaleType被限定为center_crop模式
104     private void init() { 105         super.setScaleType(SCALE_TYPE); 106         mReady = true; 107 
108         if (mSetupPending) { 109  setup(); 110             mSetupPending = false; 111  } 112  } 113 
114  @Override 115     protected void onSizeChanged(int w, int h, int oldw, int oldh) { 116         super.onSizeChanged(w, h, oldw, oldh); 117         setup();//实现了refresh功能,当属性改变时,调用此方法,完成刷新界面的功能
118  } 119 
120  @Override 121     protected void onDraw(Canvas canvas) { 122         if (mBitmap == null) { 123             return; 124  } 125 
126         //底色 mFillColor --> 画笔 mFillPaint
127         if (mFillColor != Color.TRANSPARENT) { 128             canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint); 129  } 130 
131         canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint); 132 
133         //边色 mBorderColor --> 画笔 mBorderPaint
134         if (mBorderWidth != 0) { 135             canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint); 136  } 137  } 138 
139     private void setup() { 140         if (!mReady) { 141             mSetupPending = true; 142             return; 143  } 144 
145         if (getWidth() == 0 && getHeight() == 0) { 146             return; 147  } 148 
149         if (mBitmap == null) { 150  invalidate(); 151             return; 152  } 153 
154         mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 155 
156         mBitmapPaint.setAntiAlias(true); 157  mBitmapPaint.setShader(mBitmapShader); 158 
159  mBorderPaint.setStyle(Paint.Style.STROKE); 160         mBorderPaint.setAntiAlias(true); 161  mBorderPaint.setColor(mBorderColor); 162  mBorderPaint.setStrokeWidth(mBorderWidth); 163 
164  mFillPaint.setStyle(Paint.Style.FILL); 165         mFillPaint.setAntiAlias(true); 166  mFillPaint.setColor(mFillColor); 167 
168         mBitmapHeight = mBitmap.getHeight(); 169         mBitmapWidth = mBitmap.getWidth(); 170 
171         mBorderRect.set(0, 0, getWidth(), getHeight()); 172         mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f); 173 
174  mDrawableRect.set(mBorderRect); 175         if (!mBorderOverlay) { 176  mDrawableRect.inset(mBorderWidth, mBorderWidth); 177  } 178         mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f); 179 
180  updateShaderMatrix(); 181  invalidate(); 182  } 183 
184     private void updateShaderMatrix() { 185         float scale; 186         float dx = 0; 187         float dy = 0; 188 
189         mShaderMatrix.set(null); 190         //这里判断长宽比例,即图片的实际长宽比例与view的长宽比例 191         //若是图片更"扁",则缩放的尺寸按照二者的高度来定 192         //若是图片更"瘦",则缩放的尺寸按照二者的宽度来定
193         if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { 194             scale = mDrawableRect.height() / (float) mBitmapHeight; 195             dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; 196         } else { 197             scale = mDrawableRect.width() / (float) mBitmapWidth; 198             dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; 199  } 200 
201  mShaderMatrix.setScale(scale, scale); 202         mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top); 203         //能够简单的将BitmapShader类用来给Paint设置"填充颜色",这种说法其实并不许确,shader"填充"效果针对的范围是整个canvas, 204         //而paint显示的是这个paint实时画出来的部分。 205         //shader能够设置matrix,用来缩放或位移
206  mBitmapShader.setLocalMatrix(mShaderMatrix); 207  } 208 
209  @Override 210     public ScaleType getScaleType() { 211         return SCALE_TYPE; 212  } 213 
214  @Override 215     public void setScaleType(ScaleType scaleType) { 216         if (scaleType != SCALE_TYPE) { 217             throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType)); 218  } 219  } 220 
221  @Override 222     public void setAdjustViewBounds(boolean adjustViewBounds) { 223         if (adjustViewBounds) { 224             throw new IllegalArgumentException("adjustViewBounds not supported."); 225  } 226  } 227 
228     public int getBorderColor() { 229         return mBorderColor; 230  } 231 
232     public void setBorderColor(@ColorInt int borderColor) { 233         if (borderColor == mBorderColor) { 234             return; 235  } 236 
237         mBorderColor = borderColor; 238  mBorderPaint.setColor(mBorderColor); 239  invalidate(); 240  } 241 
242     public void setBorderColorResource(@ColorRes int borderColorRes) { 243  setBorderColor(getContext().getResources().getColor(borderColorRes)); 244  } 245 
246     public int getFillColor() { 247         return mFillColor; 248  } 249 
250     public void setFillColor(@ColorInt int fillColor) { 251         if (fillColor == mFillColor) { 252             return; 253  } 254 
255         mFillColor = fillColor; 256  mFillPaint.setColor(fillColor); 257  invalidate(); 258  } 259 
260     public void setFillColorResource(@ColorRes int fillColorRes) { 261  setFillColor(getContext().getResources().getColor(fillColorRes)); 262  } 263 
264     public int getBorderWidth() { 265         return mBorderWidth; 266  } 267 
268     public void setBorderWidth(int borderWidth) { 269         if (borderWidth == mBorderWidth) { 270             return; 271  } 272 
273         mBorderWidth = borderWidth; 274  setup(); 275  } 276 
277     public boolean isBorderOverlay() { 278         return mBorderOverlay; 279  } 280 
281     public void setBorderOverlay(boolean borderOverlay) { 282         if (borderOverlay == mBorderOverlay) { 283             return; 284  } 285 
286         mBorderOverlay = borderOverlay; 287  setup(); 288  } 289 
290  @Override 291     public void setImageBitmap(Bitmap bm) { 292         super.setImageBitmap(bm); 293         mBitmap = bm; 294  setup(); 295  } 296 
297  @Override 298     public void setImageDrawable(Drawable drawable) { 299         super.setImageDrawable(drawable); 300         mBitmap = getBitmapFromDrawable(drawable); 301  setup(); 302  } 303 
304  @Override 305     public void setImageResource(@DrawableRes int resId) { 306         super.setImageResource(resId); 307         mBitmap = getBitmapFromDrawable(getDrawable()); 308  setup(); 309  } 310 
311  @Override 312     public void setImageURI(Uri uri) { 313         super.setImageURI(uri); 314         mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null; 315  setup(); 316  } 317 
318  @Override 319     public void setColorFilter(ColorFilter cf) { 320         if (cf == mColorFilter) { 321             return; 322  } 323 
324         mColorFilter = cf; 325  mBitmapPaint.setColorFilter(mColorFilter); 326  invalidate(); 327  } 328 
329     private Bitmap getBitmapFromDrawable(Drawable drawable) { 330         if (drawable == null) { 331             return null; 332  } 333 
334         if (drawable instanceof BitmapDrawable) { 335             return ((BitmapDrawable) drawable).getBitmap(); 336  } 337 
338         try { 339  Bitmap bitmap; 340 
341             if (drawable instanceof ColorDrawable) { 342                 //COLORDRAWABLE_DIMENSION == 2,若是是ColorDrawable类型的,就取2x2大小
343                 bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG); 344             } else { 345                 bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG); 346  } 347 
348             Canvas canvas = new Canvas(bitmap); 349             drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 350  drawable.draw(canvas); 351             return bitmap; 352         } catch (Exception e) { 353  e.printStackTrace(); 354             return null; 355  } 356  } 357 
358 
359 
360 }