How to render SurfaceView cropped to a circle?

时间:2019-01-20 21:33:44

标签: android surfaceview geometry android-windowmanager

I want to render a camera preview inside a circle, like this:

I have it working inside a fragment added to an activity. But I would like to show the view above other apps by adding it to the WindowManager.

So what I have working is showing views above other apps but the SurfaceView does not clip at all. And separately I have the SurfaceView clipping to circle, but in a fragment.

I have read many answers here and articles about this topic. There is very little info about cropping a SurfaceView, and no library that does what I want. Most of the info I found only explains how to crop it to a square or some other rectangle.

How it looks when added to WindowManager

I am trying to dra

How can I clip a SurfaceView that is shown above all views?

SurfaceView subclass

public class CircleSurface extends SurfaceView {

    private Path clipPath;

    public CircleSurface(Context context) {
        super(context);
        init();
    }

    public CircleSurface(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CircleSurface(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setZOrderMediaOverlay(true);
        setWillNotDraw(false);

        SurfaceHolder sfhTrackHolder = getHolder();
        sfhTrackHolder.setFormat(PixelFormat.TRANSPARENT);


        clipPath = new Path();
        //TODO: define the circle you actually want
        clipPath.addCircle(710, 330, 250, Path.Direction.CW);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.clipPath(clipPath);
        super.dispatchDraw(canvas);
    }
}

Service that adds views to the window manager

public class LiveStreamingService extends Service implements SurfaceHolder.Callback {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(startId, getNotification());
        }
        sharedInstance = this;

        // android shouldn't auto restart this service
        return START_NOT_STICKY;
    }

    @Override
    public void onCreate() {
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);

        // camera view
        CircleSurface cameraSurfaceView = new CircleSurface(this);
        cameraSurfaceView.getHolder().addCallback(this);

        wm.addView(cameraSurfaceView, getLayoutParams(360, 270));
    }

    private WindowManager.LayoutParams getLayoutParams(int width, int height) {
        int overlayType = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O 
                ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 
                : WindowManager.LayoutParams.TYPE_PHONE;

        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                width,
                height,
                overlayType,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                PixelFormat.TRANSLUCENT
        );

        return layoutParams;
    }

    // ...
}

UPDATE

Seems like the camera rendering is somehow separate from the dispatchDraw method. I can do whatever in dispatchDraw and the camera keeps showing as a rectangle.

1 个答案:

答案 0 :(得分:0)

为表面视图创建了一个绘图线程,该线程使用OnPreviewFrame侦听相机预览回调。

在线程的绘制循环中:

c.drawColor(0, PorterDuff.Mode.CLEAR);
c.drawColor(0x1000000); // magical color that removes everything (like transparency)

if (currentFrame == null) return;

c.clipPath(circleSurfaceView.path);
c.drawBitmap(currentFrame, 0, 0, paint);

我不知道什么是神奇的颜色。我碰巧碰到了它。我尝试的所有其他操作只会产生黑色或模糊的噪声。