之前说过会将项目中运用的东西抽离出来做一个总结,今天我主要想总结一下圆角和圆形头像问题。由于我们的应用涉及到很多用户头像,如果所有的图像都是方方正正的话,那显得不是很美观,所以设计湿强行要我将头像圆角化处理。好吧,so easy。项目截图我就不想贴了,还是一贯贴demo截图吧,如下:
看着效果还行啊,下面我们就讲讲怎么去实现它。
自定义view,经常会涉及到view的属性问题,很简单,按以下操作你就能轻松实现自定义属性了。首先你得在资源文件里的values目录下,新建一个文件attrs,声明你想自定义的属性。
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.customParams);
isCircle = a.getBoolean(R.styleable.customParams_isCircle, false);
isRoundCorner = a.getBoolean(R.styleable.customParams_isRoundCorner, false);
corner_radius = a.getDimension(R.styleable.customParams_corner_radius, 0);
// 回收TypedArray,以便后面重用
a.recycle();
}
}
这里有人会TypedArray为啥要recycle呢,说实话我也纳闷了,特地去查了一下,官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了。TypedArray 内部持有部分数组,它们缓存在Resources类中的静态字段中,这样就不用每次使用前都需要分配内存。你可以看看TypedArray.recycle()中的代码:
/**
* Give back a previously retrieved StyledAttributes, for later re-use.
*/
public void recycle() {
synchronized (mResources.mTmpValue) {
TypedArray cached = mResources.mCachedStyledAttributes;
if (cached == null || cached.mData.length < mData.length) {
mXml = null;
mResources.mCachedStyledAttributes = this;
}
}
}
想了解更多的话,可以点击这里去看看哦
这是本篇文章的重点,自定义View你必须去重写onDraw(Canvas canvas),在其中去实现你所需要的逻辑,代码如下:
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (defaultWidth == 0) {
defaultWidth = getWidth();
}
if (defaultHeight == 0) {
defaultHeight = getHeight();
}
if (isCircle || isRoundCorner) {// 圆形或圆角
int radius = 0;
radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2;
Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight / 2 - radius, null);
} else {
super.onDraw(canvas);
}
}
代码比较简单,我就不一一去介绍了,当布局文件该view被声明成了圆角或圆形view时,则重新画图。这里有个最关键的方法getCroppedRoundBitmap,需要注释下。
/**
* 获取裁剪后的圆形图片
*
* @param bmp
* 原图
* @param radius
* 半径
*/
public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {
Bitmap scaledSrcBmp;
int diameter = radius * 2;
// 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片
int bmpWidth = bmp.getWidth();
int bmpHeight = bmp.getHeight();
int squareWidth = 0, squareHeight = 0;
int x = 0, y = 0;
Bitmap squareBitmap;
if (bmpHeight > bmpWidth) {// 高大于宽
squareWidth = squareHeight = bmpWidth;
x = 0;
y = (bmpHeight - bmpWidth) / 2;
// 截取正方形图片
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth, squareHeight);
} else if (bmpHeight < bmpWidth) {// 宽大于高
squareWidth = squareHeight = bmpHeight;
x = (bmpWidth - bmpHeight) / 2;
y = 0;
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth, squareHeight);
} else {
squareBitmap = bmp;
}
if (squareBitmap.getWidth() != diameter || squareBitmap.getHeight() != diameter) {
scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter, diameter, true);
} else {
scaledSrcBmp = squareBitmap;
}
Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(), scaledSrcBmp.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(), scaledSrcBmp.getHeight());
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
if (isRoundCorner) {
// 画圆角
RectF rectF = new RectF(rect);
float r = corner_radius;
if (corner_radius == 0)
r = radius / 4;// 设置圆角默认值
canvas.drawRoundRect(rectF, r, r, paint);
} else {
// 画圆形图片
canvas.drawCircle(scaledSrcBmp.getWidth() / 2, scaledSrcBmp.getHeight() / 2, scaledSrcBmp.getWidth() / 2, paint);
}
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
bmp = null;
squareBitmap = null;
scaledSrcBmp = null;
return output;
}
仔细读下来,发现其实代码不是很难。主要是运用到了Xfermode的叠加显示效果,先将获得的方形图片在画布上显示,在画布上继续画上一个圆形或者圆角的图片,new PorterDuffXfermode(Mode.SRC_IN)取两层绘制交集,显示上层。需要了解更多有关Xfermode的可以点击这里去学学哦。
ok,自定义view显示圆形图片,到这里就结束了。有问题的提出来,大家一起学习哈!
显示圆形图片还有其他方法,在此顺便学习了别人的博客并介绍下如何使用自定义Drawable。
相对于自定义view,使用Drawable 又有哪些优点呢?
1.自定义Drawable,相比View来说,Drawable属于轻量级的、使用也很简单
2.自定义drawableU性能更加
那么怎么用呢,不说了直接上代码
public class CustomDrawable extends Drawable {
private Paint mPaint;
private int mWidth;
private Bitmap mBitmap;
private RectF rectF;
private int mType = 0;// 0表示正常,1表示圆形,其他表示圆角
private float mRadius = 50;
public CustomDrawable(Bitmap bitmap, int type) {
mBitmap = bitmap;
BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(bitmapShader);
mWidth = Math.min(mBitmap.getWidth(), mBitmap.getHeight());
mType = type;
}
public CustomDrawable setRadius(float radius) {
this.mRadius = radius;
return this;
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
rectF = new RectF(left, top, right, bottom);
}
@Override
public void draw(Canvas canvas) {
if (mType == 1)
canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2, mPaint);
else {
canvas.drawRoundRect(rectF, mRadius, mRadius, mPaint);
}
}
@Override
public int getIntrinsicWidth() {
return mWidth;
}
@Override
public int getIntrinsicHeight() {
return mWidth;
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
读下来发现代码不难哈,核心的思想和前面提到的自定View差不多,在此就不累赘了。直接看下如何使用它吧。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView iv_circle = (ImageView) findViewById(R.id.iv_circle);
ImageView iv_roundcorner = (ImageView) findViewById(R.id.iv_roundcorner);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.girl1);
iv_circle.setImageDrawable(new CustomDrawable(bitmap, 1));
iv_roundcorner.setImageDrawable(new CustomDrawable(bitmap, 2).setRadius(dip2px(50)));
}
/**
* Dip转Px
*
* @param context
* @param dipValue
* @return
*/
public int dip2px(float dipValue) {
try {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
使用是不是很简单哈,哈哈,相信大家也都学会了。由于篇幅关系,布局文件就不贴了。
舒口气,自定义圆形、圆角图片就讲到这里了。有问题的可以提出来,大家一起探讨哦。我也是一只边学边记录的菜鸟,欢迎骚扰。
最后感谢一下鸿洋的博客,他写的更好,大伙可以去学学!
Android Xfermode 实战 实现圆形、圆角图片
Android Drawable 那些不为人知的高效用法