android 轻触以聚焦摄像头实现

isr3a4wc  于 2023-01-24  发布在  Android
关注(0)|答案(3)|浏览(114)

我正在尝试为我的相机页面实现一个手动对焦功能,这样用户就可以点击来对焦相机。
我在原生Android上使用this StackOverflow question that's currently written in Java,我已经把它转换成C#用于我的Xamarin.Forms Android应用程序。
以下是我目前掌握的情况:

public class CameraPage : PageRenderer, TextureView.ISurfaceTextureListener, Android.Views.View.IOnTouchListener, IAutoFocusCallback
{
    global::Android.Hardware.Camera camera;
    TextureView textureView;

    public void OnAutoFocus(bool success, Android.Hardware.Camera camera)
    {
        var parameters = camera.GetParameters();
        if (parameters.FocusMode != Android.Hardware.Camera.Parameters.FocusModeContinuousPicture)
        {
            parameters.FocusMode = Android.Hardware.Camera.Parameters.FocusModeContinuousPicture;

            if (parameters.MaxNumFocusAreas > 0)
            {
                parameters.FocusAreas = null;
            }
            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(OnAutoFocus); //Here is the issue. How do I use the callback? 
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Console.Write(ex.StackTrace);
            }
            return true;
        }
        return false; 
    }

    private Rect CalculateTapArea(object x, object y, float coefficient)
    {
        var focusAreaSize = 500;
        int areaSize = Java.Lang.Float.ValueOf(focusAreaSize * coefficient).IntValue();

        int left = clamp((int)x - areaSize / 2, 0, textureView.Width - areaSize);
        int top = clamp((int)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;
    }
}

我已经成功地转换了大部分内容,但我不确定如何正确地使用AutoFocusCallback。我应该怎么做才能像上面链接的java答案那样从我的OnTouch事件调用OnAutoFocus?
附加回调后,我需要做的就是将OnTouch事件订阅到我的页面,对吗?
例如,我尝试过:
textureView.Click += OnTouch;,但"OnTouch“没有与委托”EventHandler“匹配的重载。是否需要使用特定的事件处理程序?

knsnq2tg

knsnq2tg1#

你可以尝试改变

camera.AutoFocus(OnAutoFocus);

camera.AutoFocus(this);

并且它将使用OnAutoFocus,因为它是从IAutoFocusCallback实现的。
关于订阅事件的问题,您可以尝试在OnElementChanged中订阅事件,如下所示

protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null || Element == null)
            {
                return;
            }
            try
            {
                this.SetOnTouchListener(this);
            }
            catch (Exception e)
            {

            }

        }

顺便说一句,我没有看到在这个代码中使用TextureView.ISurfaceTextureListener

jvlzgdj9

jvlzgdj92#

在链接的Java答案中所发生的一切就是他们提供了在操作系统调用回调时运行的代码:

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

上面没有“调用”回调,只是提供了回调代码来运行。操作系统调用回调。所以在Xamarin中,你需要传入实现IAutoFocusCallback接口的类型,所以我认为你应该能够做到这一点,因为CameraPage实现了IAutoFocusCallback接口:

camera.AutoFocus(this); // "this" refers to your current CameraPage which implements the interface.

这里的线索是,当你在camera.AutoFocus后面输入左括号时,弹出窗口显示你需要传入一个类型IAutoFocusCallback,这意味着任何实现该接口的类型,所以在本例中是“this”CameraPage。:-)

eqfvzcg8

eqfvzcg83#

因为这里没有完整的例子,这是我的例子。
这个解决方案很好用,至少对我来说是这样。相机会持续对焦,直到一个对焦点被点击。然后它会对焦在点击点上,直到你把相机移开。然后它会回到持续对焦模式。

public class CameraPageRenderer : PageRenderer, TextureView.ISurfaceTextureListener, Android.Hardware.Camera.IPictureCallback, Android.Hardware.Camera.IShutterCallback, IAutoFocusCallback 
{

    // ... code removed for brevity

    /// <summary>
    /// Occurs whenever the user touches the screen. Here we set the focus mode to FocusModeAuto and set a focus area based on the tapped coordinates.
    /// </summary>
    public override bool OnTouchEvent(MotionEvent e)
    {
        var parameters = camera.GetParameters();

        parameters.FocusMode = Camera.Parameters.FocusModeAuto;

        if (parameters.MaxNumFocusAreas > 0)
        {
            var focusRect = CalculateTapArea(e.GetX(), e.GetY(), textureView.Width, textureView.Height, 50f);

            parameters.FocusAreas = new List<Area>()
            {
                new Area(focusRect, 1000)
            };
        }

        try
        {
            camera.CancelAutoFocus();
            camera.SetParameters(parameters);
            camera.AutoFocus(this);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }

        return true;
    }

    /// <summary>
    /// Auto focus callback. Here we reset the focus mode to FocusModeContinuousPicture and remove any focus areas
    /// </summary>
    public void OnAutoFocus(bool success, Camera camera)
    {
        var parameters = camera.GetParameters();

        parameters.FocusMode = Parameters.FocusModeContinuousPicture;

        if (parameters.MaxNumFocusAreas > 0)
        {
            parameters.FocusAreas = null;
        }

        camera.SetParameters(parameters);
    }

    /// <summary>
    /// Calculates a tap area using the focus coordinates mentioned in <see href="https://developer.android.com/reference/android/hardware/Camera.Parameters.html#getFocusAreas()"/>
    /// <para>
    /// Coordinates of the rectangle range from -1000 to 1000. (-1000, -1000) is the upper left point. (1000, 1000) is the lower right point. The width and height of focus areas cannot be 0 or negative.</para>
    /// </summary>
    /// <param name="x">The X coordinate of the tapped area</param>
    /// <param name="y">The Y coordinate of the tapped area</param>
    /// <param name="width">The total width of the tappable area</param>
    /// <param name="height">The total height of the tappable area</param>
    /// <param name="focusAreaSize">The desired size (widht, height) of the created rectangle</param>
    /// <returns></returns>
    private Rect CalculateTapArea(float x, float y, float width, float height, float focusAreaSize)
    {
        var leftFloat = x * 2000 / width - 1000;
        var topFloat = y * 2000 / height - 1000;

        var left = RoundFocusCoordinate(leftFloat);
        var top = RoundFocusCoordinate(topFloat);
        var right = RoundFocusCoordinate(leftFloat + focusAreaSize);
        var bottom = RoundFocusCoordinate(topFloat + focusAreaSize);

        return new Rect(left, top, right, bottom);
    }

    /// <summary>
    /// Round, convert to int, and clamp between -1000 and 1000
    /// </summary>
    private int RoundFocusCoordinate(float value)
    {
        var intValue = (int)Math.Round(value, 0, MidpointRounding.AwayFromZero);
        return Math.Clamp(intValue, -1000, 1000);
    }

    // ... code removed for brevity

}

相关问题