Android Paint Xfermode實現鏤空相機掃描界面

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的屬性可以看下圖:


具體可以參考文章https://www.cnblogs.com/libertycode/p/6290497.html。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章