相机聚焦时如何实现可视指示

时间:2016-12-07 19:16:26

标签: c# android xamarin xamarin.android android-camera

当用户在我的应用中手动对焦(点按对焦)相机页面时,我想显示一个基本圆圈。

我已经在下面实现了自动对焦,但是我不确定如何在焦点上绘制圆圈并在视图变得不聚焦时将其关闭,并在相机聚焦时保持重绘。指示器不应该是最终照片的一部分,只是作为相机聚焦或不聚焦时用户的指南。

这是我到目前为止所拥有的:

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。就像在这张图片中一样:

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帖子没有官方答案:

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

正如您所看到的,多人有相同的问题,但没有答案!如果我还不清楚,请告诉我?

1 个答案:

答案 0 :(得分:1)

  

我已经在下面实现了自动对焦,但是我不确定如何在焦点上绘制圆圈并在视图变得不聚焦时将其关闭,并在相机聚焦时保持重绘。

您发布的图片对我来说就像系统相机一样,如果使用Intent启动系统相机,我不确定是否可以在系统上添加一个焦点圆圈&#39; s相机视图。但我想您可能已经使用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;
    }
}

要让它消失,您可以像success这样在AutoFocusCallBack状态进行编码:

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上动态绘制圆圈,就像在代码中一样,它也有效,但我发现我的演示响应使用这种方法很慢,而且我也没有深入挖掘这个问题。无论如何,我认为使用Image的第一种方法更简单,如果需要演示,请发表评论。