当SurfaceView正在加载相机预览时显示我的布局

8
我有一个简单的屏幕,上面有一个“开始”按钮。当按下“开始”按钮时,我希望进入一个新的屏幕,在其中显示摄像头的SurfaceView。
一切都运转良好,但是摄像头需要一段时间才能加载,这会给我一个黑屏。 我希望新布局能够加载,并在加载后启动摄像头...
因此,我将所有摄像头加载工作放在后台线程中,但仍然得到了黑屏... 这是我的布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/RelativeLayout1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/blue_bg">

    <SurfaceView
        android:id="@+id/surface_camera"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="25dp"
        android:layout_marginRight="25dp"
        android:layout_below="@id/scan_header"
        android:layout_above="@id/scan_footer">
    </SurfaceView>

</RelativeLayout>

这是我在Activity中加载新视图的方法:

private void setContent()
{
    setContentView(R.layout.scan)

    Thread t = new Thread()
    {
        public void run()
        {

            final SurfaceView mSurfaceView = (SurfaceView)findViewById(R.id.surface_camera);
            final SurfaceHolder mSurfaceHolder = mSurfaceView.getHolder();

            try
            {   
                cameraView = new CameraView();
                mSurfaceHolder.addCallback(cameraView);
                cameraView.setPictureListener(SunpluggedActivity.this);
                mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

            } catch(Exception e)
            {
                Log.d(TAG, "Another exception");
                e.printStackTrace();
            }
        }
    };
    t.start();
}

为什么新布局要等到线程完成相机加载后才显示?

编辑:我尝试在线程内使用Thread.sleep(200)来暂停一段时间...当我这样做时,新布局立即显示,但相机从未启动...

1个回答

24

问题在于我在xml布局中使用了SurfaceView。 当你调用setContentView(your_layout) -> XML文件被膨胀。 这意味着,SurfaceView也被膨胀了。那又意味着SurfaceView的onSurfaceCreated方法被调用,从而触发打开相机等操作。

因此,整个过程需要一段时间,因此,您之前的Activity(例如启动具有SurfaceView的Activity的Activity)似乎没有响应...

我的解决方案是在后台线程中创建CameraView,解决了无响应的问题。但是未能在SurfaceView中显示相机输出。

解决方法是从xml中删除SurfaceView。这将立即启动您的活动(因为未实例化SurfaceView和Camera)。 一旦加载新活动的布局,您可以以编程方式向屏幕添加一个新的SurfaceView。 当然,这也需要时间,但是您的UI快速切换到新活动,并且您可以在加载SurfaceView和Camera时显示加载程序!

因此:从xml中删除SurfaceView->以编程方式添加它: 启动Activity:

public class Launch extends Activity implements OnClickListener 
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main);
        Button btn = (Button)findViewById(R.id.button1);
        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(Launch.this, SurfaceTestActivity.class);
        startActivity(intent);  
    }
}

Main.xml文件(只有一个按钮用于启动新活动)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/RelativeLayout1"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ff6600">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />
</RelativeLayout>

这是第二个活动(包含SurfaceView)。

public class SurfaceTestActivity extends Activity {

    private Context mContext;
    private CameraView cameraView;
    private Handler mHandler = new Handler();
    private final Runnable mLoadCamera = new Runnable()
    {
        public void run()
        {
            startCamera();
        }
    };

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContent();
        mContext = getApplicationContext();
    }

    private void startCamera()
    {
        RelativeLayout rl = (RelativeLayout)findViewById(R.id.surface_camera);
        SurfaceView surfaceView = new SurfaceView(mContext);
        final SurfaceHolder mSurfaceHolder = surfaceView.getHolder();

        try
        {  
            cameraView = new CameraView();
            mSurfaceHolder.addCallback(cameraView);
            mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        } catch(Exception e)
        {
            Log.d("debug", "Another exception");
            e.printStackTrace();
        }

        if(rl != null && surfaceView != null)
            rl.addView(surfaceView);
    }

    private void setContent()
    {
        setContentView(R.layout.scan);

        // Post the Runnable with a Slight delay -> than your layout will be 
        // shown. Without the delay -> your UI will feel inresponsive
        mHandler.postDelayed(mLoadCamera, 100);
    }
}

这里是第二个活动的布局(不包含SurfaceView)。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/RelativeLayout1"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ff6600">
    <RelativeLayout 
        android:id="@+id/header"
        android:layout_width="fill_parent" android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="Explanation Txt"></TextView>
    </RelativeLayout>
    <RelativeLayout 
        android:id="@+id/footer"
        android:layout_width="fill_parent" android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">
        <TextView
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="Explanation Txt"></TextView>
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/surface_camera"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/footer"
        android:layout_below="@+id/header" 
        android:background="#ff0066">

    </RelativeLayout>

</RelativeLayout>

最后,为了完成答案,这里是CameraView()的代码。它真的只是一个简单的实现,用于打开相机并显示其内容:

public class CameraView  implements SurfaceHolder.Callback{

    // Variables
    private Camera mCamera = null;
    private boolean mPreviewRunning = false;
    private boolean mProcessing = false;
    private int mWidth = 0;
    private int mHeight = 0;

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) 
    {   
        if(mPreviewRunning )
        {
            mCamera.stopPreview();
        }

        // Store width and height
        mWidth = width;
        mHeight = height;

        // Set camera parameters
        Camera.Parameters p = mCamera.getParameters();
        mCamera.setParameters(p);

        if(android.os.Build.VERSION.SDK_INT >= 8)
        {   // If API >= 8 -> rotate display...
            mCamera.setDisplayOrientation(90);
        }

        try
        {
            mCamera.setPreviewDisplay(holder);
        } catch(IOException e)
        {
            e.printStackTrace();
        }

        mCamera.startPreview();
        mPreviewRunning = true;

    }

    @Override
    public void surfaceCreated(final SurfaceHolder holder) 
    {
        try {
            mCamera = Camera.open();
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) 
        {
            mCamera.release();
            mCamera = null;
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) 
    {
        if(mCamera != null)
        {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.setPreviewCallback(null);
            mPreviewRunning = false;
            mCamera.release();
            mCamera = null;
        }   
    }
}

2
Android开发者指南还建议在WorkerThread中调用Camera.open()以避免阻塞UI线程。 - type-a1pha
1
这段代码运行得非常好...如果我想在它上面添加我的布局怎么办? - Rishabh Srivastava
1
在xml中创建一个RelativeLayout,并在其中添加一个SurfaceView。这个SurfaceView将成为你的“相机”视图。在它的上面,你可以添加所有你的布局。 - Entreco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接