Reputation: 3068
I am trying to make a custom camera application I want to let the users can choose the focus mode in this application.
The focus mode is auto and touch-to-focus
If we want to use touch-to-focus in the camera , how can be start with?
Upvotes: 15
Views: 35812
Reputation: 1393
Call this to enable Touch-To-Focus mode:
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);
}
}
When user taps on screen, call this below to set focus area:
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;
}
Call autoFocus/cancelAutoFocus for action:
mCamera.cancelAutoFocus();
mCamera.autoFocus(mAutoFocusCallback);
Upvotes: 0
Reputation: 16798
I was trying to implement focus functionality in my app and achieved this functionality in the way i wanted. To implement Touch to Focus
please refer the code below.
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>
You can find sample app on Github - Custom Camera App
Upvotes: 2
Reputation: 1032
Try this:
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);
}
However, I've also seen this to work, possibly more accurately:
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);
}
The last one takes the picture at the moment the focussing is successfully completed. It works very well for using with QR scanning codes. I believe the same applies to cases like this.
Upvotes: 8
Reputation: 3068
It is already implemented buthow to modify this if I want to add touch to focus?
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);
}
Upvotes: 5
Reputation: 1969
The feature is software/hardware/manufacture dependent, my suggestion is that you first find a phone like Galaxy Nexus flashed with Android 4.x, then try the android.hardware.Camera.Parameters.getMaxNumFocusAreas() on it, if the return value is greater than zero then you are lucky, and can then use the setFocusAreas() to implement your "touch to focus" feature.
Why:
In old Android versions there is no public API to set the focus areas. Although many manufacturers managed to create their own API and implementation, they won't share.
Android introduced the focus areas API in API level 14, however the phone manufacturers may choose not to implement it (i.e. choose to stick to their own solutions). To check if the API is supported you can call getMaxNumFocusAreasa() first, if it returns a positive integer that means the phone does implement the API and you can go on enabling the "touch focus" feature in your camera app. (The API is also one of the enablers of the "face detection" feature, when faces are recognized the camera app uses the API to let the camera do auto focus on the them.)
You may refer to the vanilla Android Camera app source code for how to use the API properly.
References:
mInitialParams.getMaxNumFocusAreas()
Regards
Ziteng Chen
Upvotes: 17