如何使用 Android API 将相机预览大小设置为全屏?
Posted
技术标签:
【中文标题】如何使用 Android API 将相机预览大小设置为全屏?【英文标题】:How can I set the camera preview size to fullscreen with Android API? 【发布时间】:2011-11-29 14:04:50 【问题描述】:我对 android 开发非常陌生,我正在尝试设置一个简单的相机应用程序。到目前为止,我有一个可以正常工作的相机应用程序,它在菜单中有“切换相机”和“拍照”按钮,它们工作正常。
我遇到的唯一问题是我想弄清楚如何让显示器全屏显示。目前,摄像头只出现在屏幕的正中间,仅占屏幕的 1/4 左右。
MainActivity 代码
package assist.core;
import android.app.Activity;
import android.app.AlertDialog;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.hardware.Camera.CameraInfo;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.util.Log;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainActivity extends Activity
private final String TAG = "MainActivity";
private Preview mPreview;
Camera mCamera;
int numberOfCameras;
int cameraCurrentlyLocked;
//The first rear facing camera
int defaultCameraId;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
//Hide the window title.
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
//Create a RelativeLayout container that will hold a SurfaceView,
//and set it as the content of our activity.
mPreview = new Preview(this);
setContentView(mPreview);
//Find the total number of cameras available
numberOfCameras = Camera.getNumberOfCameras();
//Find the ID of the default camera
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++)
Camera.getCameraInfo(i, cameraInfo);
if(cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK)
defaultCameraId = i;
@Override
protected void onResume()
super.onResume();
//Open the default i.e. the first rear facing camera.
mCamera = Camera.open();
cameraCurrentlyLocked = defaultCameraId;
mPreview.setCamera(mCamera);
@Override
protected void onPause()
super.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.setCamera(null);
mCamera.release();
mCamera = null;
@Override
public boolean onCreateOptionsMenu(Menu menu)
//Inflate our menu which can gather user input for switching camera
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.camera_menu, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
//Handle item selection
switch (item.getItemId())
case R.id.switchCam:
//Check for availability of multiple cameras
if (numberOfCameras == 1)
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(this.getString(R.string.camera_alert)).setNeutralButton("Close", null);
AlertDialog alert = builder.create();
alert.show();
return true;
//OK, we have multiple cameras.
//Release this camera -> cameraCurrentlyLocked
if (mCamera != null)
mCamera.stopPreview();
mPreview.setCamera(null);
mCamera.release();
mCamera = null;
//Acquire the next camera and request Preview to reconfigure parameters.
mCamera = Camera.open((cameraCurrentlyLocked + 1) % numberOfCameras);
cameraCurrentlyLocked = (cameraCurrentlyLocked + 1) % numberOfCameras;
mPreview.switchCamera(mCamera);
//Start the preview
mCamera.startPreview();
return true;
case R.id.takePicture:
mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
return true;
default:
return super.onOptionsItemSelected(item);
/**
* Called when shutter is opened
*/
ShutterCallback shutterCallback = new ShutterCallback()
public void onShutter()
;
/**
* Handles data for raw picture when the picture is taken
*/
PictureCallback rawCallback = new PictureCallback()
public void onPictureTaken(byte[] data, Camera camera)
;
/**
* Handles data for jpeg picture when the picture is taken
*/
PictureCallback jpegCallback = new PictureCallback()
public void onPictureTaken(byte[] data, Camera camera)
FileOutputStream outStream = null;
try
// Write to SD Card
outStream = new FileOutputStream(String.format("/sdcard/%d.jpg",
System.currentTimeMillis()));
outStream.write(data);
outStream.close();
catch (FileNotFoundException e)
Log.e(TAG, "IOException caused by PictureCallback()", e);
catch (IOException e)
Log.e(TAG, "IOException caused by PictureCallback()", e);
;
预览课程代码
package assist.core;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import java.io.IOException;
/**
*
* @author cmetrolis
*/
class Preview extends ViewGroup implements SurfaceHolder.Callback
private final String TAG = "Preview";
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
Preview(Context context)
super(context);
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
//Install a SurfaceHolder.Callback so we get notified when the
//underlying surface is created and destroyed.
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
public void setCamera(Camera camera)
mCamera = camera;
if(mCamera != null)
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
requestLayout();
public void switchCamera(Camera camera)
setCamera(camera);
try
camera.setPreviewDisplay(mHolder);
catch (IOException exception)
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
camera.setParameters(parameters);
/**
* Called to determine the size requirements for this view and all of its children.
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected 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.
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if(mSupportedPreviewSizes != null)
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
/**
* Called when this view should assign a size and position to all of its children.
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
if(changed && getChildCount() > 0)
final View child = getChildAt(0);
final int width = r - l;
final 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)
final int scaledChildWidth = previewWidth * height / previewHeight;
child.layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height);
else
final int scaledChildHeight = previewHeight * width / previewWidth;
child.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2);
/**
* This is called immediately after the surface is first created
* @param holder
*/
public void surfaceCreated(SurfaceHolder holder)
try
if(mCamera != null)
mCamera.setPreviewDisplay(holder);
catch (IOException e)
Log.e(TAG, "IOException caused by setPreviewDisplay()", e);
/**
* This is called immediately before a surface is being destroyed
* @param holder
*/
public void surfaceDestroyed(SurfaceHolder holder)
//Surface will be destroyed when we return, so stop the preview.
if(mCamera != null)
mCamera.stopPreview();
mCamera.release();
/**
* This is called immediately after any structural changes (format or size) have been made to the surface
* @param holder
* @param format
* @param w
* @param h
*/
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setDisplayOrientation(90);
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
/**
* Returns the best preview size
* @param sizes
* @param w
* @param h
* @return Size
*/
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h)
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for(Size size : 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.MAX_VALUE;
for (Size size : sizes)
if(Math.abs(size.height - targetHeight) < minDiff)
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
return optimalSize;
camer_menu.xml 代码
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_>
<item android:id="@+id/switchCam"
android:title="@string/switch_cam" />
<item android:id="@+id/takePicture"
android:title="@string/take_picture"
android:onClick="snapPicture"
android:layout_gravity="center" />
</menu>
更新
我尝试将 Preview 构造函数中的代码更改为以下代码。
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
this.setLayoutParams(lp);
mSurfaceView = new SurfaceView(context);
mSurfaceView.setLayoutParams(lp);
addView(mSurfaceView);
这没有崩溃,但也没有让相机全屏显示。
【问题讨论】:
嗨,我想知道你是否也有这个问题:***.com/questions/16727836/… ? 【参考方案1】:我通过从 Preview 类中删除以下代码来修复它,
if(mPreviewSize != null)
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
【讨论】:
【参考方案2】:由于您将 setContentView 与派生自 ViewGroup 的自定义 Preview 类一起使用,只需传递一个 ViewGroup.LayoutParams 告诉它填充它的父级。
类似这样的:
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
Preview.setLayoutParams(lp);
【讨论】:
我的例子不应该是“this.setLayoutParams(lp)”吗? 是的,您可以在 Preview 类的构造函数内部进行,也可以在 MainActivity 的外部进行,在实例化它和调用 setContentView 之间进行 我尝试将其放入 Preview 构造函数并将“Preview”更改为“this”。它编译并运行而不会崩溃,但它仍然不是全屏。我想知道我是否需要对“getOptimalPreviewSize”函数做一些事情才能让它工作。 我刚刚再次查看了代码,您必须对 SurfaceView 执行相同的操作。 Preview 是容器(因为它派生自 ViewGroup),SurfaceView 是实际的预览。 这仍然没有解决它。我在这里用我尝试过的新代码更新了我的问题。以上是关于如何使用 Android API 将相机预览大小设置为全屏?的主要内容,如果未能解决你的问题,请参考以下文章