demo比較簡單,背景顏色是黃色,然後在上面覆蓋上自定義View。實際需求是在相機的View上蓋上一層遮罩,遮罩中心鏤空一個透明顯示的圓形部分。這個需求一般的佈局比較難以實現,如果要ui給一張素材圖片又存在不同分辨率手機對圖片的拉伸問題,中間的圓會變形,適配不好。所以想要用一個自定義View來實現,這樣不僅不存在適配問題而且也比較簡單。
效果圖:
簡單粗暴直接上代碼:
package com.xingyun.scandemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by SY on 2018/5/11.
*/
public class ScanView extends View {
private int measuredHeight;
private int measuredWidth;
//圓心x座標
private int centerX;
//圓心y座標
private int centerY;
//圓半徑
private int radius;
//起始弧度、掃過弧度
private float startAngle=-90, sweepAngle=180;
public ScanView(Context context) {
super(context);
}
public ScanView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ScanView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measuredHeight = getMeasuredHeight();
measuredWidth = getMeasuredWidth();
}
@Override
protected void onDraw(Canvas canvas) {
//創建鏤空圓和遮罩bitmap
Bitmap rectBitmap = createRectBitmap();
Bitmap circleBitmap = createCircleBitmap();
Paint paint = new Paint();
paint.setFilterBitmap(false);
//保存所有的標識
canvas.saveLayer(0, 0, measuredWidth, measuredHeight, null,
Canvas.MATRIX_SAVE_FLAG);
//畫圓
canvas.drawBitmap(circleBitmap, 0, 0, paint);
//setXfermode 爲SRC_OUT 只在源圖像和目標圖像不相交的地方繪製
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
//畫遮罩矩形
canvas.drawBitmap(rectBitmap, 0, 0, paint);
//畫外部圓環
Paint loopPaint = new Paint();
int strokeWidth = 20;
loopPaint.setStrokeWidth(strokeWidth);
loopPaint.setAntiAlias(true);
loopPaint.setStyle(Paint.Style.STROKE);
loopPaint.setStrokeCap(Paint.Cap.ROUND);
float distance = 40;
RectF oval = new RectF((int) (measuredWidth / 2f) - radius - distance, (int) (measuredHeight / 2f) - radius - distance,
(int) (measuredWidth / 2f) - radius - distance + 2 * (radius + distance), (int) (measuredHeight / 2f) - radius - distance + 2 * (radius + distance));
loopPaint.setColor(Color.GRAY);
//底層灰色圓
canvas.drawCircle(centerX, centerY, radius + distance, loopPaint);
//漸變色
LinearGradient lg = new LinearGradient(0, 0, 1000, 1000, Color.parseColor("#a5d2fe"), Color.parseColor("#519aff"), Shader.TileMode.MIRROR);
loopPaint.setShader(lg);
//藍色圓弧
canvas.drawArc(oval, startAngle, sweepAngle, false, loopPaint);
startAngle = (startAngle + 1) % 360;
//回收
rectBitmap .recycle();
circleBitmap .recycle();
rectBitmap =null;
circleBitmap =null;
//重繪
invalidate();
}
/**
* 創建鏤空層圓形形狀
*/
private Bitmap createCircleBitmap() {
Bitmap bm = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
centerX = (int) (measuredWidth / 2f);
centerY = (int) (measuredHeight / 2f);
radius = measuredWidth / 3;
canvas.drawCircle(centerX, centerY, radius, paint);
return bm;
}
/**
* 創建遮罩層形狀
*/
private Bitmap createRectBitmap() {
Bitmap bm = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
canvas.drawRect(new RectF(0, 0, measuredWidth, measuredHeight), paint);
return bm;
}
}
Activity裏添加:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/yellow"
tools:context="com.xingyun.scandemo.MainActivity">
<com.xingyun.scandemo.ScanView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
直接用FrameLayout將自定義的View覆蓋在其他View上面即可。
代碼很簡單,就是在onMeasure方法裏獲取了寬高,然後onDraw方法裏通過createCircleBitmap和createRectBitmap方法獲得遮罩矩形和中間圓形的bitmap,再分別繪製這兩個bitmap,將畫筆Xfermode設置爲PorterDuff.Mode.SRC_OUT,表示只在源圖像和目標圖像不相交的地方繪製。最後在圓的外面繪製了一個圓弧,並且不斷修改起始弧度再次繪製,從而實現旋轉的動畫。關於Paint的Xfermode的屬性可以看下圖: