1. 手指点击 surface 的坐标
给 根布局添加 添加手势监听view, 高通的相机代码
camera.xml
<com.android.camera.ui.RenderOverlay
android:id="@+id/render_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
public class RenderOverlay extends FrameLayout {
//事件分发
@Override
public boolean dispatchTouchEvent(MotionEvent m) {
if (mGestures != null) {
mGestures.dispatchTouch(m);
return true;
}
if (mTouchClients != null) {
boolean res = false;
for (Renderer client : mTouchClients) {
res |= client.onTouchEvent(m);
}
return res;
}
return true;
}
}
public class PreviewGestures{
private static final String TAG = "PreviewGestures";
private static final int MODE_NONE = 0;
private static final int MODE_ZOOM = 2;
private final int FLING_VELOCITY_MIN = 400;
private Activity mContext;
private GestureListener mGestureListener;
private ScaleGestureDetector mScaleDetector;
private GestureDetector mGestureDetector;
private int mMode;
private boolean mEnabled;
private boolean onScaling = false;
// listener
private GestureDetector.SimpleOnGestureListener mListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress (MotionEvent e) {
if(mGestureListener != null) {
mGestureListener.onLongPress((int) e.getX(), (int) e.getY());
}
}
@Override
public boolean onSingleTapUp (MotionEvent e) {
// 回调到各个 module
// Tap to focus when pie is not open
if(mGestureListener != null){
mGestureListener.onSingleTapUp(null, (int) e.getX(), (int) e.getY());
}
return true;
}
@Override
public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1 == null) {
// e1 can be null if for some cases.
return false;
}
if (mMode == MODE_ZOOM) return false;
int deltaX = (int) (e1.getX() - e2.getX());
int deltaY = (int) (e1.getY() - e2.getY());
int orientation = 0;
if (isLeftSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeRight(e1, e2, distanceX, distanceY);
}
return true;
}
if (isRightSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeLeft(e1, e2, distanceX, distanceY);
}
return true;
}
if (isUpSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeUp(e1, e2, distanceX, distanceY);
}
return true;
}
if (isDownSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeDown(e1, e2, distanceX, distanceY);
}
return true;
}
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if(mGestureListener != null &&
(Math.abs(velocityX) > FLING_VELOCITY_MIN || Math.abs(velocityY) > FLING_VELOCITY_MIN) &&
!onScaling){
mGestureListener.onFling(e1, e2, velocityX, velocityY);
}
return true;
}
// left -> right
private boolean isLeftSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
case 180:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
case 270:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
default:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
}
}
//right -> left
private boolean isRightSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
case 180:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
case 270:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
default:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
}
}
//bottom -> top
private boolean isUpSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
case 180:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
case 270:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
default:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
}
}
//top -> bottom
private boolean isDownSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
case 180:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
case 270:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
default:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
}
}
};
public interface GestureListener{
boolean onSingleTapUp(View v, int x, int y);
boolean onLongPress(int x, int y);
void onSwipeUp(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
void onSwipeDown(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
void onSwipeLeft(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
void onSwipeRight(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
void onScale(ScaleGestureDetector detector);
void onScaleBegin(ScaleGestureDetector detector);
void onScaleEnd(ScaleGestureDetector detector);
void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
public PreviewGestures(Activity ctx) {
mContext = ctx;
mMode = MODE_NONE;
mEnabled = true;
mGestureDetector = new GestureDetector(mListener);
mScaleDetector = new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
if(mGestureListener != null){
mGestureListener.onScale(detector);
onScaling = true;
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
if(mGestureListener != null){
mGestureListener.onScaleBegin(detector);
}
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
if(mGestureListener != null){
mGestureListener.onScaleEnd(detector);
onScaling = false;
}
}
});
}
public void setGestureListener(GestureListener listener){
mGestureListener = listener;
}
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
public boolean isEnabled() {
return mEnabled;
}
//接收到事件 去处理
public boolean dispatchTouch(MotionEvent m) {
mGestureDetector.onTouchEvent(m);
mScaleDetector.onTouchEvent(m);
switch (m.getAction()){
case MotionEvent.ACTION_DOWN:{
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:{
onScaling = false;
break;
}
case MotionEvent.ACTION_MOVE:{
break;
}
}
return true;
}
}
//2. 每个监听的module
class captureModule implements GestureListener{
//点击了
public void onSingleTapUp(View v, int x, int y) {
//解锁
if (isAfAeLocked) {
lockAEAF(false);
}
mState = STATE_WAITING_TOUCH_FOCUS;
int orientation = mOneCamera.getDisplayOrientation(mOrientation);
mMainHandler.removeMessages(CANCEL_TOUCH_FOCUS);
//
mOneCameraParamController.triggerFocusAtPoint(x, y, mCurrentZoom, getSurfaceViewSize(), orientation);
Message message = mMainHandler.obtainMessage(CANCEL_TOUCH_FOCUS);
mMainHandler.sendMessageDelayed(message, CANCEL_TOUCH_FOCUS_DELAY);
}
}
// 3. 具体的设置转换区域
public class OneCameraParamController {
private MeteringRectangle[] mAFRegions;
private MeteringRectangle[] mAERegions;
private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
private final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = new MeteringRectangle[]{
new MeteringRectangle(0, 0, 0, 0, 0)};
//将点击的位置坐标 和 当前变焦倍数 surfaceViewsize传递过去
public void triggerFocusAtPoint(float x, float y,double zoom,Point p,int displayOrientation){
Rect cropRegion = new Rect();
//裁剪区域
cropRegionForZoom(cropRegion,zoom);
if (cropRegion.top < 0
|| cropRegion.bottom < 0
|| cropRegion.left < 0
|| cropRegion.right < 0){
Log.d(TAG, "crop region exit error exception,param <0 ");
return;
}
//获取预览 surface 的宽高
int width = p.x;
int height = p.y;
// 点击的x y , surfaceView 的w h, 区域比例, 方向
mAFRegions = afaeRectangle(x, y, width, height, 1f, cropRegion,displayOrientation);
mAERegions = afaeRectangle(x, y, width, height, 1.5f, cropRegion,displayOrientation);
autoFocusTrigger();
}
private MeteringRectangle[] afaeRectangle(float x, float y, int width, int height,
float multiple, Rect cropRegion,int displayOrientation) {
//默认效果区域, 1/8 * multiple
float side = Math.max(width, height) / 8f * multiple;
RectF meteringRegionF = new RectF(x - side / 2f, y - side / 2f, x + side / 2f, y + side / 2f);
// inverse of matrix1 will translate from touch to (-1000 to 1000), which is camera1
// coordinates, while accounting for orientation and mirror
Matrix matrix1 = new Matrix();
//视图坐标系到 预览坐标系
CameraUtils.prepareMatrix(matrix1, !mOneCamera.isBackCamera(), displayOrientation, width, height);
matrix1.invert(matrix1);
//预览坐标系 转换 camera2 坐标系
// inverse of matrix2 will translate from (-1000 to 1000) to camera 2 coordinates
Matrix matrix2 = new Matrix();
matrix2.preTranslate(-cropRegion.width() / 2f, -cropRegion.height() / 2f);
matrix2.postScale(2000f / cropRegion.width(), 2000f / cropRegion.height());
matrix2.invert(matrix2);
//转换
matrix1.mapRect(meteringRegionF);
matrix2.mapRect(meteringRegionF);
//获取到转换后的实际af/ae 的区域大小
Rect meteringRegion = new Rect((int) meteringRegionF.left, (int) meteringRegionF.top,
(int) meteringRegionF.right, (int) meteringRegionF.bottom);
//边界验证
meteringRegion.left = CameraUtils.clamp(meteringRegion.left, cropRegion.left,
cropRegion.right);
meteringRegion.top = CameraUtils.clamp(meteringRegion.top, cropRegion.top,
cropRegion.bottom);
meteringRegion.right = CameraUtils.clamp(meteringRegion.right, cropRegion.left,
cropRegion.right);
meteringRegion.bottom = CameraUtils.clamp(meteringRegion.bottom, cropRegion.top,
cropRegion.bottom);
//生成需要实际 camera2 af/ae 的区域大小
MeteringRectangle[] meteringRectangle = new MeteringRectangle[1];
if ((meteringRegion.right - meteringRegion.left == 0) && meteringRegion.bottom - meteringRegion.top == 0) {
meteringRectangle[0] = new MeteringRectangle(cropRegion, 1);
} else {
meteringRectangle[0] = new MeteringRectangle(meteringRegion, 1);
}
return meteringRectangle;
}
private void cropRegionForZoom(Rect cropRegion,double zoom) {
//获取当前相机的可用区域的大小
Rect activeRegion = getSensorActiveArraySize(mOneCamera);
if (activeRegion == null){
return;
}
//获取中心点
int xCenter = activeRegion.width() / 2;
int yCenter = activeRegion.height() / 2;
//根据变焦倍数 重新计算
int xDelta = (int) (activeRegion.width() / (2 * zoom));
int yDelta = (int) (activeRegion.height() / (2 * zoom));
//裁剪出具体的可视区域
cropRegion.set(xCenter - xDelta, yCenter - yDelta, xCenter + xDelta, yCenter + yDelta);
}
private Rect getSensorActiveArraySize(OneCamera mOneCamera){
if(mOneCamera.getCharacteristics() == null){
return new Rect(0,0,3264,2448);
}
//获取可用区域
return mOneCamera.getCharacteristics().get(CameraCharacteristics
.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
}
3. 将区域设置
private void autoFocusTrigger() {
CaptureRequest.Builder builder = getPreviewRequestBuilder();
if(builder == null){
return;
}
//设置对焦模式
mControlAFMode = CaptureRequest.CONTROL_AF_MODE_AUTO;
builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode);
//设置af ae 区域
applySettingsForAutoFocus(builder);
//下发 builder中的指令参数
mOneCamera.startRepeatingRequest();
}
public void applySettingsForAutoFocus(CaptureRequest.Builder builder) {
builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
applyAFRegions(builder);
applyAERegions(builder);
}
private void applyAFRegions(CaptureRequest.Builder request) {
if (mControlAFMode == CaptureRequest.CONTROL_AF_MODE_AUTO) {
request.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions);
} else {
request.set(CaptureRequest.CONTROL_AF_REGIONS, ZERO_WEIGHT_3A_REGION);
}
}
private void applyAERegions(CaptureRequest.Builder request) {
if (mControlAFMode == CaptureRequest.CONTROL_AF_MODE_AUTO) {
request.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions);
} else {
request.set(CaptureRequest.CONTROL_AE_REGIONS, ZERO_WEIGHT_3A_REGION);
}
}
}
CameraUtils
class CameraUtils{
public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
int viewWidth, int viewHeight) {
// Need mirror for front camera.
matrix.setScale(mirror ? -1 : 1, 1);
// This is the value for android.hardware.Camera.setDisplayOrientation.
matrix.postRotate(displayOrientation);
// Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
// UI coordinates range from (0, 0) to (width, height).
//视图坐标系 转换到 预览坐标系 范围
matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
//视图坐标系 转换到 预览坐标系 原点
matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
}
public static int clamp(int x, int min, int max) {
if (x > max) return max;
if (x < min) return min;
return x;
}
public static float clamp(float x, float min, float max) {
if (x > max) return max;
if (x < min) return min;
return x;
}
}