2013-04-17 25 views
17

Tôi đang tạo một ứng dụng máy ảnh triển khai chế độ xem trước máy ảnh của chính nó để chụp ảnh. Ứng dụng hiện được buộc vào chế độ dọc.Xem trước máy ảnh được kéo dài ngay cả sau khi đã đặt đúng kích thước

Vấn đề của tôi là bản xem trước từ máy ảnh hơi bị kéo dài (tỷ lệ cỡ ảnh hơi lệch). Điều thú vị là tôi đang thiết lập kích thước SurfaceView của mình để luôn khớp với kích thước xem trước. Điều này đảm bảo rằng tỷ lệ khía cạnh nên luôn luôn được perserved ... nhưng nó không phải là ...

Dưới đây là cách bố trí tôi sử dụng để hiển thị xem trước máy ảnh của tôi:

public class Cp extends ViewGroup implements SurfaceHolder.Callback { 
    private final String TAG = "CameraPreview"; 

    private boolean mPreviewRunning = false; 

    private SurfaceView mSurfaceView; 
    private SurfaceHolder mHolder; 
    private Size mPreviewSize; 
    private List<Size> mSupportedPreviewSizes; 
    private Camera mCamera; 

    public boolean IsPreviewRunning() { 
     return mPreviewRunning; 
    } 

    public Cp(Context context) { 
     this(context, null, 0); 
    } 

    public Cp(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    public Cp(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 

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

    @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 && mCamera != null) { 
      mSupportedPreviewSizes = mCamera.getParameters() 
        .getSupportedPreviewSizes(); 
     } 
     if (mSupportedPreviewSizes != null) { 
      mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, 
        height); 
     } 
    } 

    @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.height; 
       previewHeight = mPreviewSize.width; 
      } 
      if (previewWidth == 0) { 
       previewWidth = 1; 
      } 
      if (previewHeight == 0) { 
       previewHeight = 1; 
      } 

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

    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
     // The Surface has been created, acquire the camera and tell it where to 
     // draw. 
     try { 
      if (mCamera != null) { 
       Parameters params = mCamera.getParameters(); 
       mSupportedPreviewSizes = params.getSupportedPreviewSizes(); 
       mCamera.setPreviewDisplay(holder); 
      } 
     } catch (IOException exception) { 
      Log.e(TAG, "IOException caused by setPreviewDisplay()", exception); 
     } 
    } 

    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 
     // Surface will be destroyed when we return, so stop the preview. 
     stop(); 
    } 

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

    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 
     if (mCamera != null) { 
      // Now that the size is known, set up the camera parameters and 
      // begin the preview. 
      Camera.Parameters parameters = mCamera.getParameters(); 
      if (mPreviewSize != null) { 
       parameters.setPreviewSize(mPreviewSize.width, 
         mPreviewSize.height); 
      } 
      requestLayout(); 

      mCamera.setParameters(parameters); 
      mCamera.startPreview(); 
      mPreviewRunning = true; 
     } 
    } 

    public void stop() { 
     if (mCamera != null) { 
      mCamera.stopPreview(); 
      mPreviewRunning = false; 
      mCamera = null; 
     } 
    } 
} 

Xin lưu ý rằng trong onLayout chiều rộng và chiều cao được đổi chỗ vì ứng dụng luôn chạy theo chiều dọc.

tôi bao gồm một số hình ảnh để hiển thị cho bạn những gì các vấn đề trông giống như: enter image description here

enter image description here

Tôi đã sửa lỗi mã. Kích thước xem trước được đặt thành 1280x720 và kích thước bố cục cũng được điều chỉnh chính xác để phù hợp với kích thước đó.

Tôi cũng đã cố gắng bố trí khác nhau nhưng kết quả là luôn luôn giống nhau ...

Trả lời

5

Tôi đã gặp vấn đề tương tự, sau khi một số ngày trong câu đố này, lớp Java của tôi kết thúc trên mã này:

Vì vậy, Sự cố đã xảy ra do màn hình máy ảnh có kích thước (chiều cao x rộng) 576x720 và màn hình của tôi là 1184x720. Vì vậy, xem trước máy ảnh (lớp bề mặt của tôi) kéo dài để điền vào phụ huynh.

