准备知识
@Override
public void onDraw(canvas){
super.onDraw(canvas)
}
@Override
public draw(canvas){
super.draw(canvas);
}
系统绘制窗口并不是直接调用这两个函数来完成,只是用来提供接口让开发者能在绘制前修改canvas的方法,所以
@Override
public void onDraw(canvas){
super.draw(new Canvas());
}
@Override
public draw(canvas){
super.onDraw(new Canvas());
}
所绘制的并不是新的Canvas,而是当前View在draw()之前的Canvas内容。
Xfermode版本(不推荐):
使用Paint.setXfermode(new PorterDuffXfermode (PorterDuff.Mode.DST_IN)),必须关闭硬件加速,图片数量众多时,卡顿明显,且实践发现在SDK28版本上无法利用DST剪裁SRC,这里提供的是改进版,思路是先绘制View底板形状,再绘制Bitmap。
核心代码如下:
@Override
public void draw(Canvas canvas) {
//判断Drawable是否是Bitmap
if (getDrawable ( ) instanceof BitmapDrawable) {
//获取当前View尺寸建立Rect
mRect.set (0, 0, getWidth ( ), getHeight ( ));
//创建目标Bitmap
Bitmap target = Bitmap.createBitmap (getWidth ( ), getHeight ( ), Bitmap.Config.ARGB_8888);
//调用ImageView.draw(),再目标Bitmap上绘制缩放调整后的Bitmap
super.draw (new Canvas (target));
//清空原canvas, 这里要关闭硬件加速, 否则会绘制出黑色背景
canvas.drawColor (Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//绘制底板形状
mPath.addRoundRect (mRect, mRadiusArray, Path.Direction.CCW);
canvas.drawPath (mPath, mBackgroundPaint);
//绘制目标Bitmap到Canvas
canvas.drawBitmap (target, 0, 0, mContentPaint);
} else {
super.draw (canvas);
}
}
完整圆角ImageView代码如下
package com.example.crxzy.centertainment.views;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import com.example.crxzy.centertainment.R;
import java.util.Arrays;
import java.util.Objects;
public class RoundedImageView extends ImageView {
private RectF mRect = new RectF ( );
private float[] mRadiusArray = new float[8];
private Path mPath = new Path ( );
PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode (PorterDuff.Mode.SRC_IN);
private Paint mBackgroundPaint;
private Paint mContentPaint;
public RoundedImageView(Context context) {
super (context);
initBackgroundPaint (context);
initContentPaint ( );
setLayerType (LAYER_TYPE_HARDWARE, null);
}
private void initContentPaint() {
mContentPaint = new Paint (Paint.ANTI_ALIAS_FLAG);
mContentPaint.setXfermode (porterDuffXfermode);
}
private void initBackgroundPaint(Context context) {
mBackgroundPaint = new Paint (Paint.ANTI_ALIAS_FLAG);
mBackgroundPaint.setStyle (Paint.Style.FILL);
mBackgroundPaint.setColor (context.getColor (R.color.gray));
mBackgroundPaint.setAntiAlias (true);//消除锯齿
}
@Override
public void draw(Canvas canvas) {
//判断Drawable是否是Bitmap
if (getDrawable ( ) instanceof BitmapDrawable) {
//获取当前View尺寸建立Rect
mRect.set (0, 0, getWidth ( ), getHeight ( ));
//创建目标Bitmap
Bitmap target = Bitmap.createBitmap (getWidth ( ), getHeight ( ), Bitmap.Config.ARGB_8888);
//调用ImageView.draw(),再目标Bitmap上绘制缩放调整后的Bitmap
super.draw (new Canvas (target));
//清空原canvas, 这里要关闭硬件加速, 否则会绘制出黑色背景
canvas.drawColor (Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//绘制底板形状
mPath.addRoundRect (mRect, mRadiusArray, Path.Direction.CCW);
canvas.drawPath (mPath, mBackgroundPaint);
//绘制目标Bitmap到Canvas
canvas.drawBitmap (target, 0, 0, mContentPaint);
} else {
super.draw (canvas);
}
}
public RoundedImageView(Context context, @Nullable AttributeSet attrs) {
super (context, attrs);
TypedArray typedArray = context.obtainStyledAttributes (attrs, R.styleable.RoundedImageView);
if (typedArray != null) {
Arrays.fill (mRadiusArray, typedArray.getDimension (R.styleable.RoundedImageView_radius, 0));
mRadiusArray[0] = mRadiusArray[1] = typedArray.getDimension (R.styleable.RoundedImageView_bottomLeftRadius, mRadiusArray[0]);
mRadiusArray[2] = mRadiusArray[3] = typedArray.getDimension (R.styleable.RoundedImageView_bottomRightRadius, mRadiusArray[2]);
mRadiusArray[4] = mRadiusArray[5] = typedArray.getDimension (R.styleable.RoundedImageView_topLeftRadius, mRadiusArray[4]);
mRadiusArray[6] = mRadiusArray[7] = typedArray.getDimension (R.styleable.RoundedImageView_topRightRadius, mRadiusArray[6]);
}
Objects.requireNonNull (typedArray).recycle ( );
initBackgroundPaint (context);
initContentPaint ( );
setLayerType (LAYER_TYPE_HARDWARE, null);
}
public void setCornerSize(float cornerSize) {
Arrays.fill (mRadiusArray, cornerSize);
}
}
attr.xml 内容
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--属性集合-->
<declare-styleable name="RoundedImageView">
<attr name="bottomLeftRadius" format="dimension" />
<attr name="topLeftRadius" format="dimension" />
<attr name="bottomRightRadius" format="dimension" />
<attr name="topRightRadius" format="dimension" />
<attr name="radius" format="dimension" />
</declare-styleable>
</resources>
BitmapShader版
Shader是Paint的着色器,提供Paint在当前位置上笔头的颜色信息,不理解的化参考一下PS的仿制图章。我们利用Shader和Path直接绘制一个圆角矩形。
核心代码如下
@Override
public void draw(Canvas canvas) {
//判断Drawable是否是Bitmap
if (getDrawable ( ) instanceof BitmapDrawable) {
//获取当前View尺寸建立Rect
mRect.set (0, 0, getWidth ( ), getHeight ( ));
//创建目标Bitmap
Bitmap target = Bitmap.createBitmap (getWidth ( ), getHeight ( ), Bitmap.Config.ARGB_8888);
//使用View.draw方法得到经过拉升变换后的bitmap
super.draw (new Canvas (target));
//利用目标Bitmap创建设置着色器
BitmapShader shader = new BitmapShader (target, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader (shader);
//绘制View形状
mPath.addRoundRect (mRect, mRadiusArray, Path.Direction.CCW);
canvas.drawPath (mPath, mPaint);
} else {
super.draw (canvas);
}
}
完整圆角ImageView代码如下
package com.example.crxzy.centertainment.views;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import com.example.crxzy.centertainment.R;
import java.util.Arrays;
import java.util.Objects;
public class RoundedImageView extends ImageView {
private RectF mRect = new RectF ( );
private Path mPath = new Path ( );
private Paint mPaint;
private float[] mRadiusArray = new float[8];
PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode (PorterDuff.Mode.SRC_IN);
public RoundedImageView(Context context) {
super (context);
initPaint (context);
setLayerType (LAYER_TYPE_HARDWARE, null);
}
public RoundedImageView(Context context, @Nullable AttributeSet attrs) {
super (context, attrs);
TypedArray typedArray = context.obtainStyledAttributes (attrs, R.styleable.RoundedImageView);
if (typedArray != null) {
Arrays.fill (mRadiusArray, typedArray.getDimension (R.styleable.RoundedImageView_radius, 0));
mRadiusArray[0] = mRadiusArray[1] = typedArray.getDimension (R.styleable.RoundedImageView_bottomLeftRadius, mRadiusArray[0]);
mRadiusArray[2] = mRadiusArray[3] = typedArray.getDimension (R.styleable.RoundedImageView_bottomRightRadius, mRadiusArray[2]);
mRadiusArray[4] = mRadiusArray[5] = typedArray.getDimension (R.styleable.RoundedImageView_topLeftRadius, mRadiusArray[4]);
mRadiusArray[6] = mRadiusArray[7] = typedArray.getDimension (R.styleable.RoundedImageView_topRightRadius, mRadiusArray[6]);
}
Objects.requireNonNull (typedArray).recycle ( );
initPaint (context);
setLayerType (LAYER_TYPE_HARDWARE, null);
}
private void initPaint(Context context) {
mPaint = new Paint (Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle (Paint.Style.FILL);
mPaint.setAntiAlias (true);//消除锯齿
}
@Override
public void draw(Canvas canvas) {
//判断Drawable是否是Bitmap
if (getDrawable ( ) instanceof BitmapDrawable) {
//获取当前View尺寸建立Rect
mRect.set (0, 0, getWidth ( ), getHeight ( ));
//创建目标Bitmap
Bitmap target = Bitmap.createBitmap (getWidth ( ), getHeight ( ), Bitmap.Config.ARGB_8888);
//使用View.draw方法得到经过拉升变换后的bitmap
super.draw (new Canvas (target));
//利用目标Bitmap创建设置着色器
BitmapShader shader = new BitmapShader (target, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader (shader);
//绘制View形状
mPath.addRoundRect (mRect, mRadiusArray, Path.Direction.CCW);
canvas.drawPath (mPath, mPaint);
} else {
super.draw (canvas);
}
}
public void setCornerSize(float cornerSize) {
Arrays.fill (mRadiusArray, cornerSize);
}
}
attr.xml 内容
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--属性集合-->
<declare-styleable name="RoundedImageView">
<attr name="bottomLeftRadius" format="dimension" />
<attr name="topLeftRadius" format="dimension" />
<attr name="bottomRightRadius" format="dimension" />
<attr name="topRightRadius" format="dimension" />
<attr name="radius" format="dimension" />
</declare-styleable>
</resources>
网友评论