Android 圆形/圆角图片的方法
眼下网上有非常多圆角图片的实例,Github上也有一些成熟的项目。之前做项目,为了稳定高效都是选用Github上的项目直接用。但这样的结束也是Android开发必备技能 。所以今天就来简单研究一下该技术,分享给大家。
预备知识:
Xfermode介绍:
以下是Android ApiDemo里的“Xfermodes”实例,效果图。
Xfermode有三个子类。结构例如以下:
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
取两图层所有区域,交集部分变为透明色
了解了上面的知识点后,我们依据上面的知识点先来实现第一种圆角图片制作方式:
原图:
先看这一段代码
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一张非常大的图片载入到内存,你会发现可能会出现异常。我们做一下改变。
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 反复方式不一样,他是以镜像方式平铺。
知道这个原理后,我们贴出相应的代码:
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.
}
是不是挺简单的
结果我就不显示了,跟上面的一样。
上面也是最原始的代码,文章的结尾贴出一份完整优化过的代码共大家參考例如以下:
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.
}
Android 圆形/圆角图片的方法的更多相关文章
-
安卓图片载入之使用universalimageloader载入圆形圆角图片
前言 话说这universalimageloader载入图片对搞过2年安卓程序都是用烂了再熟悉只是了.就是安卓新手也是百度就会有一大堆东西出来,今天为什么这里还要讲使用universalimagelo ...
-
Android实现圆形圆角图片
本文主要使用两种方法实现图形圆角图片 自定View加上使用Xfermode实现 Shader实现 自定View加上使用Xfermode实现 /** * 根据原图和变长绘制圆形图片 * * @param ...
-
Android 简单的图片缩放方法
很简单的一个图片缩放方法,注意要比例设置正确否则可能会内存溢出 相关问题 java.lang.IllegalArgumentException: bitmap size exceeds 32bits ...
-
CircleImageManager——圆形 / 圆角图片的工具类
这个类可以实现圆角,或者是圆形图片的操作. CircleImageManager.java package com.kale.utils; import android.content.Context ...
-
Android 获得各处图片的方法
<pre name="code" class="java">//1,已将图片保存到drawable目录下 //通过图片id获得Drawable Re ...
-
Android BitmapShader 实战 实现圆形、圆角图片
转载自:http://blog.csdn.net/lmj623565791/article/details/41967509 1.概述 记得初学那会写过一篇博客Android 完美实现图片圆角和圆形( ...
-
【转】Android BitmapShader 实战 实现圆形、圆角图片
转载自:http://blog.csdn.net/lmj623565791/article/details/41967509 1.概述 记得初学那会写过一篇博客Android 完美实现图片圆角和圆形( ...
-
Android_BitmapShader实现圆形、圆角图片
转:http://blog.csdn.net/lmj623565791/article/details/41967509,本文出自:[张鸿洋的博客] 1.概述 记得初学那会写过一篇博客Android ...
-
android图片处理方法
Java代码 //压缩图片大小 public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ...
随机推荐
-
reactjs 注意点
render的return return前要留一空行 return的括号要分别各占一行,不能与html同行 return中的html必须要有顶层容器包裹 return中的循环不能用for,改用map方 ...
-
NetBeansRCP-添加/修改NetBeans的JVM启动参数
NetBeans运行的速度实在是不敢恭维.还好机器配置还可以,修改其JVM启动参数命令行,以期运行的更加顺畅. 那么如何修改NetBeans IDE的JVM参数呢? 1.到NetBeans IDE的安 ...
-
c#中实现登陆窗口(无需隐藏)
C#登录窗口的实现,特点就是不用隐藏. 在入口处打开登陆: static void Main() { Application.EnableVisualStyles(); Application.Set ...
-
[CQOI2016]伪光滑数
题目描述 若一个大于1的整数M的质因数分解有k项,其最大的质因子为Ak,并且满足Ak^K<=N,Ak<128,我们就称整数M为N-伪 光滑数.现在给出N,求所有整数中,第K大的N-伪光滑数 ...
-
es6 模板字符串
模板字符串 提供构造字符串的语法糖,在 Prel/python 等语言中也都有类似特性. 1.反引号模板,可以换行 2.反引号模板,可以嵌套 用+``来嵌套 好处:语法更加简洁 var name=&q ...
-
linux安装jdk8
1.文件准备 jdk-8u201-linux-x64.tar.gz 下载地址 http://www.oracle.com/technetwork/java/javase/downloads/jdk8- ...
-
[PHP] 看博客学习插入排序
定义数组长度变量$len,使用count()函数,参数:数组 for循环数组,条件:从第二个开始,遍历数组,循环内 定义临时变量$temp,赋值当前元素 for循环数组,条件:遍历当前元素前面的所有元 ...
-
Python 中函数的 收集参数 机制
定义函数的时候,在参数前加了一个 * 号,函数可以接收零个或多个值作为参数.返回结果是一个元组. 传递零个参数时函数并不报错,而是返回一个空元组.但以上这种方法也有局限性,它不能收集关键字参数. 对关 ...
-
HDU 2895 贪心 还是 大水题
DESCRIPTION:大意是给你两个字符串.编辑距离只有add和delete会产生.所以.编辑距离最短一定是两个字符串的长度差.然后...呵呵呵呵.... 猜题意就可以了...但是...我觉得这个题 ...
-
openstack kilo部署-基础环境
公司也想搞个私有云玩玩,于是展开了一系列的调研,部署测试,openstack 有几个版本真是坑爹!!,如果喜欢被虐有兄弟,你就试试 openstack 的 juno , icehouse等版本,用不了 ...