如何在 Xamarin.Android 中更改 Surface View 高度?
How to change Surface View Height in Xamarin.Android?
我的布局代码。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:showIn="@layout/Main">
<LinearLayout
android:id="@+id/camera_ll"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:weightSum="100"
android:orientation="vertical">
<FrameLayout
android:layout_height="0dp"
android:layout_width="match_parent"
android:id="@+id/frameLayout"
android:layout_weight="90" />
<Button
android:layout_height="0dp"
android:layout_width="match_parent"
android:id="@+id/btn_capture"
android:text="Scan Answer Sheet"
fontPath="Fonts/Verb-Semibold.otf"
android:textColor="@android:color/white"
android:background="@drawable/bttn_green"
android:layout_weight="10" />
</LinearLayout>
</RelativeLayout>
我的 Java 代码是这样的。
public class CameraPreview : Activity, Android.Hardware.Camera.IPictureCallback, Android.Hardware.Camera.IPreviewCallback, Android.Hardware.Camera.IShutterCallback
{
String PICTURE_FILENAME = "picture.jpg";
Preview mPreview;
Camera mCamera;
int numberOfCameras;
int cameraCurrentlyLocked;
// The first rear facing camera
int defaultCameraId;
FrameLayout frameLayout;
Button btn_capture;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Hide the window title and go fullscreen.
RequestWindowFeature(WindowFeatures.NoTitle);
Window.AddFlags(WindowManagerFlags.Fullscreen);
// Create our Preview view and set it as the content of our activity.
SetContentView(Resource.Layout.CameraPriview);
frameLayout = FindViewById<FrameLayout>(Resource.Id.frameLayout);
btn_capture = FindViewById<Button>(Resource.Id.btn_capture);
mPreview = new Preview(this, frameLayout);
frameLayout.AddView(mPreview);
btn_capture.Click += TakeAPicture;
// Find the total number of cameras available
numberOfCameras = Camera.NumberOfCameras;
// Find the ID of the default camera
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < numberOfCameras; i++)
{
Camera.GetCameraInfo(i, cameraInfo);
if (cameraInfo.Facing == CameraFacing.Back)
{
defaultCameraId = i;
}
}
}
private void TakeAPicture(object sender, EventArgs eventArgs)
{
Android.Hardware.Camera.Parameters p = mCamera.GetParameters();
p.PictureFormat = Android.Graphics.ImageFormatType.Jpeg;
mCamera.SetParameters(p);
mCamera.TakePicture(this, this, this);
}
void Camera.IPictureCallback.OnPictureTaken(byte[] data, Android.Hardware.Camera camera)
{
FileOutputStream outStream = null;
File dataDir = Android.OS.Environment.ExternalStorageDirectory;
if (data != null)
{
try
{
outStream = new FileOutputStream(dataDir + "/" + PICTURE_FILENAME);
outStream.Write(data);
outStream.Close();
}
catch (FileNotFoundException e)
{
System.Console.Out.WriteLine(e.Message);
}
catch (IOException ie)
{
System.Console.Out.WriteLine(ie.Message);
}
}
}
void Camera.IPreviewCallback.OnPreviewFrame(byte[] b, Android.Hardware.Camera c)
{
}
void Camera.IShutterCallback.OnShutter()
{
}
protected override void OnResume()
{
base.OnResume();
// Open the default i.e. the first rear facing camera.
mCamera = Camera.Open();
cameraCurrentlyLocked = defaultCameraId;
mPreview.PreviewCamera = mCamera;
mCamera.SetDisplayOrientation(90);
}
protected override void OnPause()
{
base.OnPause();
// Because the Camera object is a shared resource, it's very
// important to release it when the activity is paused.
if (mCamera != null)
{
mPreview.PreviewCamera = null;
mCamera.Release();
mCamera = null;
}
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
// Inflate our menu which can gather user input for switching camera
//MenuInflater.Inflate(Resource.Menu.camera_menu, menu);
return true;
}
}
表面视图代码。
class Preview : ViewGroup, ISurfaceHolderCallback
{
string TAG = "Preview";
FrameLayout f;
SurfaceView mSurfaceView;
ISurfaceHolder mHolder;
Camera.Size mPreviewSize;
IList<Camera.Size> mSupportedPreviewSizes;
Camera _camera;
public Camera PreviewCamera
{
get { return _camera; }
set
{
_camera = value;
if (_camera != null)
{
mSupportedPreviewSizes = PreviewCamera.GetParameters().SupportedPreviewSizes;
RequestLayout();
}
}
}
public Preview(Context context, FrameLayout f) : base(context)
{
this.f = f;
mSurfaceView = new SurfaceView(context);
AddView(mSurfaceView);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.Holder;
mHolder.AddCallback(this);
mHolder.SetType(SurfaceType.PushBuffers);
}
public void SwitchCamera(Camera camera)
{
PreviewCamera = camera;
try
{
camera.SetPreviewDisplay(mHolder);
}
catch (Java.IO.IOException exception)
{
Log.Error(TAG, "IOException caused by setPreviewDisplay()", exception);
}
Camera.Parameters parameters = camera.GetParameters();
parameters.SetPreviewSize(mPreviewSize.Width, mPreviewSize.Height);
RequestLayout();
camera.SetParameters(parameters);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// We purposely disregard child measurements because act as a
// wrapper to a SurfaceView that centers the camera preview instead
// of stretching it.
int width = ResolveSize(SuggestedMinimumWidth, widthMeasureSpec);
int height = ResolveSize(SuggestedMinimumHeight, heightMeasureSpec);
SetMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null)
{
mPreviewSize = GetOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (changed && ChildCount > 0)
{
View child = GetChildAt(0);
int width = r - l;
int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null)
{
previewWidth = mPreviewSize.Width;
previewHeight = mPreviewSize.Height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight > height * previewWidth)
{
int scaledChildWidth = previewWidth * height / previewHeight;
child.Layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
}
else
{
int scaledChildHeight = previewHeight * width / previewWidth;
child.Layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
public void SurfaceCreated(ISurfaceHolder holder)
{
// The Surface has been created, acquire the camera and tell it where
// to draw.
try
{
if (PreviewCamera != null)
{
PreviewCamera.SetPreviewDisplay(holder);
}
}
catch (Java.IO.IOException exception)
{
Log.Error(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
// Surface will be destroyed when we return, so stop the preview.
if (PreviewCamera != null)
{
PreviewCamera.StopPreview();
}
}
private Camera.Size GetOptimalPreviewSize(IList<Camera.Size> sizes, int w, int h)
{
const double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double)w / h;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MaxValue;
int targetHeight = h;
// Try to find an size match aspect ratio and size
foreach (Camera.Size size in sizes)
{
double ratio = (double)size.Width / size.Height;
if (Math.Abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null)
{
minDiff = Double.MaxValue;
foreach (Camera.Size size in sizes)
{
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
}
return optimalSize;
}
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int w, int h)
{
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = PreviewCamera.GetParameters();
parameters.SetPreviewSize(mPreviewSize.Width, mPreviewSize.Height);
RequestLayout();
PreviewCamera.SetParameters(parameters);
PreviewCamera.StartPreview();
}
}
输出:
现在我想增加相机高度以填充所有可用的space,但对我没有任何作用。如果有人知道解决方案请帮助谢谢。
Now i want to increase the camera height to fill all available space but nothing working for me. If anyone know the solution please help thanks.
预览尺寸的设置代码在Preview
的OnLayout
方法中:
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (changed && ChildCount > 0)
{
View child = GetChildAt(0);
int width = r - l;
int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null)
{
previewWidth = mPreviewSize.Width;
previewHeight = mPreviewSize.Height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight > height * previewWidth)
{
int scaledChildWidth = previewWidth * height / previewHeight;
child.Layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
}
else
{
int scaledChildHeight = previewHeight * width / previewWidth;
child.Layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
这里我不明白代码的意思。但是通过 child.Layout(0, (height - scaledChildHeight) / 2,width, (height + scaledChildHeight) / 2);
,您正在将预览 window 的顶部边界和底部边界减小到 (height - scaledChildHeight) / 2
和 (height + scaledChildHeight) / 2)
。
因此,要增加相机高度以填充所有可用的 space,您需要修改此代码段。
例如:
int LeftBoundary=0,TopBoundary=0;
int RightBoundary=r,BottomBoundary=b;
child.Layout(LeftBoundary,TopBoundary,RightBoundary,BottomBoundary));
我的布局代码。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:showIn="@layout/Main">
<LinearLayout
android:id="@+id/camera_ll"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:weightSum="100"
android:orientation="vertical">
<FrameLayout
android:layout_height="0dp"
android:layout_width="match_parent"
android:id="@+id/frameLayout"
android:layout_weight="90" />
<Button
android:layout_height="0dp"
android:layout_width="match_parent"
android:id="@+id/btn_capture"
android:text="Scan Answer Sheet"
fontPath="Fonts/Verb-Semibold.otf"
android:textColor="@android:color/white"
android:background="@drawable/bttn_green"
android:layout_weight="10" />
</LinearLayout>
</RelativeLayout>
我的 Java 代码是这样的。
public class CameraPreview : Activity, Android.Hardware.Camera.IPictureCallback, Android.Hardware.Camera.IPreviewCallback, Android.Hardware.Camera.IShutterCallback
{
String PICTURE_FILENAME = "picture.jpg";
Preview mPreview;
Camera mCamera;
int numberOfCameras;
int cameraCurrentlyLocked;
// The first rear facing camera
int defaultCameraId;
FrameLayout frameLayout;
Button btn_capture;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Hide the window title and go fullscreen.
RequestWindowFeature(WindowFeatures.NoTitle);
Window.AddFlags(WindowManagerFlags.Fullscreen);
// Create our Preview view and set it as the content of our activity.
SetContentView(Resource.Layout.CameraPriview);
frameLayout = FindViewById<FrameLayout>(Resource.Id.frameLayout);
btn_capture = FindViewById<Button>(Resource.Id.btn_capture);
mPreview = new Preview(this, frameLayout);
frameLayout.AddView(mPreview);
btn_capture.Click += TakeAPicture;
// Find the total number of cameras available
numberOfCameras = Camera.NumberOfCameras;
// Find the ID of the default camera
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < numberOfCameras; i++)
{
Camera.GetCameraInfo(i, cameraInfo);
if (cameraInfo.Facing == CameraFacing.Back)
{
defaultCameraId = i;
}
}
}
private void TakeAPicture(object sender, EventArgs eventArgs)
{
Android.Hardware.Camera.Parameters p = mCamera.GetParameters();
p.PictureFormat = Android.Graphics.ImageFormatType.Jpeg;
mCamera.SetParameters(p);
mCamera.TakePicture(this, this, this);
}
void Camera.IPictureCallback.OnPictureTaken(byte[] data, Android.Hardware.Camera camera)
{
FileOutputStream outStream = null;
File dataDir = Android.OS.Environment.ExternalStorageDirectory;
if (data != null)
{
try
{
outStream = new FileOutputStream(dataDir + "/" + PICTURE_FILENAME);
outStream.Write(data);
outStream.Close();
}
catch (FileNotFoundException e)
{
System.Console.Out.WriteLine(e.Message);
}
catch (IOException ie)
{
System.Console.Out.WriteLine(ie.Message);
}
}
}
void Camera.IPreviewCallback.OnPreviewFrame(byte[] b, Android.Hardware.Camera c)
{
}
void Camera.IShutterCallback.OnShutter()
{
}
protected override void OnResume()
{
base.OnResume();
// Open the default i.e. the first rear facing camera.
mCamera = Camera.Open();
cameraCurrentlyLocked = defaultCameraId;
mPreview.PreviewCamera = mCamera;
mCamera.SetDisplayOrientation(90);
}
protected override void OnPause()
{
base.OnPause();
// Because the Camera object is a shared resource, it's very
// important to release it when the activity is paused.
if (mCamera != null)
{
mPreview.PreviewCamera = null;
mCamera.Release();
mCamera = null;
}
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
// Inflate our menu which can gather user input for switching camera
//MenuInflater.Inflate(Resource.Menu.camera_menu, menu);
return true;
}
}
表面视图代码。
class Preview : ViewGroup, ISurfaceHolderCallback
{
string TAG = "Preview";
FrameLayout f;
SurfaceView mSurfaceView;
ISurfaceHolder mHolder;
Camera.Size mPreviewSize;
IList<Camera.Size> mSupportedPreviewSizes;
Camera _camera;
public Camera PreviewCamera
{
get { return _camera; }
set
{
_camera = value;
if (_camera != null)
{
mSupportedPreviewSizes = PreviewCamera.GetParameters().SupportedPreviewSizes;
RequestLayout();
}
}
}
public Preview(Context context, FrameLayout f) : base(context)
{
this.f = f;
mSurfaceView = new SurfaceView(context);
AddView(mSurfaceView);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.Holder;
mHolder.AddCallback(this);
mHolder.SetType(SurfaceType.PushBuffers);
}
public void SwitchCamera(Camera camera)
{
PreviewCamera = camera;
try
{
camera.SetPreviewDisplay(mHolder);
}
catch (Java.IO.IOException exception)
{
Log.Error(TAG, "IOException caused by setPreviewDisplay()", exception);
}
Camera.Parameters parameters = camera.GetParameters();
parameters.SetPreviewSize(mPreviewSize.Width, mPreviewSize.Height);
RequestLayout();
camera.SetParameters(parameters);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// We purposely disregard child measurements because act as a
// wrapper to a SurfaceView that centers the camera preview instead
// of stretching it.
int width = ResolveSize(SuggestedMinimumWidth, widthMeasureSpec);
int height = ResolveSize(SuggestedMinimumHeight, heightMeasureSpec);
SetMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null)
{
mPreviewSize = GetOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (changed && ChildCount > 0)
{
View child = GetChildAt(0);
int width = r - l;
int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null)
{
previewWidth = mPreviewSize.Width;
previewHeight = mPreviewSize.Height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight > height * previewWidth)
{
int scaledChildWidth = previewWidth * height / previewHeight;
child.Layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
}
else
{
int scaledChildHeight = previewHeight * width / previewWidth;
child.Layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
public void SurfaceCreated(ISurfaceHolder holder)
{
// The Surface has been created, acquire the camera and tell it where
// to draw.
try
{
if (PreviewCamera != null)
{
PreviewCamera.SetPreviewDisplay(holder);
}
}
catch (Java.IO.IOException exception)
{
Log.Error(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
// Surface will be destroyed when we return, so stop the preview.
if (PreviewCamera != null)
{
PreviewCamera.StopPreview();
}
}
private Camera.Size GetOptimalPreviewSize(IList<Camera.Size> sizes, int w, int h)
{
const double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double)w / h;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MaxValue;
int targetHeight = h;
// Try to find an size match aspect ratio and size
foreach (Camera.Size size in sizes)
{
double ratio = (double)size.Width / size.Height;
if (Math.Abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null)
{
minDiff = Double.MaxValue;
foreach (Camera.Size size in sizes)
{
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
}
return optimalSize;
}
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int w, int h)
{
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = PreviewCamera.GetParameters();
parameters.SetPreviewSize(mPreviewSize.Width, mPreviewSize.Height);
RequestLayout();
PreviewCamera.SetParameters(parameters);
PreviewCamera.StartPreview();
}
}
输出:
现在我想增加相机高度以填充所有可用的space,但对我没有任何作用。如果有人知道解决方案请帮助谢谢。
Now i want to increase the camera height to fill all available space but nothing working for me. If anyone know the solution please help thanks.
预览尺寸的设置代码在Preview
的OnLayout
方法中:
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (changed && ChildCount > 0)
{
View child = GetChildAt(0);
int width = r - l;
int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null)
{
previewWidth = mPreviewSize.Width;
previewHeight = mPreviewSize.Height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight > height * previewWidth)
{
int scaledChildWidth = previewWidth * height / previewHeight;
child.Layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
}
else
{
int scaledChildHeight = previewHeight * width / previewWidth;
child.Layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
这里我不明白代码的意思。但是通过 child.Layout(0, (height - scaledChildHeight) / 2,width, (height + scaledChildHeight) / 2);
,您正在将预览 window 的顶部边界和底部边界减小到 (height - scaledChildHeight) / 2
和 (height + scaledChildHeight) / 2)
。
因此,要增加相机高度以填充所有可用的 space,您需要修改此代码段。 例如:
int LeftBoundary=0,TopBoundary=0;
int RightBoundary=r,BottomBoundary=b;
child.Layout(LeftBoundary,TopBoundary,RightBoundary,BottomBoundary));