在很多应用中头像是以圆形图片展示的,默认图片的话可以让UI切一套圆形的,但是用户设置头像的话不可能也让UI切。其实已经有很多前辈牛人已经写了成熟完善的工具类供程序猿使用。当然实现方式也多种多样。这里道长使用图形渲染实现CircleImageView,然后说一下图形渲染的一些知识。
一、CircleImageView实现
- 构造方法初始化变量
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
mMatrix = new Matrix();
mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
}
public CircleImageView(Context context) {
this(context, null);
}
- 测量并设置控件宽高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = mWidth / 2;
setMeasuredDimension(mWidth, mWidth);
}
- 给Paint设置渲染规则
private void setPaintShader() {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
Bitmap bitmap = drawable2Bitmap(drawable);
// 创建Bitmap渲染对象
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
float scale = 1.0f;
// 比较bitmap宽和高,获得较小值
int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight());
scale = mWidth * 1.0f / bSize;
// shader的变换矩阵,用于放大或者缩小
mMatrix.setScale(scale, scale);
// 设置变换矩阵
mBitmapShader.setLocalMatrix(mMatrix);
// 设置shader
mBitmapPaint.setShader(mBitmapShader);
}
/**
* drawable转bitmap
*
* @param drawable
* @return
*/
private Bitmap drawable2Bitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
}
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
}
- 在画布上绘制图片
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() == null) {
return;
}
try {
setPaintShader();
canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint);
} catch (Exception e) {
e.printStackTrace();
}
}
- 在布局中添加如下代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="@+id/iv_default"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:src="@drawable/head" />
<com.yushan.picturerenderdemo.CircleImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_below="@id/iv_default"
android:layout_margin="5dp"
android:src="@drawable/head" />
</RelativeLayout>
- 上面的为原图,下面的为CircleImageView,效果图如下
就这么简单,是不是so easy~当然这个代码中有一个弊端道长没有处理,就是在测量设置控件宽高时道长只是简单的取了一个宽高最小值,然后除以2就设置了。这就导致了CircleImageView最大只能展示原图大小。这个大家可以自行处理。然后我们说一下图形渲染……
二、图形渲染
在实现自定义CircleImageView中道长使用了图形渲染中的图像渲染,而前面道长说过的画布实现自定义View(折线图实现),在绘制限制区域时道长使用的就是图形渲染中的线性渲染。这里道长就不贴代码了,前面有链接童鞋们可以去看一下。图形渲染大概分为五种:
- BitmapShader(图像渲染)
/**
* @param bitmap 用来作为纹理填充的位图
* @param tileX 在位图X方向上位图衔接形式
* @param tileY 在位图Y方向上位图衔接形式
*/
public BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY);
Shader.TileMode有3种参数可供选择,分别为CLAMP、REPEAT和MIRROR。
CLAMP - 如果渲染器超出原始边界范围,则会复制边缘颜色对超出范围的区域进行着色。
REPEAT - 在横向和纵向上以平铺的形式重复渲染位图。
MIRROR - 在横向和纵向上以镜像的方式重复渲染位图。
简单使用:
/**
* 图像渲染
*
* @param canvas
* @param paint
*/
private void drawBitmapShader(Canvas canvas, Paint paint) {
// 加载图像资源
Bitmap mBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.head)).getBitmap();
// 创建Bitmap渲染对象
Shader mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
// 绘制Bitmap渲染的圆
paint.setShader(mBitmapShader);
canvas.drawCircle(100, 100, 100, paint);
}
- LinearGradient(线性渲染)
/**
* @param x0 渐变的起始点x坐标
* @param y0 渐变的起始点y坐标
* @param x1 渐变的终点x坐标
* @param y1 渐变的终点y坐标
* @param colors 渐变的颜色数组
* @param positions 指定颜色数组的相对位置
* @param tile 平铺方式
*/
public LinearGradient (float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile);
注意:参数positions设为null,表示颜色数组以斜坡线的形式均匀分布。
简单使用:
/**
* 线性渲染
*
* @param canvas
* @param paint
*/
private void drawLinearGradient(Canvas canvas, Paint paint) {
// 创建线性渲染对象
int mColorLinear[] = {Color.WHITE, Color.YELLOW, Color.GREEN, Color.BLUE};
Shader mLinearGradient = new LinearGradient(150, 150, 100, 100, mColorLinear, null, Shader.TileMode.REPEAT);
// 绘制线性渐变的矩形
paint.setShader(mLinearGradient);
canvas.drawRect(100, 300, 400, 500, paint);
}
- ComposeShader(混合渲染)
/**
* @param shaderA 一种渲染效果
* @param shaderB 一种渲染效果
* @param mode 两种渲染效果的叠加模式
*/
public ComposeShader (Shader shaderA, Shader shaderB, PorterDuff.Mode mode);
PorterDuff.Mode有16种参数可供选择,分别为:CLEAR、SRC、DST、SRC_OVER、DST_OVER、SRC_IN、DST_IN、SRC_OUT、DST_OUT、SRC_ATOP、DST_ATOP、XOR、DARKEN、LIGHTEN、MULTIPLY、SCREEN。
叠加模式的具体叠加效果如下图所示:
简单使用:
/**
* 混合渲染
*
* @param canvas
* @param paint
*/
private void drawComposeShader(Canvas canvas, Paint paint) {
// 创建线性渲染对象
int mColorLinear[] = {Color.WHITE, Color.YELLOW, Color.GREEN, Color.BLUE};
Shader mLinearGradient = new LinearGradient(150, 150, 100, 100, mColorLinear, null, Shader.TileMode.REPEAT);
// 创建环形渲染对象
int mColorRadial[] = {Color.GREEN, Color.RED, Color.BLUE, Color.WHITE};
Shader mRadialGradient = new RadialGradient(300, 300, 75, mColorRadial, null, Shader.TileMode.REPEAT);
//创建混合渲染对象
Shader mComposeShader = new ComposeShader(mLinearGradient, mRadialGradient, PorterDuff.Mode.DARKEN);
//绘制混合渐变(线性与环形混合)的矩形
paint.setShader(mComposeShader);
canvas.drawRect(10, 420, 250, 570, paint);
}
- RadialGradient(环形渲染)
/**
* @param x 圆心x坐标
* @param y 圆心y坐标
* @param radius 半径
* @param colors 渐变的颜色数组
* @param positions 颜色数组的相对位置
* @param tile 平铺的方式
*/
public RadialGradient (float x, float y, float radius, int[] colors, float[] positions, Shader.TileMode tile);
简单使用:
/**
* 环形渲染
*
* @param canvas
* @param paint
*/
private void drawRadialGradient(Canvas canvas, Paint paint) {
// 创建环形渲染对象
int mColorRadial[] = {Color.WHITE, Color.YELLOW, Color.GREEN, Color.BLUE};
Shader mRadialGradient = new RadialGradient(500, 500, 100, mColorRadial, null, Shader.TileMode.REPEAT);
// 绘制环形渐变的圆
paint.setShader(mRadialGradient);
canvas.drawCircle(500, 500, 100, paint);
}
- SweepGradient(梯度渲染)
/**
* @param cx 扫描的中心x坐标
* @param cy 扫描的中心y坐标
* @param colors 渐变的颜色数组
* @param positions 颜色数组的相对位置
*/
public SweepGradient (float cx, float cy, int[] colors, float[] positions);
简单使用:
/**
* 梯度渲染
*
* @param canvas
* @param paint
*/
private void drawSweepGradient(Canvas canvas, Paint paint) {
// 创建梯形渲染对象
int mColorSweep[] = {Color.GREEN, Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN};
Shader mSweepGradient = new SweepGradient(650, 700, mColorSweep, null);
// 绘制梯形渐变的矩形
paint.setShader(mSweepGradient);
canvas.drawRect(600, 600, 700, 800, paint);
}
到了这里关于自定义CircleImageView的实现以及图形渲染已经说完了,其实道长说自定义CircleImageView是一个引子,重点是说一下图形渲染,这个图形渲染在自定义View中还是比较常用的。好了,希望这篇博客能够为你提供一些帮助。