2012-11-22 159 views
3

我在這裏發現了很多關於安卓相機預覽方向問題的問題。所有修復都涉及將屏幕方向固定爲橫向或調用camera.setDisplayOrientation(90)或調用params.setRotation(90)。我無法找到橫向放置時實際使其工作的設置組合。如果手機處於橫向模式,Android相機預覽方向不正確

使用android:screenOrientation =「landscape」將活動固定爲橫向模式。

我的問題是,如果我以縱向方向握住相機並啓動應用程序,它可以正常工作(我的意思是它正確地將圖像顯示爲風景)。如果我在啓動應用程序時以橫向方向握住相機,則圖像會全部混亂(交錯)。如果我使用camera.setDisplayOrientation(90);圖片不再混亂,但圖像側向。奇怪的是,如果我刪除android:screenOrientation =「風景」並允許屏幕旋轉,我仍然有同樣的問題,但如果我將手機旋轉到縱向,它在縱向上看起來不錯。如果我將其旋轉回風景,那麼它在風景中看起來不錯!我唯一的問題是它首次啓動時無法正常工作。

import java.util.List; 

import android.content.Context; 
import android.graphics.Color; 
import android.hardware.Camera; 
import android.hardware.Camera.Parameters; 
import android.hardware.Camera.Size; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.*; 


public class CameraView extends SurfaceView 
{  
    //Callback for the surfaceholder 
    SurfaceHolder.Callback surfaceHolderListener = new SurfaceHolder.Callback() { 
     public void surfaceCreated(SurfaceHolder holder) { 
      try { 
       camera=Camera.open(); 
      } catch(Exception e) { 
       Log.e("CameraView","Couldn't open the camera.",e); 
      } 
      try { 
       camera.setPreviewDisplay(previewHolder); 
      } catch (Throwable e) { 
       Log.e("CameraView","Couldn't call setPreviewDisplay.",e); 
      } 
     } 

     public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) { 
      if(camera!=null) { 
       Parameters params = camera.getParameters(); 

       List<Size> sizes = params.getSupportedPreviewSizes(); 
       Size optimalSize = getOptimalPreviewSize(sizes, w, h); 
       params.setPreviewSize(optimalSize.width, optimalSize.height); 

       camera.setParameters(params); 
       camera.startPreview(); 
      } 
     } 

     public void surfaceDestroyed(SurfaceHolder arg0) { 
      if(camera!=null) { 
       camera.setPreviewCallback(null); 
       camera.stopPreview(); 
       camera.release(); 
      } 
     } 

     private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { 
      final double ASPECT_TOLERANCE = 0.05; 
      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; 
     } 
    }; 


    public CameraView(Context ctx) { 
     super(ctx); 

     previewHolder = this.getHolder(); 
     previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
     previewHolder.addCallback(surfaceHolderListener); 
     setBackgroundColor(Color.TRANSPARENT); 
    } 

    public CameraView(Context ctx, AttributeSet attrs) { 
     super(ctx, attrs); 
    } 

    private Camera camera; 
    private SurfaceHolder previewHolder; 
} 

編輯: 真正的問題似乎是,後surfaceChanged其螺絲東西有事。我發現如果我啓動相機預覽,然後在surfaceChanged的最後添加一個調試點,則相機看起來很好。然後,如果我向前走了大約20步,它突然看起來搞砸了。

我解決了這個問題(通過一種我認爲是完全破解的方式),方法是使用方向更改偵聽器一次更新方向,然後禁用它自己。我只需要在視圖最初設置後激活的東西。我可能可以在其他地方做到這一點。

如果有人有任何想法如何解決這個問題,而不是這是完全愚蠢的,請幫助!

/** This works fine for me, but it's a hack. */ 

import java.util.List; 

import android.content.Context; 
import android.graphics.Color; 
import android.hardware.Camera; 
import android.hardware.Camera.Parameters; 
import android.hardware.Camera.Size; 
import android.util.AttributeSet; 
import android.util.DisplayMetrics; 
import android.util.Log; 
import android.view.OrientationEventListener; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 

