Android 圆形/圆角图片的方法

Android 圆形/圆角图片的方法

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

预备知识:

Xfermode介绍:

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

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

view sourceprint?

1.public
class

2.Xfermode

3.extends
Object

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 sourceprint?

01.private
ImageView mImg;

02.

03.@Override

04.protected
void 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.int
width = 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(new
PorterDuffXfermode(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 sourceprint?

01.private
static final
String TAG =
"RoundImage";

02.private
ImageView mImg;

03.

04.@Override

05.protected
void 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.int
dstWidth = 150;

17.// 我们需要加载的图片可能很大,我们先对原有的图片进行裁剪

18.int
sampleSize = 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(new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

35.canvas.drawBitmap(bmp,
new Rect(0,
0, bmp.getWidth(), bmp.getWidth()),

36.new
Rect(0,
0, dstWidth, dstWidth), paint);

37.mImg.setImageBitmap(resultBmp);

38.bmp.recycle();

39.}

40.

41.private
int calculateInSampleSize(BitmapFactory.Options options,

42.int
reqWidth, int
reqHeight) {

43.// Raw height and width of image

44.final
int height = options.outHeight;

45.final
int width = options.outWidth;

46.int
inSampleSize = 1;

47.

48.if
(height > reqHeight || width > reqWidth) {

49.

50.final
int halfHeight = height /
2;

51.final
int 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.return
inSampleSize;

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 sourceprint?

01.public
class CircleImageView extends
ImageView {

02.

03.private
static final
String TAG = CircleImageView.
class.getSimpleName();

04.private
Paint mBitmapPaint = new
Paint();

05.private
int mRadius;

06.

07.public
CircleImageView(Context context, AttributeSet attrs, int
defStyleAttr) {

08.super(context, attrs, defStyleAttr);

09.init();

10.}

11.

12.public
CircleImageView(Context context, AttributeSet attrs) {

13.super(context, attrs);

14.init();

15.}

16.

17.public
CircleImageView(Context context) {

18.super(context);

19.init();

20.}

21.

22.private
void 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.protected
void 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 sourceprint?

001.public
class CircleImageView extends
ImageView {

002.

003.private
static final
ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

004.

005.private
static final
Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;

006.private
static final
int
COLORDRAWABLE_DIMENSION = 1;

007.

008.private
static final
int
DEFAULT_BORDER_WIDTH = 0;

009.private
static final
int
DEFAULT_BORDER_COLOR = Color.BLACK;

010.

011.private
final RectF mDrawableRect =
new RectF();

012.private
final RectF mBorderRect =
new RectF();

013.

014.private
final Matrix mShaderMatrix =
new Matrix();

015.private
final Paint mBitmapPaint =
new Paint();

016.private
final Paint mBorderPaint =
new Paint();

017.

018.private
int mBorderColor = DEFAULT_BORDER_COLOR;

019.private
int mBorderWidth = DEFAULT_BORDER_WIDTH;

020.

021.private
Bitmap mBitmap;

022.private
BitmapShader mBitmapShader;

023.private
int mBitmapWidth;

024.private
int mBitmapHeight;

025.

026.private
float mDrawableRadius;

027.private
float mBorderRadius;

028.

029.private
boolean mReady;

030.private
boolean mSetupPending;

031.

032.public
CircleImageView(Context context) {

033.super(context);

034.

035.init();

036.}

037.

038.public
CircleImageView(Context context, AttributeSet attrs) {

039.this(context, attrs,
0);

040.}

041.

042.public
CircleImageView(Context context, AttributeSet attrs, int
defStyle) {

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.private
void init() {

056.super.setScaleType(SCALE_TYPE);

057.mReady =
true;

058.

059.if
(mSetupPending) {

060.setup();

061.mSetupPending =
false;

062.}

063.}

064.

065.@Override

066.public
ScaleType getScaleType() {

067.return
SCALE_TYPE;

068.}

069.

070.@Override

071.public
void setScaleType(ScaleType scaleType) {

072.if
(scaleType != SCALE_TYPE) {

073.throw
new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));

074.}

075.}

076.

077.@Override

078.protected
void 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.protected
void onSizeChanged(int
w, int
h,
int oldw, int
oldh) {

091.super.onSizeChanged(w, h, oldw, oldh);

092.setup();

093.}

094.

095.public
int getBorderColor() {

096.return
mBorderColor;

097.}

098.

099.public
void setBorderColor(int
borderColor) {

100.if
(borderColor == mBorderColor) {

101.return;

102.}

103.

104.mBorderColor = borderColor;

105.mBorderPaint.setColor(mBorderColor);

106.invalidate();

107.}

108.

109.public
int getBorderWidth() {

110.return
mBorderWidth;

111.}

112.

113.public
void setBorderWidth(int
borderWidth) {

114.if
(borderWidth == mBorderWidth) {

115.return;

116.}

117.

118.mBorderWidth = borderWidth;

119.setup();

120.}

121.

122.@Override

123.public
void setImageBitmap(Bitmap bm) {

124.super.setImageBitmap(bm);

125.mBitmap = bm;

126.setup();

127.}

128.

129.@Override

130.public
void setImageDrawable(Drawable drawable) {

131.super.setImageDrawable(drawable);

132.mBitmap = getBitmapFromDrawable(drawable);

133.setup();

134.}

135.

136.@Override

137.public
void setImageResource(int
resId) {

138.super.setImageResource(resId);

139.mBitmap = getBitmapFromDrawable(getDrawable());

140.setup();

141.}

142.

143.@Override

144.public
void setImageURI(Uri uri) {

145.super.setImageURI(uri);

146.mBitmap = getBitmapFromDrawable(getDrawable());

147.setup();

148.}

149.

150.private
Bitmap getBitmapFromDrawable(Drawable drawable) {

151.if
(drawable == null) {

152.return
null;

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.return
bitmap;

172.}
catch (OutOfMemoryError e) {

173.return
null;

174.}

175.}

176.

177.private
void 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.private
void updateShaderMatrix() {

211.float
scale;

212.float
dx = 0;

213.float
dy = 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.}

时间: 2024-08-01 18:43:26

Android 圆形/圆角图片的方法的相关文章

Android 两种制作圆形/圆角图片的方法

前言: 目前网上有很多圆角图片的实例,Github上也有一些成熟的项目.之前做项目,为了稳定高效都是选用Github上的项目直接用.但这种结束也是Android开发必备技能 ,所以今天就来简单研究一下该技术,分享给大家. 预备知识: Xfermode介绍: 下面是Android ApiDemo里的"Xfermodes"实例,效果图. Xfermode有三个子类,结构如下: public class Xfermode extends Object java.lang.Object ? a

圆形 / 圆角图片工具类——CircleImageManager

这个类可以实现圆角,或者是圆形图片的操作. CircleImageManager.java package com.kale.utils; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import

Android不错的图片压缩方法

Android不错的图片压缩方法 一.图片质量压缩 /** * 质量压缩方法 * * @param image * @return */ public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不

Android BitmapShader 实战 实现圆形、圆角图片

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41967509,本文出自:[张鸿洋的博客] 1.概述 记得初学那会写过一篇博客Android 完美实现图片圆角和圆形(对实现进行分析),主要是个自定View加上使用Xfermode实现的.其实实现圆角图片的方法应该很多,常见的就是利用Xfermode,Shader.本篇博客会直接继承直接继承ImageView,使用BitmapShader实现圆角的绘制,大家如果耐着性子看完,我估

AndroidBitmapShader实战实现圆形、圆角图片(转载)

AndroidBitmapShader实战实现圆形.圆角图片 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41967509,本文出自:[张鸿洋的博客] 1.概述 记得初学那会写过一篇博客Android 完美实现图片圆角和圆形(对实现进行分析),主要是个自定View加上使用Xfermode实现的.其实实现圆角图片的方法应该很多,常见的就是利用Xfermode,Shader.本篇博客会直接继承直接继承ImageView,使用Bi

Android_BitmapShader实现圆形、圆角图片

转:http://blog.csdn.net/lmj623565791/article/details/41967509,本文出自:[张鸿洋的博客] 1.概述 记得初学那会写过一篇博客Android 完美实现图片圆角和圆形(对实现进行分析),主要是个自定View加上使用Xfermode实现的.其实实现圆角图片的方法应该很多,常见的就是利用Xfermode,Shader.本篇博客会直接继承直接继承ImageView,使用BitmapShader实现圆角的绘制,大家如果耐着性子看完,我估计什么形状都

android图片处理方法(不断收集中)

//压缩图片大小 public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int options = 100; while ( baos.toByteArra

android图片处理方法(转)

//压缩图片大小 public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int options = 100; while ( baos.toByteArra

Android 图片处理方法

//压缩图片大小 public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int options = 100; while ( baos.toByteArra