前言
这篇博客只是为了做一个记录而已, 方便而后查询, 核心代码都是直接采用鸿洋博客里面的代码的
圆形头像在实际开发中实际很常见, 一般来说, 主要有两种实现方式:
第一种: 使用 Paint 的 Xfermode 实战
第二种方法: 使用 BitmapShader 实现
第一种: 使用 Paint 的 Xfermode 实战
圆形头像, 我们可以看成是 在原图上面绘制一个圆, 再取交集
从代码的角度来讲, 先绘制 Dst, 再绘制 Src, 显示的区域是二者交集, 由此可知 SrcIn 符合我们的要求
圆形图片的核心思路
取出 Bitmap, 并根据图片的宽高计算缩放比例
设置 Paint 的 setXfermode
在 onDraw 方法里面绘制 Bitmap
核心代码
- protected void onDraw(Canvas canvas)
- {
- // 在缓存中取出 bitmap
- Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
- if (null == bitmap || bitmap.isRecycled())
- {
- // 拿到 Drawable
- Drawable drawable = getDrawable();
- // 获取 drawable 的宽和高
- int dWidth = drawable.getIntrinsicWidth();
- int dHeight = drawable.getIntrinsicHeight();
- if (drawable != null)
- {
- // 创建 bitmap
- bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
- Config.ARGB_8888);
- float scale = 1.0f;
- // 创建画布
- Canvas drawCanvas = new Canvas(bitmap);
- // 按照 bitmap 的宽高, 以及 view 的宽高, 计算缩放比例; 因为设置的 src 宽高比例可能和 imageview 的宽高比例不同, 这里我们不希望图片失真;
- if (type == TYPE_ROUND)
- {
- // 如果图片的宽或者高与 view 的宽高不匹配, 计算出需要缩放的比例; 缩放后的图片的宽高, 一定要大于我们 view 的宽高; 所以我们这里取大值;
- scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
- * 1.0f / dHeight);
- } else
- {
- scale = getWidth() * 1.0F / Math.min(dWidth, dHeight);
- }
- // 根据缩放比例, 设置 bounds, 相当于缩放图片了
- drawable.setBounds(0, 0, (int) (scale * dWidth),
- (int) (scale * dHeight));
- drawable.draw(drawCanvas);
- if (mMaskBitmap == null || mMaskBitmap.isRecycled())
- {
- mMaskBitmap = getBitmap();
- }
- // Draw Bitmap.
- mPaint.reset();
- mPaint.setFilterBitmap(false);
- mPaint.setXfermode(mXfermode);
- // 绘制形状
- drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);
- mPaint.setXfermode(null);
- // 将准备好的 bitmap 绘制出来
- canvas.drawBitmap(bitmap, 0, 0, null);
- //bitmap 缓存起来, 避免每次调用 onDraw, 分配内存
- mWeakBitmap = new WeakReference<Bitmap>(bitmap);
- }
- }
- // 如果 bitmap 还存在, 则直接绘制即可
- if (bitmap != null)
- {
- mPaint.setXfermode(null);
- canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
- return;
- }
- }
- /**
- * 绘制形状
- * @return
- */
- public Bitmap getBitmap()
- {
- Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setColor(Color.BLACK);
- if (type == TYPE_ROUND)
- {
- canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()),
- mBorderRadius, mBorderRadius, paint);
- } else
- {
- canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2,
- paint);
- }
- return bitmap;
- }
以上代码来自鸿洋大神的博客 Android Xfermode 实战实现圆形圆角图片
第二种方法: 使用 BitmapShader 实现
Shader used to draw a bitmap as a texture.
官方文档说的很清楚了: BitmapShader 的作用是使用特定的图片来作为纹理来使用
BitmapShader 的构造函数
public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)
三个参数:
bitmap 指的是要作为纹理的图片,
tileX 指的是在x方向纹理的绘制模式,
tileY 指的是Y方向上的绘制模式
TileMode 源码:
- public enum TileMode {
- /**
- * replicate the edge color if the shader draws outside of its
- * original bounds
- */
- CLAMP (0),
- /**
- * repeat the shader's image horizontally and vertically
- */
- REPEAT (1),
- /**
- * repeat the shader's image horizontally and vertically, alternating
- * mirror images so that adjacent images always seam
- */
- MIRROR (2);
- TileMode(int nativeInt) {
- this.nativeInt = nativeInt;
- }
- final int nativeInt;
- }
TileMode 是一个枚举类型, 有3个可能的值:
CLMP 如果需要填充的内容大小超过了 bitmap size 就选 bitmap 边界的颜色进行扩展
REPEAT 重复, 不断的重复 bitmap 去填满, 如果绘制的区域大于纹理图片的话, 纹理图片会在这片区域不断重复
MIRROR 镜像的去填满如果绘制的区域大于纹理图片的话, 纹理图片会以镜像的形式重复出现
BitmapShader 实战 实现圆形圆角图片 核心思路
取出 bitmap
用 BitmapShader 去装饰 bitmap, 并设置给画笔
在 onDraw 方法中, 调用 canvas 的 draw 方法绘制
伪代码实现思路
- // 创建
- BitmapShader shader=new BitmapShader(bitmap,TileMode.CLAMP,TileMode.CLAMP);
- Paint paint=new Paint();
- // 为 paint 设置 Shader
- paint.setShader(shader);
- // 这样就可以使用 shader 的纹理去覆盖绘制的图形的表面了, 其中根据: CLAMP,REPEAT,MIRROR,
- // 来确定纹理的绘制模式
- canvas.draw**(***,paint);
核心代码实心思路
- @Override
- protected void onDraw(Canvas canvas)
- {
- if (getDrawable() == null)
- {
- return;
- }
- setUpShader();
- if (type == TYPE_ROUND)
- {
- canvas.drawRoundRect(mRoundRect, mBorderRadius, mBorderRadius,
- mBitmapPaint);
- } else
- {
- canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint);
- // drawSomeThing(canvas);
- }
- }
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh)
- {
- super.onSizeChanged(w, h, oldw, oldh);
- // 圆角图片的范围
- if (type == TYPE_ROUND)
- mRoundRect = new RectF(0, 0, getWidth(), getHeight());
- }
- /**
- * 初始化 BitmapShader
- */
- private void setUpShader()
- {
- Drawable drawable = getDrawable();
- if (drawable == null)
- {
- return;
- }
- Bitmap bmp = drawableToBitamp(drawable);
- // 将 bmp 作为着色器, 就是在指定区域内绘制 bmp
- mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP, TileMode.CLAMP);
- float scale = 1.0f;
- if (type == TYPE_CIRCLE)
- {
- // 拿到 bitmap 宽或高的小值
- int bSize = Math.min(bmp.getWidth(), bmp.getHeight());
- scale = mWidth * 1.0f / bSize;
- } else if (type == TYPE_ROUND)
- {
- // 如果图片的宽或者高与 view 的宽高不匹配, 计算出需要缩放的比例; 缩放后的图片的宽高, 一定要大于我们 view 的宽高; 所以我们这里取大值;
- scale = Math.max(getWidth() * 1.0f / bmp.getWidth(), getHeight()
- * 1.0f / bmp.getHeight());
- }
- // shader 的变换矩阵, 我们这里主要用于放大或者缩小
- mMatrix.setScale(scale, scale);
- // 设置变换矩阵
- mBitmapShader.setLocalMatrix(mMatrix);
- // 设置 shader
- mBitmapPaint.setShader(mBitmapShader);
- }
以上代码来自鸿洋大神的 Android BitmapShader 实战 实现圆形圆角图片
参考博客:
Android Xfermode 实战实现圆形圆角图片
Android BitmapShader 实战 实现圆形圆角图片
来源: https://blog.csdn.net/gdutxiaoxu/article/details/79658621