Android 圆形/圆角图片的方法

Android 圆形/圆角图片的方法

目前网上有很多圆角图片的实例,Github上也有一些成熟的项目。之前做项目,为了稳定高效都是选用Github上的项目直接用。但这种结束也是Android开发必备技能 ,所以今天就来简单研究一下该技术,分享给大家。

预备知识:

Xfermode介绍:

下面是Android ApiDemo里的“Xfermodes”实例,效果图。


Xfermode有三个子类,结构如下:

view source print ?
1. publicclass
2. Xfermode
3. extendsObject
4. java.lang.Object
5. ?    android.graphics.Xfermode
6. Known Direct Subclasses
7. AvoidXfermode, PixelXorXfermode, PorterDuffXfermode

AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素异或操作。

PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。


上面图片种显示的16种模式介绍如下:

1.PorterDuff.Mode.CLEAR

所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC

显示上层绘制图片
3.PorterDuff.Mode.DST

显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER

正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER

上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN

取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN

取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT

取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT

取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP

取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP

取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR

异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN

取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN

取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY

取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN

取两图层全部区域,交集部分变为透明色


了解了上面的知识点后,我们根据上面的知识点先来实现第一种圆角图片制作方式:

原图:


先看这一段代码

view source print ?
01. privateImageView mImg;
02.  
03. @Override
04. protectedvoid onCreate(Bundle savedInstanceState) {
05. super.onCreate(savedInstanceState);
06. setContentView(R.layout.activity_main);
07. mImg = (ImageView) findViewById(R.id.img);
08.  
09. //获得imageview中设置的图片
10. BitmapDrawable drawable = (BitmapDrawable) mImg.getDrawable();
11. Bitmap bmp = drawable.getBitmap();
12. //获得图片的宽,并创建结果bitmap
13. intwidth = bmp.getWidth();
14. Bitmap resultBmp = Bitmap.createBitmap(width, width,
15. Bitmap.Config.ARGB_8888);
16. Paint paint =new Paint();
17. Canvas canvas =new Canvas(resultBmp);
18. //画圆
19. canvas.drawCircle(width /2, width / 2, width /2, paint);
20. paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));// 选择交集去上层图片
21. canvas.drawBitmap(bmp,0, 0, paint);
22. mImg.setImageBitmap(resultBmp);
23. bmp.recycle();
24.  
25. }

通过运行上面的代码,我们得出的结果如下:


大家看到这是我们需要的结果。可是这样做可能导致OutOfMomery异常。假如图片很大或者你可能并非通过ImageView的getDrawable获得图像,而是直接Decode一张很大的图片加载到内存,你会发现可能会出现异常。我们做一下改变。

view source print ?
01. privatestatic final String TAG = "RoundImage";
02. privateImageView mImg;
03.  
04. @Override
05. protectedvoid onCreate(Bundle savedInstanceState) {
06. super.onCreate(savedInstanceState);
07. setContentView(R.layout.activity_main);
08. mImg = (ImageView) findViewById(R.id.img);
09. // 裁剪图片
10. BitmapFactory.Options options =new BitmapFactory.Options();
11. options.inJustDecodeBounds =true;
12. BitmapFactory
13. .decodeResource(getResources(), R.drawable.avatar, options);
14. Log.d(TAG,"original outwidth: " + options.outWidth);
15. // 此宽度是目标ImageView希望的大小,你可以自定义ImageView,然后获得ImageView的宽度。
16. intdstWidth = 150;
17. // 我们需要加载的图片可能很大,我们先对原有的图片进行裁剪
18. intsampleSize = calculateInSampleSize(options, dstWidth, dstWidth);
19. options.inSampleSize = sampleSize;
20. options.inJustDecodeBounds =false;
21. Log.d(TAG,"sample size: " + sampleSize);
22. Bitmap bmp = BitmapFactory.decodeResource(getResources(),
23. R.drawable.avatar, options);
24.  
25. // 绘制图片
26. Bitmap resultBmp = Bitmap.createBitmap(dstWidth, dstWidth,
27. Bitmap.Config.ARGB_8888);
28. Paint paint =new Paint();
29. paint.setAntiAlias(true);
30. Canvas canvas =new Canvas(resultBmp);
31. // 画圆
32. canvas.drawCircle(dstWidth /2, dstWidth / 2, dstWidth /2, paint);
33. // 选择交集去上层图片
34. paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));
35. canvas.drawBitmap(bmp,new Rect(0,0, bmp.getWidth(), bmp.getWidth()),
36. newRect(0,0, dstWidth, dstWidth), paint);
37. mImg.setImageBitmap(resultBmp);
38. bmp.recycle();
39. }
40.  
41. privateint calculateInSampleSize(BitmapFactory.Options options,
42. intreqWidth, int reqHeight) {
43. // Raw height and width of image
44. finalint height = options.outHeight;
45. finalint width = options.outWidth;
46. intinSampleSize = 1;
47.  
48. if(height > reqHeight || width > reqWidth) {
49.  
50. finalint halfHeight = height / 2;
51. finalint halfWidth = width / 2;
52.  
53. // Calculate the largest inSampleSize value that is a power of 2 and
54. // keeps both
55. // height and width larger than the requested height and width.
56. while((halfHeight / inSampleSize) > reqHeight
57. && (halfWidth / inSampleSize) > reqWidth) {
58. inSampleSize *=2;
59. }
60. }
61. returninSampleSize;
62. }

