30

I'm working on an app which has a custom camera screen, for which I'm supposed to implement tap to focus, like in the Android(more specifically, the Galaxy S4) camera app.

I've tried using the steps outlined here, but it doesn't seem to cause any noticeable focusing. The Focus Mode is set to Continuous Picture(we are supporting only a specific device).

When the user taps on the camera preview, I need to be focusing on the top half of the image. For this, I use the code snippet

Parameters parameters = mCamera.getParameters();

if (parameters.getMaxNumFocusAreas() > 0) {

    ArrayList<Area> focusAreas = new ArrayList<Camera.Area>(1);
    focusAreas.add(new Area(new Rect(-1000, -1000, 1000, 0), 750));

    parameters.setFocusAreas(focusAreas);
    mCamera.setParameters(parameters);
}

I do NOT want AutoFocus as it takes too long to focus on the image. I am interested only in the top half of the image. Has anybody successfully implemented Tap to Focus along with Continuous Picture mode?

Vinay S Shenoy
  • 4,028
  • 3
  • 31
  • 36

5 Answers5

27

Bumped into this issue recently. As MatheusJardimB said, this question helps a lot.

However, in my case, I wanted to start in the ContinuousPicture mode then be able to tap to focus and then continue with the ContinuousPicture mode.

I managed to get it to work by using the onAutoFocus method of the Camera.AutoFocusCallback(). I'm not sure if it's the best or the prettiest way of doing it, but it seems to work.

Here's the code:

setOnTouchListener(new View.OnTouchListener() {         
    @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (mCamera != null) {
                Camera camera = mCamera.getCamera();
                camera.cancelAutoFocus();
                Rect focusRect = calculateTapArea(event.getX(), event.getY(), 1f);

                Parameters parameters = camera.getParameters();
                parameters.setFocusMode(Parameters.FOCUS_MODE_MACRO);

                if (parameters.getMaxNumFocusAreas() > 0) {
                    List<Area> mylist = new ArrayList<Area>();
                    mylist.add(new Camera.Area(focusRect, 1000));
                    parameters.setFocusAreas(mylist);
                }

                camera.setParameters(parameters);
                camera.autoFocus(new Camera.AutoFocusCallback() {                   
                    @Override
                    public void onAutoFocus(boolean success, Camera camera) {
                        camera.cancelAutoFocus();
                        Parameters params = camera.getParameters();
                        if (!params.getFocusMode().equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                            params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                            camera.setParameters(params);
                        }
                    }
                });
            }
            return true;
        }
        return false;
    });

You could just change the focus area to

ArrayList<Area> focusAreas = new ArrayList<Camera.Area>(1);
focusAreas.add(new Area(new Rect(-1000, -1000, 1000, 0), 750));

and it should work.

UPDATE

I recently acquired a Samsung S5 and tested this out on it. It didn't work that well, so I added a few modifications and it's working now. This was also successfully tested on the Galaxy S6 and Galaxy Note4.

Here's the modified code:

setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mCamera != null) {
            Camera camera = mCamera.getCamera();
            camera.cancelAutoFocus();
            Rect focusRect = calculateTapArea(event.getX(), event.getY(), 1f);

            Parameters parameters = camera.getParameters();
            if (parameters.getFocusMode().equals(
                    Camera.Parameters.FOCUS_MODE_AUTO) {
                parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
            }

            if (parameters.getMaxNumFocusAreas() > 0) {
                List<Area> mylist = new ArrayList<Area>();
                mylist.add(new Camera.Area(focusRect, 1000));
                parameters.setFocusAreas(mylist);
            }

            try {
                camera.cancelAutoFocus();
                camera.setParameters(parameters);
                camera.startPreview();
                camera.autoFocus(new Camera.AutoFocusCallback() {
                    @Override
                    public void onAutoFocus(boolean success, Camera camera) {
                        if (!camera.getParameters().getFocusMode().equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                            Parameters parameters = camera.getParameters();
                            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                            if (parameters.getMaxNumFocusAreas() > 0) {
                                parameters.setFocusAreas(null);
                            }
                            camera.setParameters(parameters);
                            camera.startPreview();
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }
});
Dude
  • 1,202
  • 21
  • 30
  • 4
    Any idea how we are suppose to support multiple devices when every device acts differently? – bcorso Aug 15 '14 at 19:27
  • 1
    Good question, I wish I had the answer... I guess I'd say test on as many devices as possible... – Dude Aug 18 '14 at 10:04
  • 6
    calculateTapArea() is undefined. please explain – Qadir Hussain Jul 01 '15 at 05:59
  • 1
    @bcorso there are a few libraries around, like the ones by Commonsware, that at least provide a bit of work towards device specific issues. The only way to be sure is the old fashioned "test on everything you can" though. – Kane O'Riley Aug 19 '15 at 06:02
  • 1
    @QadirHussain `calculateTapArea()` is just a method that converts the touch position x:y to a rectangle with coordinates between -1000:-1000 and 1000:1000([as defined in the docs](http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getFocusAreas%28%29)) – Dude Aug 26 '15 at 11:50
  • if (parameters.getFocusMode().equals( Camera.Parameters.FOCUS_MODE_AUTO) { parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO); } If its already in Auto mode why are we setting it again? – Cyph3rCod3r Oct 29 '18 at 06:32
  • @Dr.aNdRO you are right, there was a typo in that part, a "!" is missing. If you check the edits you'll see that someone actually added it while trying to make the answer clearer. – Dude Oct 30 '18 at 07:48
22

this has the solution. I just added the implementation of some missing methods in his code.

private static  final int FOCUS_AREA_SIZE= 300;

//

mCameraPreview.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                focusOnTouch(event);
            }
            return true;
        }
    });

