import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
public class ClipPathCircleView extends AppCompatImageView {
private Paint mPaint;
private int mRadius;// 圆形图片的半径
private Path mPath;
private RectF mRect;
private Bitmap mBitmap;
public ClipPathCircleView(Context context) {
super(context);
init();
}
public ClipPathCircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ClipPathCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setAntiAlias(true);
mPath = new Path();
mRect = new RectF();
mBitmap = drawableToBitmap(getDrawable());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = Math.min(getMeasuredWidth(),getMeasuredHeight());
mRadius = size / 2;
setMeasuredDimension(size,size);
}
//canvas.save();
//Path path = new Path();
//path.addCircle(300, 300, 200, Path.Direction.CCW); // 画一个圆形的path
//canvas.clipPath(path); // 裁剪画布的区域为圆形
//RectF rectF = new RectF(100, 100, 500, 500);
//canvas.drawBitmap(photo, null, rectF, paint); // 在区域之外的部分不会被渲染出来
//canvas.restore();
@Override
protected void onDraw(Canvas canvas) {
// 注意如果这行不注释调,ImageView会把原图画在底部
//super.onDraw(canvas);
mRect.set(0,0,mRadius*2,mRadius*2);
mBitmap = thumbImageWithMatrix(mRadius*2,mRadius*2,mBitmap);
mPath.addCircle(mRadius,mRadius,mRadius, Path.Direction.CCW);// 逆时针
canvas.clipPath(mPath);
// 第一个rect是要画图片的哪个区域,第一个rect是画到哪里
canvas.drawBitmap(mBitmap,null,mRect,mPaint);
}
//写一个drawble转BitMap的方法
private Bitmap drawableToBitmap(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;
}
// https://www.jianshu.com/p/abcfa74c967b
/**
* 此方法对图片进行缩小,无法放大
* @param targetWidth
* @param targetHeight
* @return
*/
private Bitmap zoomBitmap(int targetWidth,int targetHeight, int bitmapRes){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(),bitmapRes,options);
float scaleW = options.outWidth / (targetWidth*1f);
float scaleH = options.outHeight / (targetHeight*1f);
int size = (int)Math.max(scaleW,scaleH);
if (size <= 1){
options.inSampleSize = 1;
}else {
options.inSampleSize = size;
}
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(),bitmapRes,options);
}
private Bitmap zoomBitmapV1(int targetWidth,int targetHeight, int bitmapRes){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), bitmapRes, options);
options.inJustDecodeBounds = false;
//设置位图的屏幕密度,即每英寸有多少个像素
options.inDensity = options.outWidth;
//设置位图被画出来时的目标像素密度
//与options.inDensity配合使用,可对图片进行缩放
options.inTargetDensity = targetWidth;
return BitmapFactory.decodeResource(getResources(),bitmapRes,options);
}
/**
* 对图片进行缩放
* 图片始终都会被加载到内存中,注意OOM
* @param destWidth
* @param destHeight
* @param bitmapOrg
* @return
*/
public Bitmap thumbImageWithMatrix(float destWidth, float destHeight,Bitmap bitmapOrg) {
float bitmapOrgW = bitmapOrg.getWidth();
float bitmapOrgH = bitmapOrg.getHeight();
float bitmapNewW = (int) destWidth;
float bitmapNewH = (int) destHeight;
Matrix matrix = new Matrix();
matrix.postScale(bitmapNewW / bitmapOrgW, bitmapNewH / bitmapOrgH);
Bitmap destBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, (int) bitmapOrgW, (int) bitmapOrgH, matrix, true);
//bitmapOrg.recycle();
return destBitmap;
}
}
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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
public class BitmapShaderCircleView extends AppCompatImageView {
private Paint mPaint;
private int mRadius;// 圆形图片的半径
public BitmapShaderCircleView(@NonNull Context context) {
super(context);
init();
}
public BitmapShaderCircleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BitmapShaderCircleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
}
@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) {
Bitmap bitmap = drawableToBitmap(getDrawable());
//初始化BitmapShader,传入bitmap对象
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//计算图片的缩放比例
float mScale = (mRadius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth());
Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
bitmapShader.setLocalMatrix(matrix);
mPaint.setShader(bitmapShader);
//画圆形,指定好中心点坐标、半径、画笔
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
}
//写一个drawble转BitMap的方法
private Bitmap drawableToBitmap(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;
}
}
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
public class PorterDuffXfermodeCircleView extends AppCompatImageView {
private Paint mPaint;
private int mRadius;// 圆形图片的半径
private Xfermode mPorterDuffXfermode; // 图像混合模式
public PorterDuffXfermodeCircleView(@NonNull Context context) {
super(context);
init();
}
public PorterDuffXfermodeCircleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public PorterDuffXfermodeCircleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
//绘制两层,先绘制形状(DST),再绘制图片(SRC),然后根据Xfermode来去计算两个图层的关系(SRC_IN:显示的区域是二者交集部分的SRC)。
mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
}
@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) {
//获取sourceBitmap,即通过xml或者java设置进来的图片
Drawable drawable = getDrawable();
if (drawable == null) return;
Bitmap sourceBitmap = ((BitmapDrawable)getDrawable()).getBitmap();
if (sourceBitmap != null){
//对图片进行缩放,以适应控件的大小
Bitmap bitmap = resizeBitmap(sourceBitmap,getWidth(),getHeight());
drawCircleBitmapByXfermode(canvas,bitmap); //(1)利用PorterDuffXfermode实现
}
}
private Bitmap resizeBitmap(Bitmap sourceBitmap,int dstWidth,int dstHeight){
int width = sourceBitmap.getWidth();
int height = sourceBitmap.getHeight();
float widthScale = ((float)dstWidth) / width;
float heightScale = ((float)dstHeight) / height;
//取最大缩放比
float scale = Math.max(widthScale,heightScale);
Matrix matrix = new Matrix();
matrix.postScale(scale,scale);
return Bitmap.createBitmap(sourceBitmap,0,0,width,height,matrix,true);
}
//相同点
//saveLayer可以实现save所能实现的功能
//不同点
//1,saveLaye生成一个独立的图层而save只是保存了一下当时画布的状态类似于一个还原点(本来就是)。
//2,saveLaye因为多了一个图层的原因更加耗费内存慎用。
//3,saveLaye可指定保存相应区域,尽量避免2中所指的情况。
//4,在使用混合模式setXfermode时会产生不同的影响。
//
//
//
private void drawCircleBitmapByXfermode(Canvas canvas,Bitmap bitmap){
final int sc = canvas.saveLayer(0,0,getWidth(),getHeight(),null,Canvas.ALL_SAVE_FLAG);
//canvas.save();这里不能使用
//绘制dst层
canvas.drawCircle(mRadius,mRadius,mRadius,mPaint);
//设置图层混合模式为SRC_IN
mPaint.setXfermode(mPorterDuffXfermode);
//绘制src层
canvas.drawBitmap(bitmap,0,0,mPaint);
//canvas.restore();
canvas.restoreToCount(sc);
}
}
参考文章Android圆形图片不求人,自定义View实现(BitmapShader使用)android圆形图片效果Android 实现圆角/圆形图片的几种方式Android绘制圆形图片的3个方法浅谈Android实现圆形头像效果的几种思路和方法