[原创]android开源项目源码解析(一)----CircleImageView的源码解析
CircleImageView的代码很简洁,因此先将此工程作为源码解析系列的第一篇文章.
解析说明都在代码里了。
/*
* Copyright 2014 - 2015 Henning Dodenhof
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hdodenhof.circleimageview; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
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.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.util.AttributeSet;
import android.widget.ImageView; public class CircleImageView extends ImageView { private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;//这里限制了只要一种模式:CENTER_CROP private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2; private static final int DEFAULT_BORDER_WIDTH = 0;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
private static final boolean DEFAULT_BORDER_OVERLAY = false; private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF(); //用来变换 mBitmapShader,从而影响 mBitmapPaint的显示效果
private final Matrix mShaderMatrix = new Matrix();
private final Paint mBitmapPaint = new Paint();
private final Paint mBorderPaint = new Paint();
private final Paint mFillPaint = new Paint(); private int mBorderColor = DEFAULT_BORDER_COLOR;
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
private int mFillColor = DEFAULT_FILL_COLOR; private Bitmap mBitmap;
private BitmapShader mBitmapShader;
private int mBitmapWidth;
private int mBitmapHeight; private float mDrawableRadius;
private float mBorderRadius; private ColorFilter mColorFilter;//滤镜效果,用在中间的bitmap上 private boolean mReady;
private boolean mSetupPending;
private boolean mBorderOverlay; public CircleImageView(Context context) {
super(context);
init();
} public CircleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} //构造方法,获取属性文件中的定义,不必细说
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0); mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR); a.recycle(); init();
} //初始化方法,主要是设置ScaleType,ScaleType被限定为center_crop模式
private void init() {
super.setScaleType(SCALE_TYPE);
mReady = true; if (mSetupPending) {
setup();
mSetupPending = false;
}
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setup();//实现了refresh功能,当属性改变时,调用此方法,完成刷新界面的功能
} @Override
protected void onDraw(Canvas canvas) {
if (mBitmap == null) {
return;
} //底色 mFillColor --> 画笔 mFillPaint
if (mFillColor != Color.TRANSPARENT) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
} canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint); //边色 mBorderColor --> 画笔 mBorderPaint
if (mBorderWidth != 0) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
}
} private void setup() {
if (!mReady) {
mSetupPending = true;
return;
} if (getWidth() == 0 && getHeight() == 0) {
return;
} if (mBitmap == null) {
invalidate();
return;
} mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader); mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth); mFillPaint.setStyle(Paint.Style.FILL);
mFillPaint.setAntiAlias(true);
mFillPaint.setColor(mFillColor); mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth(); mBorderRect.set(0, 0, getWidth(), getHeight());
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f); mDrawableRect.set(mBorderRect);
if (!mBorderOverlay) {
mDrawableRect.inset(mBorderWidth, mBorderWidth);
}
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f); updateShaderMatrix();
invalidate();
} private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0; mShaderMatrix.set(null);
//这里判断长宽比例,即图片的实际长宽比例与view的长宽比例
//如果图片更"扁",则缩放的尺寸按照两者的高度来定
//如果图片更"瘦",则缩放的尺寸按照两者的宽度来定
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
} else {
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
} mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
//可以简单的将BitmapShader类用来给Paint设置"填充颜色",这种说法其实并不准确,shader"填充"效果针对的范围是整个canvas,
//而paint显示的是这个paint实时画出来的部分。
//shader可以设置matrix,用来缩放或位移
mBitmapShader.setLocalMatrix(mShaderMatrix);
} @Override
public ScaleType getScaleType() {
return SCALE_TYPE;
} @Override
public void setScaleType(ScaleType scaleType) {
if (scaleType != SCALE_TYPE) {
throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
}
} @Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (adjustViewBounds) {
throw new IllegalArgumentException("adjustViewBounds not supported.");
}
} public int getBorderColor() {
return mBorderColor;
} public void setBorderColor(@ColorInt int borderColor) {
if (borderColor == mBorderColor) {
return;
} mBorderColor = borderColor;
mBorderPaint.setColor(mBorderColor);
invalidate();
} public void setBorderColorResource(@ColorRes int borderColorRes) {
setBorderColor(getContext().getResources().getColor(borderColorRes));
} public int getFillColor() {
return mFillColor;
} public void setFillColor(@ColorInt int fillColor) {
if (fillColor == mFillColor) {
return;
} mFillColor = fillColor;
mFillPaint.setColor(fillColor);
invalidate();
} public void setFillColorResource(@ColorRes int fillColorRes) {
setFillColor(getContext().getResources().getColor(fillColorRes));
} public int getBorderWidth() {
return mBorderWidth;
} public void setBorderWidth(int borderWidth) {
if (borderWidth == mBorderWidth) {
return;
} mBorderWidth = borderWidth;
setup();
} public boolean isBorderOverlay() {
return mBorderOverlay;
} public void setBorderOverlay(boolean borderOverlay) {
if (borderOverlay == mBorderOverlay) {
return;
} mBorderOverlay = borderOverlay;
setup();
} @Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = bm;
setup();
} @Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = getBitmapFromDrawable(drawable);
setup();
} @Override
public void setImageResource(@DrawableRes int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
} @Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
setup();
} @Override
public void setColorFilter(ColorFilter cf) {
if (cf == mColorFilter) {
return;
} mColorFilter = cf;
mBitmapPaint.setColorFilter(mColorFilter);
invalidate();
} private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
} if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} try {
Bitmap bitmap; if (drawable instanceof ColorDrawable) {
//COLORDRAWABLE_DIMENSION == 2,如果是ColorDrawable类型的,就取2x2大小
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
} Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
} }
[原创]android开源项目源码解析(一)----CircleImageView的源码解析的更多相关文章
- Android 开源项目源码解析(第二期)
Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations ...
- GitHub上史上最全的Android开源项目分类汇总 (转)
GitHub上史上最全的Android开源项目分类汇总 标签: github android 开源 | 发表时间:2014-11-23 23:00 | 作者:u013149325 分享到: 出处:ht ...
- github上有android开源项目
下面是一些比较好的开源项目,总共分为5大类,也许对某一些人有用,有些项目也许将来某一天对自己也有用,所以整理到此,希望对大家有帮助.首先声明非原创,这篇blog的目的是分享给那些可能需要的人. htt ...
- Android开源项目总结
Android开源项目--分类汇总 Android开源项目第一篇--个性化控件(View)篇 包含ListView.ActionBar.Menu.ViewPager.Gallery.GridView. ...
- 【Android 应用开发】GitHub 优秀的 Android 开源项目
原文地址为http://www.trinea.cn/android/android-open-source-projects-view/,作者Trinea 主要介绍那些不错个性化的View,包括Lis ...
- Android 开源项目及其学习
Android 系统研究:http://blog.csdn.net/luoshengyang/article/details/8923485 Android 腾讯技术人员博客 http://hukai ...
- 2015-2016最火的Android开源项目--github开源项目集锦(不看你就out了)
标签: Android开发开源项目最火Android项目github 2015-2016最火的Android开源项目 本文整理与集结了近期github上使用最广泛最火热与最流行的开源项目,想要充电与提 ...
- Android开源项目分类汇总
目前包括: Android开源项目第一篇——个性化控件(View)篇 包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView. ...
- fir.im Weekly - 1000 个 Android 开源项目集合
冬天到了,适宜囤点代码暖暖身.本期 fir.im Weekly 收集了最近一些不错的 GitHub 源码.开发工具和技术实践教程类文章分享给大家. codeKK - 集合近 1000 Android ...
随机推荐
- [Orchard] 通过指定RouteData设置Pager的链接地址
Orchard 中的Pager是一个很方便的用来分页的Shape, 但默认情况下,它是使用当前Action的地址作为链接地址,如果分页的数据要是由别的Action提供时,这样的分页链接就不对了,其实它 ...
- 从C#到Objective-C
Objective-C 程序设计语言采用特定的语法,来定义类和方法.调用对象的方法.动态地扩展类,以及创建编程接口,来解决具体问题.Objective-C 作为 C 程序设计语言的超集,支持与 C 相 ...
- Jquery最全过滤器总结
不管什么时候,总是有这么些时候:当我们使用jQuery的各种过滤器时,总是有那么几个记不牢,还要搜索一下或者翻翻手册!多少次想总结一下,最终都没总结,现在网上找到一篇总结的不错的,但是排版有点乱,本人 ...
- MongoDB 基础命令使用学习记录
1. 启动 mongod 几个常用命令说明:--dbpath : 指定数据库相关文件的存储目录 --logpath: 指定日志文件的存储目录 --port: 指定数据库的端口,默认是 27017 -- ...
- C#与数据库访问技术总结(六)之Command对象创建SQl语句代码示例
Command对象创建SQl语句代码示例 说明:前面介绍了 Command 对象的方法和一些属性,回顾一下 Command对象主要用来执行SQL语句.利用Command对象,可以查询数据和修改数据. ...
- Redis教程(七):Key操作命令详解
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/134.html?1455807040 一.概述: 在该系列的前几篇博客中, ...
- 转载: Qt 学习之路 2归档
Qt 学习之路 2归档 http://www.devbean.net/2012/08/qt-study-road-2-catelog/
- paip.元数据驱动的转换-读取文件行到个list理念 uapi java php python总结
paip.元数据驱动的转换-读取文件行到个list理念 uapi java php python总结 #两个思路 1.思路如下:使用file_get_contents()获取txt文件的内容,然后通过 ...
- 关于EntityFramework连接Oracle的详细教程
研发环境需安装的组件 Oracle Developer Tools for Visual Studio 2015 - MSI Installer 来自Oracle官网. 为Visual Studio ...
- php对csv文件的读取,写入,输出下载操作
在实际工作中,很多时候需要把网站上的一些数据下载到CSV文件里,方便以后查看. 亦或者是用CSV进行一些批量的上传工作. 这个时候我们就需要对CSV进行读写操作. 1.CSV的读取操作 <?ph ...