2013-04-17 23 views
17

Tworzę aplikację z kamerą, która implementuje własny podgląd kamery do robienia zdjęć. Aplikacja jest obecnie zmuszona do trybu portretowego.Podgląd kamery rozciągnięty nawet po ustawieniu prawidłowych wymiarów

Moim problemem jest to, że podgląd z kamery jest lekko rozciągnięty (proporcje są nieco mniejsze). Zabawne jest to, że ustawiam rozmiar SurfaceView, aby zawsze pasował do rozmiaru podglądu. Gwarantuje to, że proporcje powinny być zawsze perserved ... ale to nie jest ...

Oto układ używam pokazać mój podgląd kamery:

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

Należy pamiętać, że w onLayout szerokości i wysokość są zamieniane, ponieważ aplikacja działa zawsze w trybie portretu.

jestem tym kilka zdjęć, aby pokazać, na czym polega problem wygląda następująco: enter image description here

enter image description here

Mam debugowania kodu. Rozmiar podglądu jest ustawiany na 1280x720, a rozmiar układu jest również prawidłowo dopasowywany do tego rozmiaru.

Próbowałem również różne układy, ale wynik był zawsze taki sam ...

Odpowiedz

5

miałem ten sam problem, po kilku dniach w tej układanki, moja klasa Java kończy się na tym kodzie:

Tak, problem występował, ponieważ wyświetlacz aparatu miał rozmiar (wysokość x szerokość) 576x720, a mój wyświetlacz to 1184x720. Tak więc podgląd kamery (moja klasa widoku powierzchni) został rozciągnięty, aby wypełnić rodzica.

Tak więc podejście, które działało, sprawiło, że widok ten był większy niż mój ekran, a to po prostu wyświetlał obszar mojego ekranu. Musiałem więc użyć tej dziwnej kombinacji ramek (względny układ w liniowym layoucie), aby wielkość elementu relatywnego była tak duża, jak chcę - widok powierzchni wypełni ją -, a liniowy zabierze tylko część, którą chcę wyświetlić.

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

Czy to nie testowane w pełni, ale to wydaje się działać. Miły. –

+0

To rozwiązanie nie działa dla mnie. Proszę zapoznać się z moim pytaniem http: // stackoverflow.com/questions/21107208/how-to-make-camera-preview-size-to-jak-viewview-size-without-preview-stretch –

+1

getBestPreviewSize() zawsze daje mi mniejszy rozmiar aparatu i ten sam problem występuje rozciągnąć widok. –

1

I starali się rozwiązać ten sam problem ... poniżej kod pracował dla mnie:

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

można zobaczyć wątek: Resizing surface view for aspect ratio change in video display in android

0

Oto rozwiązanie

surfaceChanged jest po to, aby uzyskać najlepszy podgląd rozmiaru:

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

Spowoduje to ustawienie przechwyconego obrazu na imageReview, ale trzeba się sposób, aby uzyskać obrót bitmapy

trzeba ustawić atrybut ImageView scaleType na rozciągniętym emisji obrazu

<ImageView 
     android:id="@+id/imageView" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:scaleType="centerCrop" 
     /> 
Powiązane problemy