如何使用Android API将相机预览尺寸设置为全屏?

时间:2011-10-02 22:22:50

标签: java android

我是Android开发的新手,我正在尝试设置简单的相机应用程序。到目前为止,我有一个工作相机应用程序,在菜单中有“切换摄像头”和“拍照”按钮,工作正常。

我遇到的唯一问题是,我正在试图弄清楚如何让显示器全屏显示。现在,相机只显示在屏幕的中间位置,只占屏幕的1/4左右。

MainActivity Code

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_width="fill_parent"
    android:layout_height="fill_parent">

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

这并没有崩溃,但它也没有使相机全屏。

2 个答案:

答案 0 :(得分:11)

我通过从Preview类中删除以下代码来修复它,

if(mPreviewSize != null) {
    previewWidth = mPreviewSize.width;
    previewHeight = mPreviewSize.height;
}

答案 1 :(得分:2)

由于您将setContentView与从ViewGroup派生的自定义Preview类一起使用,因此只需传递一个ViewGroup.LayoutParams,告诉它填充它的父级。

这样的事情:

ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
Preview.setLayoutParams(lp);