自定义View(1)--圆形图片、圆角图片的实现

来源:互联网 发布:铁盒下料软件 编辑:程序博客网 时间:2024/05/18 19:47

之前说过会将项目中运用的东西抽离出来做一个总结,今天我主要想总结一下圆角和圆形头像问题。由于我们的应用涉及到很多用户头像,如果所有的图像都是方方正正的话,那显得不是很美观,所以设计湿强行要我将头像圆角化处理。好吧,so easy。项目截图我就不想贴了,还是一贯贴demo截图吧,如下:


看着效果还行啊,下面我们就讲讲怎么去实现它。

一、自定义ImageView显示

1.自定义View属性

自定义view,经常会涉及到view的属性问题,很简单,按以下操作你就能轻松实现自定义属性了。首先你得在资源文件里的values目录下,新建一个文件attrs,声明你想自定义的属性。

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="customParams">        <attr name="isCircle" format="boolean" />        <attr name="isRoundCorner" format="boolean" />        <attr name="corner_radius" format="dimension" />    </declare-styleable></resources>

至于declare-styleable的format,需要了解的同学,可以百度一下,很多文章有介绍哦(点这里查看)。其次,你需要在构造函数去关联这些属性,才能在画图过程中使用到他们。

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;        }    }}
想了解更多的话,可以点击这里去看看哦

2.图片绘画过程onDraw

这是本篇文章的重点,自定义View你必须去重写onDraw(Canvas canvas),在其中去实现你所需要的逻辑,代码如下:

@Overrideprotected 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来显示圆形、圆角图片

显示圆形图片还有其他方法,在此顺便学习了别人的博客并介绍下如何使用自定义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;}@Overridepublic void setBounds(int left, int top, int right, int bottom) {super.setBounds(left, top, right, bottom);rectF = new RectF(left, top, right, bottom);}@Overridepublic void draw(Canvas canvas) {if (mType == 1)canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2, mPaint);else {canvas.drawRoundRect(rectF, mRadius, mRadius, mPaint);}}@Overridepublic int getIntrinsicWidth() {return mWidth;}@Overridepublic int getIntrinsicHeight() {return mWidth;}@Overridepublic void setAlpha(int alpha) {mPaint.setAlpha(alpha);}@Overridepublic void setColorFilter(ColorFilter cf) {mPaint.setColorFilter(cf);}@Overridepublic int getOpacity() {return PixelFormat.TRANSLUCENT;}
读下来发现代码不难哈,核心的思想和前面提到的自定View差不多,在此就不累赘了。直接看下如何使用它吧。

public class MainActivity extends Activity {@Overrideprotected 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 那些不为人知的高效用法


0 0
原创粉丝点击