Android 裁剪摄像头预览窗口-SurfaceView

19 篇文章 0 订阅

概述

 Android 下, 使用SurfaceView显示摄像头预览, 通常使用的是一个矩形窗口, 如果, 要使用一个圆形窗口呢?
先上效果图
未打开摄像头
未打开摄像头
打开摄像头
打开摄像头

实现过程

视图布局
在这里插入图片描述
实现圆形裁剪的方法有很多, 最简单的, 可以在SurfaceView上方增加一个视图遮罩, 挡住不需要显示的区域即可
遮罩的方法有一个问题:
在这里插入图片描述
上图中, 属于ImageView的红色区域也会被遮挡


本文中采用的方法是:
用一个RelativeLayout包含SurfaceView, 通过裁剪RelativeLayout来实现:

		@Override
		protected void dispatchDraw(Canvas canvas) {
			//裁剪圆型画布,使SurfaceView显示圆形区域图像
			canvas.save();
			canvas.clipPath(path);
			super.dispatchDraw(canvas);
			canvas.restore();
		}

增加按键位置的拖动功能, 主要是为了解决验证一个问题:

//设置输入监听, 用于拖动按键位置.
		btn.setOnTouchListener(new View.OnTouchListener() {
			float cx, cy, dx, dy;
			int tx, ty;
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				cx = event.getRawX();
				cy = event.getRawY();
				switch(event.getAction()){
					case MotionEvent.ACTION_DOWN:
						dx = cx;
						dy = cy;
						tx = lpBtn.leftMargin;
						ty = lpBtn.topMargin;
						break;
					case MotionEvent.ACTION_MOVE:
						lpBtn.leftMargin = (int)(tx + (cx - dx));
						lpBtn.topMargin = (int)(ty + (cy - dy));
						btn.setLayoutParams(lpBtn);
						break;

					case MotionEvent.ACTION_UP:
						//开始预览
						cameraHelper.openCamera(false);
						cameraHelper.startPreview(null);

						//必须设置为FALSE
						//sv.setZOrderOnTop(false);
						rlRoot.requestLayout();
						break;
				}
				return false;
			}
		});

当写完代码开始在设备上运行调试时, 出现了一个奇怪的问题:
当系统显示导航栏时, 可以正常显示裁剪后的圆形, 在全屏或隐藏导航栏后, 裁剪失效了:
在这里插入图片描述
要解决这个问题也简单, 只需要在**XRelativeLayout(rl)**中的SurfaceView上, 覆盖一个控件, 源码中已注释

		//MUST add view overlay
		rl.addView(new View(this));

当然也可以增加到rlRoot中, 从上面的动图可以看到, 裁剪的有效范围与Button的位置有关.

参考代码

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.app.Activity;

public class CameraPreview extends Activity {
	int cameraId = 1;
	//根控件
	RelativeLayout rlRoot;
	CameraHelper cameraHelper;
	SurfaceView sv;
	RelativeLayoutX rl;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		rlRoot = new RelativeLayout(this);