//

 private void focusOnTouch(MotionEvent event) {
    if (mCamera != null ) {

        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters.getMaxNumMeteringAreas() > 0){
            Log.i(TAG,"fancy !");
            Rect rect = calculateFocusArea(event.getX(), event.getY());

            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
            meteringAreas.add(new Camera.Area(rect, 800));
            parameters.setFocusAreas(meteringAreas);

            mCamera.setParameters(parameters);
            mCamera.autoFocus(mAutoFocusTakePictureCallback);
        }else {
            mCamera.autoFocus(mAutoFocusTakePictureCallback);
        }
    }
}

private Rect calculateFocusArea(float x, float y) {
    int left = clamp(Float.valueOf((x / mCameraPreview.getWidth()) * 2000 - 1000).intValue(), FOCUS_AREA_SIZE);
    int top = clamp(Float.valueOf((y / mCameraPreview.getHeight()) * 2000 - 1000).intValue(), FOCUS_AREA_SIZE);

    return new Rect(left, top, left + FOCUS_AREA_SIZE, top + FOCUS_AREA_SIZE);
}

private int clamp(int touchCoordinateInCameraReper, int focusAreaSize) {
    int result;
    if (Math.abs(touchCoordinateInCameraReper)+focusAreaSize/2>1000){
        if (touchCoordinateInCameraReper>0){
            result = 1000 - focusAreaSize/2;
        } else {
            result = -1000 + focusAreaSize/2;
        }
    } else{
         result = touchCoordinateInCameraReper - focusAreaSize/2;
    }
    return result;
}

// implement this callback to trigger the focus.

private Camera.AutoFocusCallback mAutoFocusTakePictureCallback = new Camera.AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            if (success) {
                // do something...
                Log.i("tap_to_focus","success!");
            } else {
                // do something...
                Log.i("tap_to_focus","fail!");
            }
        }
    };
Community
  • 1
  • 1
ahmed_khan_89
  • 2,755
  • 26
  • 49
  • mAutoFocusTakePictureCallback cannot be resolved to a variable, please explain – Maroti Jul 06 '15 at 13:17
  • mAutoFocusTakePictureCallback is as its name indicates it, an instance of Camera.AutoFocusCallback. I updated my answer. – ahmed_khan_89 Jul 06 '15 at 17:51
  • I tried this solution, but it does not work. :( Where do you cancel auto focus? – Zookey Nov 18 '16 at 18:45
  • what doesn't work exactly? neither the question or the answer is speaking about "canceling the auto focus"...? – ahmed_khan_89 Nov 20 '16 at 15:55
  • I didn't try it. PS: I posted this answer more than two years ago, in an older project. So may be you need to make some changes to get it working with "camera2" . good luck. – ahmed_khan_89 Apr 18 '17 at 12:15
  • Don't forget mCamera.startPreview(); after setting the focus, else it will get stuck after taking a photo. – AEQ Jul 07 '18 at 18:20
2

One of the other answers causes your camera to discard the focus points previously given and return to continuous focus which I don't think makes sense.

Also if you take a look at the link in the post the original answer uses parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);

I've implemented the above with this line of code instead of Continuous focus and it seems to work much better.

Community
  • 1
  • 1
Arjun
  • 322
  • 4
  • 20
  • Please link to the answer.. the list changes dynamically. – AStopher Jun 07 '16 at 17:11
  • Not sure how to do that @cybermonkey (new to stack overflow) but the answer I am referring to is by "Dude" – Arjun Jun 07 '16 at 17:40
  • That's ok, I've submitted a suggested edit on your answer for you :-). You can either accept it now, or wait for the community to approve it. – AStopher Jun 07 '16 at 18:37
0
 binding.cPreview.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mCamera.autoFocus(myAutoFocusCallback);
        }
    });


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

        @Override
        public void onAutoFocus(boolean arg0, Camera arg1) {
            // TODO Auto-generated method stub
        }};
Mohsinali
  • 543
  • 7
  • 14
-1

I have the same issue. I've checked this on many devices, and many Android versions.

It appears that focus-area is not working in CONTINUOUS focus-mode.

My workaround is to set focus-mode to AUTO or MACRO along with setting focus-area:

params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
params.setFocusAreas(focusAreas);
mCamera.setParameters(params);

Please note that the stock Camera app on Galaxy S3 & S4 works the same way: it's permanently in continuous mode. When you touch the screen it switches to auto & sets focus-area. But after a while it comes back to continuous mode, and focus-area goes back to the center of the screen.

I hope this helps you somehow.

Grzegorz D.
  • 1,748
  • 15
  • 18