Reputation: 2568
I want to display a basic circle when the user focuses manually (tap to focus) for the camera page in my app.
I already have autofocus implemented below on tap but I'm not sure how to draw the circle on focus and dismiss it when the view becomes unfocused as well keep redrawing it when the camera is focused. The indicator should not be part of the final photo, just as a guide to the user when the camera is focused or not.
Here's what I have so far:
public class AutoFocusCallback : Java.Lang.Object, IAutoFocusCallback
{
public void OnAutoFocus(bool success, Android.Hardware.Camera camera)
{
var parameters = camera.GetParameters();
var supportedFocusModes = parameters.SupportedFocusModes;
if (string.IsNullOrEmpty(parameters.FocusMode))
{
string focusModeContinuous = Android.Hardware.Camera.Parameters.FocusModeContinuousPicture;
string focusModeAuto = Android.Hardware.Camera.Parameters.FocusModeAuto;
if (supportedFocusModes != null && supportedFocusModes.Any())
{
if (supportedFocusModes.Contains(focusModeContinuous))
{
parameters.FocusMode = focusModeContinuous;
}
else if (supportedFocusModes.Contains(focusModeAuto))
{
parameters.FocusMode = focusModeAuto;
}
}
}
if (supportedFocusModes != null && supportedFocusModes.Any())
{
if (parameters.MaxNumFocusAreas > 0)
{
parameters.FocusAreas = null;
}
if (success)
{
CameraPage cameraPage = new CameraPage();
Canvas canvas = new Canvas();
cameraPage.Draw(canvas);
}
camera.SetParameters(parameters);
camera.StartPreview();
}
}
}
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
if (camera != null)
{
var parameters = camera.GetParameters();
camera.CancelAutoFocus();
Rect focusRect = CalculateTapArea(e.GetX(), e.GetY(), 1f);
if (parameters.FocusMode != Android.Hardware.Camera.Parameters.FocusModeAuto)
{
parameters.FocusMode = Android.Hardware.Camera.Parameters.FocusModeAuto;
}
if (parameters.MaxNumFocusAreas > 0)
{
List<Area> mylist = new List<Area>();
mylist.Add(new Android.Hardware.Camera.Area(focusRect, 1000));
parameters.FocusAreas = mylist;
}
try
{
camera.CancelAutoFocus();
camera.SetParameters(parameters);
camera.StartPreview();
camera.AutoFocus(new AutoFocusCallback());
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
Console.Write(ex.StackTrace);
}
return true;
}
else
{
return false;
}
}
private Rect CalculateTapArea(object x, object y, float coefficient)
{
var focusAreaSize = Math.Max(textureView.Width, textureView.Height) / 8; //Recommended focus area size from the manufacture is 1/8 of the image
int areaSize = focusAreaSize * (int)coefficient;
int left = clamp(Convert.ToInt32(x) - areaSize / 2, 0, textureView.Width - areaSize);
int top = clamp(Convert.ToInt32(y) - areaSize / 2, 0, textureView.Height - areaSize);
RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
Matrix.MapRect(rectF);
return new Rect((int)System.Math.Round(rectF.Left), (int)System.Math.Round(rectF.Top), (int)System.Math.Round(rectF.Right), (int)System.Math.Round(rectF.Bottom));
}
private int clamp(int x, int min, int max)
{
if (x > max)
{
return max;
}
if (x < min)
{
return min;
}
return x;
}
public override void Draw(Canvas canvas)
{
base.Draw(canvas);
Paint p = new Paint();
p.Color = Android.Graphics.Color.White;
p.SetStyle(Paint.Style.Stroke);
p.StrokeWidth = 3;
canvas.DrawCircle(300, 300, 100, p);
}
E.g. like in this picture:
This is for my Xamarin.Forms Android app.
EDIT: Here's a copy of the Android Source code for the camera but not sure where they're making the indicator:
Similar SO posts with no official answers:
How to implement tap to focus indicator in android camera?
Implement indicator on tap to focus in camera [android]
Android Camera2 - Draw circle focus area
As you can see, multiple people have the same question but no answers! Please let me know if I'm still being unclear?
Upvotes: 3
Views: 1873
Reputation: 16652
I already have autofocus implemented below on tap but I'm not sure how to draw the circle on focus and dismiss it when the view becomes unfocused as well keep redrawing it when the camera is focused.
The picture you posted looks like a system camera to me, if using Intent
to launch the system camera, I'm not sure it is possible to add a focus circle on the system's camera view. But I guess you possibly used a SurfaceView
or TextureView
to host camera by yourself.
If so, for your scenario, I think the most simply method is to place a ImageView
in your layout and change its Visibility and reset its LayoutParameters
according to the camera focus state. The image source need to be a transparent one for example I used this one. Then my layout is like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView android:id="@+id/textureView"
android:layout_height="match_parent"
android:layout_width="match_parent" />
<Button android:id="@+id/take_photo"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="@string/takephoto" />
<ImageView android:id="@+id/focuscircle"
android:layout_height="80dp"
android:layout_width="80dp"
android:layout_centerInParent="true"
android:src="@drawable/FocusCircle"
android:visibility="invisible" />
</RelativeLayout>
I've modified your code a little bit in order to make it meet my scenario using TextureView
. here is the code to make the image visible when tap the screen:
private void _textureView_Touch(object sender, View.TouchEventArgs e)
{
if (_camera != null)
{
var parameters = _camera.GetParameters();
_camera.CancelAutoFocus();
Rect focusRect = CalculateTapArea(e.Event.GetX(), e.Event.GetY(), 1f);
if (parameters.FocusMode != Android.Hardware.Camera.Parameters.FocusModeAuto)
{
parameters.FocusMode = Android.Hardware.Camera.Parameters.FocusModeAuto;
}
if (parameters.MaxNumFocusAreas > 0)
{
List<Area> mylist = new List<Area>();
mylist.Add(new Android.Hardware.Camera.Area(focusRect, 1000));
parameters.FocusAreas = mylist;
}
try
{
_camera.CancelAutoFocus();
_camera.SetParameters(parameters);
_camera.StartPreview();
_camera.AutoFocus(new AutoFocusCallBack());
MarginLayoutParams margin = new MarginLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent,
ViewGroup.LayoutParams.WrapContent));
margin.SetMargins(focusRect.Left, focusRect.Top,
focusRect.Right, focusRect.Bottom);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(margin);
layoutParams.Height = 200;
layoutParams.Width = 200;
_focusimg.LayoutParameters = layoutParams;
_focusimg.Visibility = ViewStates.Visible;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
Console.Write(ex.StackTrace);
}
//return true;
}
else
{
//return false;
}
}
And to make it disappear you can code in the success
state in AutoFocusCallBack
like this:
if (success)
{
Task.Delay(1000);
Activity1._focusimg.Visibility = ViewStates.Invisible;
}
In order to make the ImageView
accessible from AutoFocusCallBack
, you can make it a static one in xamarin:
private Android.Hardware.Camera _camera;
private TextureView _textureView;
public static ImageView _focusimg;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
_textureView = (TextureView)this.FindViewById(Resource.Id.textureView);
_textureView.SurfaceTextureListener = this;
_textureView.Touch += _textureView_Touch;
var tpBtn = (Button)this.FindViewById(Resource.Id.take_photo);
tpBtn.Click += TpBtn_Click;
_focusimg = (ImageView)this.FindViewById(Resource.Id.focuscircle);
}
This works fine every time when it successfully set focus, but I found that sometimes when I tapped on TextureView
, it will not fire the _textureView_Touch
event, I didn't deep dig this problem.
Another method I thought was to draw a circle dynamically on Canvas
like you did in your code, it also worked, but I found my demo responses slow using this method, and I also didn't deep dig this issue. Anyway, I think the first method using a Image is simpler, if a demo is needed, please leave a comment.
Upvotes: 1