項目中要繪製一個人像對正框,第一個想到的就是先繪製背景,然後利用PorterDuffXfermode(PorterDuff.Mode.XOR)將人像摳掉,繪製人像框,然而,調了許久才發現當預覽區域的背景是半透明的,PorterDuff.Mode.XOR不能將重疊區域摳掉,預覽區域是不透明時就可以摳掉,具體原因可能是PorterDuff.Mode對透明度有一些要求吧。後來想到ucrop庫在裁剪圖片時的對正框和這裏很類似,看了源碼後,才知道還有這種操作,主要是clipPath方法的第二個參數,話不多說,看圖上代碼。
設計稿:
實現:
實現方式:上方是一個貝塞爾曲線,下方是大半個橢圓
public class FaceAlignView extends View {
private Context mContext;
private RectF mBgRect;
private Path mClipPath;
private Paint mClipPaint;
private float mVerticalMargin;
private float mHorizontalMargin;
private PathMeasure mPathMeasure;
public FaceAlignView(Context context) {
this(context, null);
}
public FaceAlignView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FaceAlignView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initPaint();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBgRect.left = 0;
mBgRect.top = 0;
mBgRect.right = w;
mBgRect.bottom = h;
mClipPath.reset();
//畫下方的橢圓
mClipPath.addArc(mHorizontalMargin, mVerticalMargin / 3f, w - mHorizontalMargin, h - mVerticalMargin, -35, 250);
//取出橢圓的右上角的起始座標
float[] pos = new float[2];
float[] tan = new float[2];
mPathMeasure.setPath(mClipPath, false);
mPathMeasure.getPosTan(0, pos, tan);
//畫上方的二階貝塞爾曲線
mClipPath.moveTo(w - pos[0], pos[1]);
mClipPath.cubicTo(w - pos[0] * 0.86f, pos[1] * 0.35f, pos[0] * 0.86f, pos[1] * 0.35f, pos[0], pos[1]);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//截取人像透明區域 Region.Op.DIFFERENCE path外的區域才允許繪製
canvas.clipPath(mClipPath, Region.Op.DIFFERENCE);
canvas.drawColor(mContext.getResources().getColor(R.color.face_align_bg));
canvas.restore();
//繪製人像邊緣
canvas.drawPath(mClipPath, mClipPaint);
}
private void initPaint() {
mBgRect = new RectF();
mClipPath = new Path();
mPathMeasure = new PathMeasure();
mClipPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mClipPaint.setColor(mContext.getResources().getColor(R.color.face_align_clip_path));
mClipPaint.setStrokeWidth(DisplayUtils.dpToPx(4));
mClipPaint.setStyle(Paint.Style.STROKE);
mClipPaint.setStrokeCap(Paint.Cap.ROUND);
mVerticalMargin = dpToPx(50);
mHorizontalMargin = dpToPx(40);
}
public static int dpToPx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}
}