Vì vậy, cách tiếp cận đã làm việc là làm cho chế độ xem này lớn hơn màn hình của tôi và chỉ hiển thị vùng màn hình của tôi. Vì vậy, tôi đã phải sử dụng sự kết hợp khung kỳ lạ này (một bố trí tương đối bên trong một linearlayout), để kích thước của tương đối sẽ lớn như tôi muốn - xem bề mặt sẽ điền vào nó - và tuyến tính sẽ chỉ lấy một phần mà tôi muốn hiển thị.

package com.example.frame.camera; 

import android.content.Context; 
import android.hardware.Camera; 
import android.util.Log; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 
import android.view.ViewGroup.LayoutParams; 
import android.widget.LinearLayout; 
import android.widget.RelativeLayout; 
import android.widget.Toast; 

import com.example.frame.MainActivity; 

public class NativeCamera extends SurfaceView implements SurfaceHolder.Callback { 

    static private NativeCamera instance; 

    private LinearLayout  frame    = null; 
    private RelativeLayout  innerFrame   = null; 

    private Camera    camera; 
    private final SurfaceHolder previewHolder; 
    private static final String TAG     = "NativeCamera.java"; 

    private boolean    inPreview   = false; 
    private boolean    cameraConfigured = false; 
    private boolean    frontCamera   = false; 

    private Camera.Size   size; 

    static public NativeCamera getInstance() { 
     if (NativeCamera.instance == null) { 
      if (MainActivity.debug) { 
       Log.d(TAG, "Creating Camera Singleton"); 
      } 
      NativeCamera.instance = new NativeCamera(MainActivity.instance); 
     } 
     return NativeCamera.instance; 
    } 

    public void onResume() { 
     if (MainActivity.debug) { 
      Log.d(TAG, "onResume"); 
     } 
     camera = Camera.open(); 
     if (size != null) { 
      initPreview(size.width, size.height); 
     } 
     startPreview(); 
    } 

    public void onPause() { 
     if (MainActivity.debug) { 
      Log.d(TAG, "onPause"); 
     } 
     if (inPreview) { 
      camera.stopPreview(); 
     } 
     camera.release(); 
     camera = null; 
     inPreview = false; 
    } 

    public void onDestroy() { 
     if (MainActivity.debug) { 
      Log.d(TAG, "onDestroy"); 
     } 
     NativeCamera.instance = null; 
    } 

    public void onSwitch() { 
     frontCamera = !frontCamera; 
     if (inPreview) { 
      camera.stopPreview(); 
     } 
     camera.release(); 

     int cam = frontCamera ? 1 : 0; 

     camera = Camera.open(cam); 
     if (size != null) { 
      initPreview(size.width, size.height); 
     } 
     startPreview(); 
    } 

    private NativeCamera(Context context) { 
     super(context); 
     // TODO Auto-generated constructor stub 

     // Install a SurfaceHolder.Callback so we get notified when the 
     // underlying surface is created and destroyed. 
     previewHolder = getHolder(); 
     previewHolder.addCallback(this); 
     // deprecated setting, but required on Android versions prior to 3.0 
     // previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
     innerFrame = new RelativeLayout(MainActivity.instance); 
     innerFrame.addView(this); 
     frame = new LinearLayout(MainActivity.instance); 
     frame.addView(innerFrame); 
    } 

    public LinearLayout getFrame() { 
     return frame; 
    } 

    public void surfaceChanged(SurfaceHolder holder, int format, int width, 
      int height) { 
     // TODO Auto-generated method stub 
     if (MainActivity.debug) { 
      Log.d(TAG, "surfaceChanged"); 
     } 
     initPreview(width, height); 
     startPreview(); 
    } 

    public void surfaceCreated(SurfaceHolder holder) { 
     // TODO Auto-generated method stub 
     // no-op -- wait until surfaceChanged() 

    } 

    public void surfaceDestroyed(SurfaceHolder holder) { 
     // TODO Auto-generated method stub 
     // no-op 
    } 

    private Camera.Size getBestPreviewSize(int width, int height, 
      Camera.Parameters parameters) { 
     Camera.Size result = null; 

     for (Camera.Size size : parameters.getSupportedPreviewSizes()) { 
      if (size.width <= width && size.height <= height) { 
       if (result == null) { 
        result = size; 
       } else { 
        int resultArea = result.width * result.height; 
        int newArea = size.width * size.height; 

        if (newArea > resultArea) { 
         result = size; 
        } 
       } 
      } 
     } 

     this.size = result; 
     return (result); 
    } 