再来看一下效果:


上面提供了一种方式,更多细节,需要你自己去优化,下面介绍第二种绘制圆角图片的方式。

首先我们需要了解一个类BitmapShader

引用的介绍如下:

public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)

调用这个方法来产生一个画有一个位图的渲染器(Shader)。

bitmap 在渲染器内使用的位图

tileX The tiling mode for x to draw the bitmap in. 在位图上X方向花砖模式

tileY The tiling mode for y to draw the bitmap in. 在位图上Y方向花砖模式

TileMode:(一共有三种)

CLAMP :如果渲染器超出原始边界范围,会复制范围内边缘染色。

REPEAT :横向和纵向的重复渲染器图片,平铺。

MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT 重复方式不一样,他是以镜像方式平铺。

知道这个原理后,我们贴出对应的代码:
view source print ?
01. publicclass CircleImageView extendsImageView {
02.  
03. privatestatic final String TAG = CircleImageView.class.getSimpleName();
04. privatePaint mBitmapPaint = new Paint();
05. privateint mRadius;
06.  
07. publicCircleImageView(Context context, AttributeSet attrs, intdefStyleAttr) {
08. super(context, attrs, defStyleAttr);
09. init();
10. }
11.  
12. publicCircleImageView(Context context, AttributeSet attrs) {
13. super(context, attrs);
14. init();
15. }
16.  
17. publicCircleImageView(Context context) {
18. super(context);
19. init();
20. }
21.  
22. privatevoid init() {
23. BitmapDrawable drawable = (BitmapDrawable) getDrawable();
24. if(drawable == null) {
25. Log.i(TAG,"drawable: null");
26. return;
27. }
28. Bitmap bmp = drawable.getBitmap();
29. BitmapShader shader =new BitmapShader(bmp, TileMode.CLAMP,
30. TileMode.CLAMP);
31. mBitmapPaint.setShader(shader);
32. mBitmapPaint.setAntiAlias(true);
33. invalidate();
34. }
35.  
36. @Override
37. protectedvoid onDraw(Canvas canvas) {
38. if(getDrawable() == null) {
39. return;
40. }
41. mRadius = Math.min(getWidth()/2, getHeight()/2);
42. canvas.drawCircle(getWidth() /2, getHeight() / 2, mRadius,
43. mBitmapPaint);
44. }
45.  
46. }

是不是挺简单的

结果我就不显示了,跟上面的一样。上面也是最原始的代码,文章的结尾贴出一份完整优化过的代码共大家参考如下:

