在项目开发中,有时候需要展示圆形图片或者是角图片,android原生的TextView和Button等可以用定义shape实现,而偏偏ImageView通过定义shape不能实现这样的需求。本文就是介绍如何通过BitmapShader来实现圆角、圆形的ImageView的。
在Android提供了Shader类专门用来渲染图像以及一些几何图形。Shader就是着色器的意思。我们可以这样理解,Canvas中的各种drawXXX方法定义了图形的形状,画笔中的Shader则定义了图形的着色、外观,二者结合到一起就决定了最终Canvas绘制的被Shader类包括了5个直接子类,分别是BitmapShader、ComposeShader、LinearGradient、RadialGradient和SweepGradient。BitmapShader要用于图像渲染,ComposeShader用于混合渲染,LinearGradient用于线性渲染,RadialGradient用于环形渲染,SweepGradient则用于梯度渲染。
BitmapShader就是我们这次自定义圆角和圆形ImageView要用到的类,BitmapShader,就是用Bitmap对绘制的图形进行渲染着色。可以通过Paint.setShader(Shader shader)进行设置,BitmapShader构造函数如下所示:
public BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY);
参数bitmap表示用来作为纹理填充的位图;参数tileX表示在位图X方向上位图衔接形式;参数tileY表示在位图Y方向上位图衔接形式。
Shader.TileMode有3种参数可供选择,分别为CLAMP、REPEAT和MIRROR。
CLAMP: 表示拉伸 ,当所画图形的尺寸大于Bitmap的尺寸的时候,拉伸的是图片最后的那一个像素,横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复。
REPEAT:表示重复,当绘制的图形尺寸大于Bitmap尺寸时,会用Bitmap重复平铺整个绘制的区域。
MIRROR:表示镜像,当绘制的图形尺寸大于Bitmap尺寸时,MIRROR也会用Bitmap重复平铺整个绘图区域,与REPEAT不同的是横向不断翻转重复,纵向不断翻转重复。
实现圆形和圆角ImageView我们使用的TileMode 是CLAMP模式。下面是自定圆形ImageView和自定义圆角ImageView的示例demo。
这是使用的原图片
一 自定义圆形ImageView最终显示效果
package com.circleimageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class CircleImageView extends ImageView {
private Paint mPaint; //画笔
private int mRadius; //圆形图片的半径
private float mScale; //图片的缩放比例
public CircleImageView(Context context) {
super(context);
}
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//因为是圆形图片,所以宽高保持一致
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = size / 2;
//设置最终尺寸
setMeasuredDimension(size, size);
}
@Override
protected void onDraw(Canvas canvas) {
//初始化画笔
mPaint = new Paint();
//设置抗锯齿
mPaint.setAntiAlias(true);
//将drawable转成Bitmap
Bitmap bitmap = drawable2Bitmap(getDrawable());
//初始化BitmapShader,TileMode设置为拉模式
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//计算缩放比例
mScale = (mRadius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth());
//使用Matrix对bitmapShader进行缩放,为什么要缩放?因为当图片大于View的设定的宽高时,只会绘制左上角的局域,
// 这样会显示不完全。
Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
bitmapShader.setLocalMatrix(matrix);
//画笔设置定义好的BitmapShader
mPaint.setShader(bitmapShader);
//画圆形,参数分别为中心点坐标、半径、画笔
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
}
//drawble转成Bitmap
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;
}
}
布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.circleimageview.CircleImageView
android:id="@+id/image1"
android:layout_width="120dp"
android:layout_height="120dp"
android:scaleType="centerCrop"
android:src="@mipmap/ic_piture">
</com.circleimageview.CircleImageView>
</LinearLayout>
二 自定义圆角ImageView效果图如下
实现代码如下,每一行也都写了注释
package com.circleimageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Created by moxianli on 2017/11/22.
*/
public class RoundImageView extends ImageView{
private Paint mPaint; //画笔
//圆角半径,可放到自定义属性里面,此处不做处理,只给默认值
private int mBorderRadius = 20;
private float mScale; //图片的缩放比例
private RectF mRoundRect; //矩形
public RoundImageView(Context context) {
super(context);
}
public RoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
//初始化画笔
mPaint = new Paint();
//设置抗锯齿
mPaint.setAntiAlias(true);
//将drawable转成Bitmap
Bitmap bitmap = drawable2Bitmap(getDrawable());
//初始化BitmapShader,TileMode设置为拉模式
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//计算缩放比例
mScale = Math.max(getWidth() * 1.0f/bitmap.getWidth(), getHeight() * 1.0f/bitmap.getHeight());
//使用Matrix对bitmapShader进行缩放
// 如果图片的宽或者高与view的宽高不一致,需要计算出缩放的比例
// 图片缩放后的的宽高要大于view的宽高,此处取大值
Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
bitmapShader.setLocalMatrix(matrix);
//画笔设置定义好的BitmapShader
mPaint.setShader(bitmapShader);
//画圆形,参数分别为中心点坐标、半径、画笔
canvas.drawRoundRect(mRoundRect,mBorderRadius,mBorderRadius,mPaint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//圆角图片的范围绘制范围为设置的宽高值
mRoundRect = new RectF(0, 0, getWidth(), getHeight());
}
//drawble转成Bitmap
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;
}
}
布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.circleimageview.RoundImageView
android:id="@+id/image1"
android:layout_width="120dp"
android:layout_height="120dp"
android:scaleType="centerCrop"
android:src="@mipmap/ic_piture">
</com.circleimageview.RoundImageView>
</LinearLayout>