public class CameraView3 
    extends SurfaceView 
{ 
    private static Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { 
     final double ASPECT_TOLERANCE = 0.05; 
     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; 
    } 


    private class CameraSurfaceHolder 
     extends OrientationEventListener 
     implements SurfaceHolder.Callback 
    { 
     CameraSurfaceHolder(Context ctx) { 
      super(ctx); 
     } 

     public void surfaceCreated(SurfaceHolder holder) { 
      try { 
       camera= Camera.open(); 
      } catch(Exception e) { 
       Log.e("CameraView","Couldn't open the camera.",e); 
      } 
      try { 
       camera.setPreviewDisplay(previewHolder); 
      } catch (Throwable e) { 
       Log.e("CameraView","Couldn't call setPreviewDisplay.",e); 
      } 
     } 

     public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) { 
     } 

     public void surfaceDestroyed(SurfaceHolder arg0) { 
      if(camera!=null) { 
       camera.setPreviewCallback(null); 
       camera.stopPreview(); 
       camera.release(); 
      } 
     } 

     @Override 
     public void onOrientationChanged(int orientation) { 
      if(camera!=null) { 
       DisplayMetrics dm = ctx.getResources().getDisplayMetrics(); 
       camera.stopPreview(); 
       CameraView3.this.layout(0, 0, dm.widthPixels-1, dm.heightPixels-1); 
       camera.startPreview(); 
       disable(); 
       return; 
      } 
     } 
    } 


    public CameraView3(Context ctx) { 
     super(ctx); 
     this.ctx = ctx; 

     previewHolder = this.getHolder(); 
     previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
     surfaceHolderListener = new CameraSurfaceHolder(ctx); 
     previewHolder.addCallback(surfaceHolderListener); 
     surfaceHolderListener.enable(); 

     setBackgroundColor(Color.TRANSPARENT); 
    } 

    private Context ctx; 
    private Camera camera; 
    private SurfaceHolder previewHolder; 
    private CameraSurfaceHolder surfaceHolderListener; 
    private Size optimalSize; 
} 

總之,我發佈的第一個版本在我的Nexus One上工作得很好。但是,它在我的Skyrocket上不起作用。我需要使用第二個版本才能在那裏工作。

+0

爲什麼在您的工作代碼中,您是否使用寬度和高度減1?如果沒有-1,我的代碼不能在Skyrocket上運行,但是它的代碼是-1。正如你所說,這感覺就像一個徹頭徹尾的黑客,但它是我發現這個奇怪行爲的唯一解決方案:(我不想減1,因爲它不完全填充屏幕......只是嘗試 –

回答

0

奇怪的是,如果我刪除了android:screenOrientation =「風景」和 讓屏幕旋轉,我仍然有同樣的問題,但如果我 手機旋轉爲縱向它會在肖像罰款。

首先請注意,相機預覽方向與活動方向無關。兩者都不同。當您改變設備方向時,您必須撥打camera.setDisplayOrientation根據方向設置預覽的方向。

這個方向你必須設定度數。這可以通過在相機每個視圖中設置OrientationEventListener來獲得。在onOrientationChanged(int orientation)中,您將獲得設備的精確旋轉角度(度)。

不要忘了計算旋轉,因爲API onOrientationChanged(int orientation)即使在設備方向上有小的角度變化也會被調用。你可以看到this的問題作爲參考。

+0

如何幫助?當前的代碼實際上在方向改變時開始工作,它只在第一次啓動時失敗並且指定旋轉值不起作用 – HappyEngineer

+0

方法onOrientationChanged也會在第一次被調用。你可以試試這個, – AndroDev

+1

這個方向監聽器的工作原理,但並不是我能看到的任何好的理由,在SurfaceChanged之後發生了一些事情,我在上面發佈了代碼,但我不喜歡它。 – HappyEngineer

相關問題