15

我正在尝试制作一个自定义相机应用程序,我想让用户可以在此应用程序中选择对焦模式。

对焦模式是自动和触摸对焦

如果我们想在相机中使用触控对焦,如何入手?

4

5 回答 5

17

该功能取决于软件/硬件/制造商,我的建议是你首先找到一个像 Galaxy Nexus 这样的手机刷有 Android 4.x,然后尝试 android.hardware.Camera.Parameters.getMaxNumFocusAreas() ,如果返回值大于零那么你很幸运,然后可以使用 setFocusAreas() 来实现你的“触摸聚焦”功能。

为什么:

在旧的 Android 版本中,没有用于设置焦点区域的公共 API。尽管许多制造商设法创建了自己的 API 和实现,但他们不会分享。

Android 在 API 级别 14 中引入了重点领域 API,但是手机制造商可能会选择不实施(即选择坚持自己的解决方案)。要检查 API 是否受支持,您可以先调用 getMaxNumFocusAreasa(),如果它返回一个正整数,则表示手机确实实现了 API,您可以继续在相机应用中启用“触摸焦点”功能。(API 也是“人脸检测”功能的推动者之一,当人脸被识别时,相机应用程序使用 API 让相机自动对焦于人脸。)

关于如何正确使用 API,您可以参考 vanilla Android Camera 应用程序源代码。

参考:

  1. 安卓相机 API

getMaxNumFocusAreas()

设置焦点区域()

  1. Android 4.0 相机应用源码

mInitialParams.getMaxNumFocusAreas()

mParameters.setFocusAreas()

问候

陈子腾

于 2012-07-27T06:19:45.963 回答
8

尝试这个:

public void takePhoto(File photoFile, String workerName, int width, int height, int    quality) {
if (getAutoFocusStatus()){
    camera.autoFocus(new AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            camera.takePicture(shutterCallback, rawCallback, jpegCallback);
        }
    }); 
}else{
    camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}

但是,我也看到了这个工作,可能更准确:

if (getAutoFocusStatus()){
    camera.autoFocus(new AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
           if(success) camera.takePicture(shutterCallback, rawCallback, jpegCallback);
        }
    }); 
}else{
    camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}

最后一张是在对焦成功完成的那一刻拍照。它非常适合与 QR 扫描码一起使用。我相信这同样适用于这样的情况。

于 2014-08-27T16:10:32.747 回答
5

它已经实现了,但是如果我想添加触摸到焦点,如何修改它?

public void takePhoto(File photoFile, String workerName, int width, int height, int    quality) {
    if (getAutoFocusStatus()){
        camera.autoFocus(new AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                camera.takePicture(shutterCallback, rawCallback, jpegCallback);
            }
        }); 
    }else{
        camera.takePicture(shutterCallback, rawCallback, jpegCallback);
    }

    this.photoFile = photoFile;
    this.workerName = workerName;
    this.imageOutputWidth = width;
    this.imageOutputHeight = height;
}

public void takePhoto(File photoFile, int width, int height, int quality) {
    takePhoto(photoFile, null, width, height, quality);
}
于 2012-07-28T02:44:14.517 回答
2

我试图在我的应用程序中实现焦点功能,并以我想要的方式实现了这个功能。要实现Touch to Focus,请参考下面的代码。

CameraPreview.java

public class CameraPreview extends SurfaceView implements
    SurfaceHolder.Callback {
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
private OnFocusListener onFocusListener;

private boolean needToTakePic = false;

private Camera.AutoFocusCallback myAutoFocusCallback = new Camera.AutoFocusCallback() {

    @Override
    public void onAutoFocus(boolean arg0, Camera arg1) {
        if (arg0) {
            mCamera.cancelAutoFocus();
        }
    }
};

// Constructor that obtains context and camera
@SuppressWarnings("deprecation")
public CameraPreview(Context context, Camera camera) {
    super(context);
    this.mCamera = camera;
    this.mSurfaceHolder = this.getHolder();
    this.mSurfaceHolder.addCallback(this);
    this.mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    this.onFocusListener = (OnFocusListener) context;
}

@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
    try {
        mCamera.setPreviewDisplay(surfaceHolder);
        mCamera.getParameters().setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        mCamera.setDisplayOrientation(90);
        mCamera.startPreview();
    } catch (IOException e) {
        // left blank for now
        e.printStackTrace();
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    mCamera.stopPreview();
    this.mSurfaceHolder.removeCallback(this);
    mCamera.release();
}

@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format,
                           int width, int height) {
    // start preview with new settings
    try {
        mCamera.setPreviewDisplay(surfaceHolder);
        mCamera.startPreview();
    } catch (Exception e) {
        // intentionally left blank for a test
        e.printStackTrace();
    }
}

/**
 * Called from PreviewSurfaceView to set touch focus.
 *
 * @param - Rect - new area for auto focus
 */
public void doTouchFocus(final Rect tfocusRect) {
    try {
        List<Camera.Area> focusList = new ArrayList<Camera.Area>();
        Camera.Area focusArea = new Camera.Area(tfocusRect, 1000);
        focusList.add(focusArea);

        Camera.Parameters param = mCamera.getParameters();
        param.setFocusAreas(focusList);
        param.setMeteringAreas(focusList);
        mCamera.setParameters(param);

        mCamera.autoFocus(myAutoFocusCallback);
    } catch (Exception e) {
        e.printStackTrace();
    }

    if (isNeedToTakePic()) {
        onFocusListener.onFocused();
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        float x = event.getX();
        float y = event.getY();

        Rect touchRect = new Rect(
                (int) (x - 100),
                (int) (y - 100),
                (int) (x + 100),
                (int) (y + 100));


        final Rect targetFocusRect = new Rect(
                touchRect.left * 2000 / this.getWidth() - 1000,
                touchRect.top * 2000 / this.getHeight() - 1000,
                touchRect.right * 2000 / this.getWidth() - 1000,
                touchRect.bottom * 2000 / this.getHeight() - 1000);

        doTouchFocus(targetFocusRect);
    }

    return false;
}

