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

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

解析说明都在代码里了。

  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 }

 

posted @ 2016-03-15 19:13  carbs  阅读(1204)  评论(0编辑  收藏  举报