		//显示于SurfaceView 下方的ImageView
		ImageView iv = new ImageView(this);
		RelativeLayout.LayoutParams lpIv = new RelativeLayout.LayoutParams(UiTools.WRAP_CONTENT, UiTools.WRAP_CONTENT);
		iv.setScaleType(ImageView.ScaleType.FIT_XY);
		iv.setImageResource(R.drawable.ic_drawer_product_imgs);
		iv.setOnTouchListener(new View.OnTouchListener() {
			float cx, cy, dx, dy;
			int tx, ty;
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				cx = event.getRawX();
				cy = event.getRawY();
				switch(event.getAction()){
					case MotionEvent.ACTION_DOWN:
						dx = cx;
						dy = cy;
						tx = lpIv.leftMargin;
						ty = lpIv.topMargin;

						break;
					case MotionEvent.ACTION_MOVE:
					    //同样有效
						//btn.setTranslationX(tx + (cx - dx));
						//btn.setTranslationY(ty + (cy - dy));
						lpIv.leftMargin = (int)(tx + (cx - dx));
						lpIv.topMargin = (int)(ty + (cy - dy));
						iv.setLayoutParams(lpIv);
						break;
				}
				return true;
			}
		});
		rlRoot.addView(iv, lpIv);

		//SurfaceView
		rl = new RelativeLayoutX(this);
		sv = new SurfaceView(this);
		//当未打开摄像头开始预览时, 显示透明(不设置则显示黑色)
		//sv.setZOrderOnTop(true);
		//sv.getHolder().setFormat(PixelFormat.TRANSLUCENT);
		rl.addView(sv, new RelativeLayout.LayoutParams(UiTools.MATCH_PARENT, UiTools.MATCH_PARENT));
		cameraHelper = new CameraHelper(camBase.getParameters(cameraId), sv, camBase);
		rlRoot.addView(rl);

		//MUST add view overlay
		//rl.addView(new View(this));
		RelativeLayout.LayoutParams lpBtn = new RelativeLayout.LayoutParams(UiTools.WRAP_CONTENT, UiTools.WRAP_CONTENT);
		Button btn = new Button(this);
		btn.setText("Show");
		//设置输入监听, 用于拖动按键位置.
		btn.setOnTouchListener(new View.OnTouchListener() {
			float cx, cy, dx, dy;
			int tx, ty;
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				cx = event.getRawX();
				cy = event.getRawY();
				switch(event.getAction()){
					case MotionEvent.ACTION_DOWN:
						dx = cx;
						dy = cy;
						tx = lpBtn.leftMargin;
						ty = lpBtn.topMargin;
						break;
					case MotionEvent.ACTION_MOVE:
						lpBtn.leftMargin = (int)(tx + (cx - dx));
						lpBtn.topMargin = (int)(ty + (cy - dy));
						btn.setLayoutParams(lpBtn);
						break;

					case MotionEvent.ACTION_UP:
						//开始预览
						cameraHelper.openCamera(false);
						cameraHelper.startPreview(null);

						//必须设置为FALSE
						//sv.setZOrderOnTop(false);
						rlRoot.requestLayout();
						break;
				}
				return false;
			}
		});
		rlRoot.addView(btn, lpBtn);

		setContentView(rlRoot);
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		cameraHelper.stopPreview();
	}

	static class RelativeLayoutX extends RelativeLayout{
		public RelativeLayoutX(Context context) {
			super(context);
		}

		Path path = new Path();
		@Override
		protected void onSizeChanged(int w, int h, int oldw, int oldh) {
			super.onSizeChanged(w, h, oldw, oldh);
			path.reset();
			path.addCircle(w/2f, h/2f, Math.min(w, h)/2f, Path.Direction.CW);
		}

		@Override
		protected void dispatchDraw(Canvas canvas) {
			//裁剪圆型画布,使SurfaceView显示圆形区域图像
			canvas.save();
			canvas.clipPath(path);
			super.dispatchDraw(canvas);
			canvas.restore();
		}
	}

	//非关键代码, 主要用于返回打开摄像头预览的参数
	CameraHelper.CameraBase camBase = new CameraHelper.CameraBaseSimple(){
		@Override
		public CameraHelper.CameraParams getParameters(int camIndex) {
			return new CameraHelper.CameraParams(cameraId, 640, 480, 30, 0, 0);
		}

		@Override
		public boolean sameParams(CameraHelper.CameraParams cp) {
			return true;
		}

		@Override
		public void onPreviewStarted(int camIndex) {}

		@Override
		public void onCameraError(int error) {}
	};

}

PS
虽然提出了需求, 也实现了想要的效果, 却也留下了一个问题, 问题虽然解决, 但却找不到具体的原因!

  1. 浮动窗的情况未测试过
  2. Dialog或其它弹出窗口未测试过
  3. setZOrderOnTop(true)的情况未有好的解决办法

参考

解决SurfaceView渲染的各种疑难杂症
android 圆形相机预览拍照_Android相机(摄像头)圆形预览窗口,圆形SurfaceView

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值