如何在相机对焦时实现视觉指示器

3

我希望在我的应用程序相机页面中,当用户手动对焦(轻触对焦)时显示一个基本的圆形。

我已经实现了下面的自动对焦,但我不知道如何在焦点上绘制圆圈,并在视图变得无焦点时将其取消,同时在相机对焦时保持重新绘制。指示器不应是最终照片的一部分,只是在相机聚焦或未聚焦时向用户提供指导。

这是我目前的代码:

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);
}

例如,就像这张图片中所示:

enter image description here

这是为我的Xamarin.Forms Android应用程序设计的。

编辑:这里提供了相机的Android源代码,但不确定他们在哪里制作指示器:

https://android.googlesource.com/platform/packages/apps/Camera.git/+/refs/heads/marshmallow-release/src/com/android/camera

类似的SO帖子没有官方答案:

如何在Android相机中实现点击对焦指示器?

在相机[Android]上实现点击对焦指示器

Android Camera2-绘制圆形对焦区域

正如您所看到的,有很多人有同样的问题,但没有答案! 如果我还没讲清楚,请告诉我吧。


有人能告诉我为什么我表达不清吗?我会进一步编辑,但我认为我已经很清楚了。我甚至添加了一个我想要绘制的视觉指示器的照片。 - Euridice01
1
我认为这是一个可以接受的问题,尽管在Stack Overflow上的人可能有些刻薄。 - jdmdevdotnet
我也觉得看起来不错,而且完全有效。 - Basic
1个回答

1
我已经在点击时实现了自动对焦,但是我不知道如何在聚焦时绘制圆圈,并在视图失去焦点时将其取消,同时在相机聚焦时保持重新绘制它。
您发布的图片看起来像是系统相机。如果使用 Intent 启动系统相机,则我不确定是否可以在系统相机视图上添加聚焦圆圈。但是我猜您可能使用了 SurfaceViewTextureView 自己托管相机。
如果是这样的话,针对您的场景,我认为最简单的方法是在布局中放置一个 ImageView,并根据相机聚焦状态更改其可见性和重置其 LayoutParameters。图片源需要是透明的,例如我使用了 this one。然后我的布局如下:
<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>

我稍微修改了您的代码,以便在我的场景中使用TextureView。以下是使图像在屏幕上轻触时可见的代码:

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;
    }
}

如果想让它消失,你可以在AutoFocusCallBack中编写success状态的代码,像这样:

if (success)
{
    Task.Delay(1000);
    Activity1._focusimg.Visibility = ViewStates.Invisible;
}

为了使ImageView可以从AutoFocusCallBack访问,您可以在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);
}

每次成功设置焦点时,这个方法都能正常工作,但是我发现有时当我点击TextureView时,它不会触发_textureView_Touch事件,我没有深入研究这个问题。
另一种方法是在Canvas上动态绘制一个圆形,就像你在代码中所做的那样,这也可以工作,但我发现我的演示使用这种方法响应速度较慢,而且我也没有深入研究这个问题。无论如何,我认为使用图像的第一种方法更简单,如果需要演示,请留言。

聪明人想得一样!昨天我也做了同样的事情来解决这个问题。我可以分享我的解决方案(虽然可能没有你的准确)。我很想听听你的反馈。稍后我会分享一下 :) - Euridice01
Grace,这部分内容:“有时候当我点击TextureView时,它不会触发_textureView_Touch事件,我没有深入研究这个问题”。你有任何想法为什么会出现这种情况吗? - Euridice01
@Euridice01,不知道,如果我们将“Image”设置为“ViewStates.Invisible”,它仍然会占用空间,也许我点击了图片...也许我们可以尝试将其更改为“ViewStates.Gone”。 - Grace Feng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接