public boolean isNeedToTakePic() {
    return needToTakePic;
}

public void setNeedToTakePic(boolean needToTakePic) {
    this.needToTakePic = needToTakePic;
}
}

MainActivity.java

public class MainActivity extends Activity
    implements OnFocusListener {
private Button captureButton, switchCameraButton;
private Camera mCamera;
private CameraPreview mCameraPreview;
private int currentCameraId;

/**
 * Called when the activity is first created.
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (getIntent().hasExtra("camera_id")) {
        currentCameraId = getIntent().getIntExtra("camera_id", Camera.CameraInfo.CAMERA_FACING_BACK);
    } else {
        currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
    }

    captureButton = (Button) findViewById(R.id.button_capture);
    captureButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            // Obtain MotionEvent object
            v.setEnabled(false);
            mCameraPreview.setNeedToTakePic(true);
            long downTime = SystemClock.uptimeMillis();
            long eventTime = SystemClock.uptimeMillis() + 100;
            float x = mCameraPreview.getWidth() / 2;
            float y = mCameraPreview.getHeight() / 2;
            // List of meta states found here:     developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
            int metaState = 0;
            MotionEvent motionEvent = MotionEvent.obtain(
                    downTime,
                    eventTime,
                    MotionEvent.ACTION_DOWN,
                    x,
                    y,
                    metaState
            );

            // Dispatch touch event to view
            mCameraPreview.dispatchTouchEvent(motionEvent);
        }
    });

    switchCameraButton = (Button) findViewById(R.id.button_switch_camera);
    switchCameraButton.setVisibility(
            Camera.getNumberOfCameras() > 1 ? View.VISIBLE : View.GONE);
    switchCameraButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mCamera.stopPreview();
            //NB: if you don't release the current camera before switching, you app will crash
            mCameraPreview.getHolder().removeCallback(mCameraPreview);
            mCamera.release();

            //swap the id of the camera to be used
            if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
            } else {
                currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
            }

            mCamera = getCameraInstance(currentCameraId);
            mCameraPreview = new CameraPreview(MainActivity.this, mCamera);
            FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
            preview.removeAllViews();
            preview.addView(mCameraPreview);
        }
    });
}

@Override
protected void onResume() {
    super.onResume();
    mCamera = getCameraInstance(currentCameraId);
    mCameraPreview = new CameraPreview(this, mCamera);
    FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
    preview.addView(mCameraPreview);
}

/**
 * Helper method to access the camera returns null if it cannot get the
 * camera or does not exist
 *
 * @return
 */
private Camera getCameraInstance(int currentCameraId) {
    Camera camera = null;
    try {
        camera = Camera.open(currentCameraId);
    } catch (Exception e) {
        // cannot get camera or does not exist
    }
    return camera;
}

Camera.PictureCallback mPicture = new Camera.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        File pictureFile = getOutputMediaFile();
        if (pictureFile == null) {
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
};

private static File getOutputMediaFile() {
    File mediaStorageDir = new File(
            Environment
                    .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            "MyCameraApp");
    if (!mediaStorageDir.exists()) {
        if (!mediaStorageDir.mkdirs()) {
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }
    // Create a media file name
    //        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
    //                .format(new Date());
    File mediaFile;
    mediaFile = new File(mediaStorageDir.getPath() + File.separator
            + "IMG_" + "DEMO_" + ".jpg");
    if (mediaFile.exists()) mediaFile.delete();

    return mediaFile;
}

@Override
public void onFocused() {
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            mCamera.takePicture(null, null, mPicture);
            mCameraPreview.setNeedToTakePic(false);
            captureButton.setEnabled(true);
        }
    }, 1500);
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />

<Button
    android:id="@+id/button_switch_camera"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="Switch Camera" />

<Button
    android:id="@+id/button_capture"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="Capture" />

</LinearLayout>

您可以在 Github 上找到示例应用程序- 自定义相机应用程序

于 2016-02-24T13:32:59.917 回答
0

调用它来启用触控对焦模式:

private void setTouchToFocusMode(Camera.Parameters parameters){
    String focusMode;
    if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
        focusMode = Camera.Parameters.FOCUS_MODE_AUTO;
    }
    if (focusMode != null && focusMode.length() > 0){
        parameters.setFocusMode(focusMode);
    }
}

当用户点击屏幕时,在下面调用这个来设置焦点区域:

private static final int FOCUS_WIDTH = 80;
private static final int FOCUS_HEIGHT = 80;

public static String setFocalPoint(Camera.Parameters params, int x, int y){
    String focusMode = "";
    if (params != null && params.getMaxNumFocusAreas() > 0) {
        List<Camera.Area> focusArea = new ArrayList<Camera.Area>();
        focusArea.add(new Camera.Area(new Rect(x, y, x + FOCUS_WIDTH, y + FOCUS_HEIGHT), 1000));

        params.setFocusAreas(focusArea);

        if(params.getMaxNumMeteringAreas() > 0) {
            params.setMeteringAreas(focusArea);
        }
        if(params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            focusMode = Camera.Parameters.FOCUS_MODE_AUTO;
        }
    }
    return focusMode;
}

调用 autoFocus/cancelAutoFocus 进行操作:

mCamera.cancelAutoFocus();    
mCamera.autoFocus(mAutoFocusCallback);
于 2016-12-23T02:20:42.380 回答