view source print ?
001. publicclass CircleImageView extendsImageView {
002.  
003. privatestatic final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
004.  
005. privatestatic final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
006. privatestatic final int COLORDRAWABLE_DIMENSION = 1;
007.  
008. privatestatic final int DEFAULT_BORDER_WIDTH = 0;
009. privatestatic final int DEFAULT_BORDER_COLOR = Color.BLACK;
010.  
011. privatefinal RectF mDrawableRect = new RectF();
012. privatefinal RectF mBorderRect = new RectF();
013.  
014. privatefinal Matrix mShaderMatrix = new Matrix();
015. privatefinal Paint mBitmapPaint = new Paint();
016. privatefinal Paint mBorderPaint = new Paint();
017.  
018. privateint mBorderColor = DEFAULT_BORDER_COLOR;
019. privateint mBorderWidth = DEFAULT_BORDER_WIDTH;
020.  
021. privateBitmap mBitmap;
022. privateBitmapShader mBitmapShader;
023. privateint mBitmapWidth;
024. privateint mBitmapHeight;
025.  
026. privatefloat mDrawableRadius;
027. privatefloat mBorderRadius;
028.  
029. privateboolean mReady;
030. privateboolean mSetupPending;
031.  
032. publicCircleImageView(Context context) {
033. super(context);
034.  
035. init();
036. }
037.  
038. publicCircleImageView(Context context, AttributeSet attrs) {
039. this(context, attrs,0);
040. }
041.  
042. publicCircleImageView(Context context, AttributeSet attrs, intdefStyle) {
043. super(context, attrs, defStyle);
044.  
045. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle,0);
046.  
047. mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
048. mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
049.  
050. a.recycle();
051.  
052. init();
053. }
054.  
055. privatevoid init() {
056. super.setScaleType(SCALE_TYPE);
057. mReady =true;
058.  
059. if(mSetupPending) {
060. setup();
061. mSetupPending =false;
062. }
063. }
064.  
065. @Override
066. publicScaleType getScaleType() {
067. returnSCALE_TYPE;
068. }
069.  
070. @Override
071. publicvoid setScaleType(ScaleType scaleType) {
072. if(scaleType != SCALE_TYPE) {
073. thrownew IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
074. }
075. }
076.  
077. @Override
078. protectedvoid onDraw(Canvas canvas) {
079. if(getDrawable() == null) {
080. return;
081. }
082.  
083. canvas.drawCircle(getWidth() /2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
084. if(mBorderWidth != 0) {
085. canvas.drawCircle(getWidth() /2, getHeight() / 2, mBorderRadius, mBorderPaint);
086. }
087. }
088.  
089. @Override
090. protectedvoid onSizeChanged(intw, int h, int oldw, intoldh) {
091. super.onSizeChanged(w, h, oldw, oldh);
092. setup();
093. }
094.  
095. publicint getBorderColor() {
096. returnmBorderColor;
097. }
098.  
099. publicvoid setBorderColor(intborderColor) {
100. if(borderColor == mBorderColor) {
101. return;
102. }
103.  
104. mBorderColor = borderColor;
105. mBorderPaint.setColor(mBorderColor);
106. invalidate();
107. }
108.  
109. publicint getBorderWidth() {
110. returnmBorderWidth;
111. }
112.  
113. publicvoid setBorderWidth(intborderWidth) {
114. if(borderWidth == mBorderWidth) {
115. return;
116. }
117.  
118. mBorderWidth = borderWidth;
119. setup();
120. }
121.  
122. @Override
123. publicvoid setImageBitmap(Bitmap bm) {
124. super.setImageBitmap(bm);
125. mBitmap = bm;
126. setup();
127. }
128.  
129. @Override
130. publicvoid setImageDrawable(Drawable drawable) {
131. super.setImageDrawable(drawable);
132. mBitmap = getBitmapFromDrawable(drawable);
133. setup();
134. }
135.  
136. @Override
137. publicvoid setImageResource(intresId) {
138. super.setImageResource(resId);
139. mBitmap = getBitmapFromDrawable(getDrawable());
140. setup();
141. }
142.  
143. @Override
144. publicvoid setImageURI(Uri uri) {
145. super.setImageURI(uri);
146. mBitmap = getBitmapFromDrawable(getDrawable());
147. setup();
148. }
149.  
150. privateBitmap getBitmapFromDrawable(Drawable drawable) {
151. if(drawable == null) {
152. returnnull;
153. }
154.  
155. if(drawable instanceof BitmapDrawable) {
156. return((BitmapDrawable) drawable).getBitmap();
157. }
158.  
159. try{
160. Bitmap bitmap;
161.  
162. if(drawable instanceof ColorDrawable) {
163. bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
164. }else {
165. bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
166. }
167.  
168. Canvas canvas =new Canvas(bitmap);
169. drawable.setBounds(0,0, canvas.getWidth(), canvas.getHeight());
170. drawable.draw(canvas);
171. returnbitmap;
172. }catch (OutOfMemoryError e) {
173. returnnull;
174. }
175. }
176.  
177. privatevoid setup() {
178. if(!mReady) {
179. mSetupPending =true;
180. return;
181. }
182.  
183. if(mBitmap == null) {
184. return;
185. }
186.  
187. mBitmapShader =new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
188.  
189. mBitmapPaint.setAntiAlias(true);
190. mBitmapPaint.setShader(mBitmapShader);
191.  
192. mBorderPaint.setStyle(Paint.Style.STROKE);
193. mBorderPaint.setAntiAlias(true);
194. mBorderPaint.setColor(mBorderColor);
195. mBorderPaint.setStrokeWidth(mBorderWidth);
196.  
197. mBitmapHeight = mBitmap.getHeight();
198. mBitmapWidth = mBitmap.getWidth();
199.  
200. mBorderRect.set(0,0, getWidth(), getHeight());
201. mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) /2, (mBorderRect.width() - mBorderWidth) /2);
202.  
203. mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
204. mDrawableRadius = Math.min(mDrawableRect.height() /2, mDrawableRect.width() / 2);
205.  
206. updateShaderMatrix();
207. invalidate();
208. }
209.  
210. privatevoid updateShaderMatrix() {
211. floatscale;
212. floatdx = 0;
213. floatdy = 0;
214.  
215. mShaderMatrix.set(null);
216.  
217. if(mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
218. scale = mDrawableRect.height() / (float) mBitmapHeight;
219. dx = (mDrawableRect.width() - mBitmapWidth * scale) *0.5f;
220. }else {
221. scale = mDrawableRect.width() / (float) mBitmapWidth;
222. dy = (mDrawableRect.height() - mBitmapHeight * scale) *0.5f;
223. }
224.  
225. mShaderMatrix.setScale(scale, scale);
226. mShaderMatrix.postTranslate((int) (dx +0.5f) + mBorderWidth, (int) (dy +0.5f) + mBorderWidth);
227.  
228. mBitmapShader.setLocalMatrix(mShaderMatrix);
229. }
230.  
231. }

你可能感兴趣的:(android,UI,canvas,bitmap,位图)