    private void initPreview(int width, int height) { 
     if (camera != null && previewHolder.getSurface() != null) { 

      if (!cameraConfigured) { 
       Camera.Parameters parameters = camera.getParameters(); 
       Camera.Size size = getBestPreviewSize(width, height, parameters); 

       if (size != null) { 
        parameters.setPreviewSize(size.width, size.height); 
        camera.setParameters(parameters); 
        cameraConfigured = true; 
        // Setting up correctly the view 
        double ratio = size.height/(double) size.width; 
        LayoutParams params = innerFrame.getLayoutParams(); 
        params.height = MainActivity.size.y; 
        params.width = (int) (MainActivity.size.y * ratio); 
        innerFrame.setLayoutParams(params); 
        int deslocationX = (int) (params.width/2.0 - MainActivity.size.x/2.0); 
        innerFrame.animate().translationX(-deslocationX); 
       } 
      } 

      try { 
       camera.setPreviewDisplay(previewHolder); 
       camera.setDisplayOrientation(90); 

      } catch (Throwable t) { 
       Log.e(TAG, "Exception in setPreviewDisplay()", t); 
       Toast.makeText(MainActivity.instance, t.getMessage(), Toast.LENGTH_LONG).show(); 
      } 

     } 
    } 

    private void startPreview() { 
     if (MainActivity.debug) { 
      Log.d(TAG, "startPreview"); 
     } 
     if (cameraConfigured && camera != null) { 
      camera.startPreview(); 
      inPreview = true; 
     } 
    } 

} 
+0

đã không kiểm tra nó hoàn toàn được nêu ra, nhưng điều này thực sự có vẻ làm việc. Tốt đẹp. –

+0

Giải pháp này không hoạt động đối với tôi. Vui lòng tham khảo câu hỏi của tôi http: // stackoverflow.com/questions/21107208/how-to-make-camera-preview-size-same-as-imageview-size-without-preview-stretch –

+1

getBestPreviewSize() luôn mang lại cho tôi kích thước nhỏ hơn của máy ảnh và cùng một vấn đề xảy ra khi xem căng. –

1

Tôi đã cố gắng để giải quyết cùng một vấn đề ... Dưới đây đang làm việc cho tôi:

private void setMyPreviewSize(int width, int height) { 
    // Get the set dimensions 
    float newProportion = (float) width/(float) height; 

    // Get the width of the screen 
    int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); 
    int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); 
    float screenProportion = (float) screenWidth/(float) screenHeight; 

    // Get the SurfaceView layout parameters 
    android.view.ViewGroup.LayoutParams lp = surfaceView.getLayoutParams(); 
    if (newProportion > screenProportion) { 
     lp.width = screenWidth; 
     lp.height = (int) ((float) screenWidth/newProportion); 
    } else { 
     lp.width = (int) (newProportion * (float) screenHeight); 
     lp.height = screenHeight; 
    } 
    // Commit the layout parameters 
    surfaceView.setLayoutParams(lp); 
} 

Bạn có thể xem chủ đề: Resizing surface view for aspect ratio change in video display in android

0

Dưới đây là một giải pháp

surfaceChanged chỉ để có kích thước xem trước tốt nhất

@Override 
      public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
       Log.e("MYTAG", "surfaceChanged "); 

       Camera.Parameters myParameters = camera.getParameters(); 
       Camera.Size myBestSize = getBestPreviewSize(width, height, myParameters); 

       if(myBestSize != null){ 
        myParameters.setPreviewSize(myBestSize.width, myBestSize.height); 
        camera.setParameters(myParameters); 
        camera.startPreview(); 


       } 
      } 



@Override 
    public void onPictureTaken(byte[] data, Camera camera) { 

     Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 
     imageReview.setImageBitmap(bitmap); 
    } 

này sẽ thiết lập các hình ảnh được chụp trên imageReview, nhưng bạn cần một phương pháp để có được vòng quay của bitmap

bạn cần phải thiết lập IMAGExem scaleType thuộc tính cho vấn đề hình ảnh kéo dài

<ImageView 
     android:id="@+id/imageView" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:scaleType="centerCrop" 
     /> 
Các vấn đề liên quan