Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show my Layout while SurfaceView is loading Camera Preview

I have a simple screen with a start button. When the Start button is pressed, I want to go to a new Screen with a SurfaceView to show the Camera in.

Everything works fine, but the Camera takes a while to load, and this gives me a black screen. I would like the new layout to load. And than start the camera after it has been loaded...

Therefor, I do all Camera loading in a background thread, but still, I get a black screen... Here's my layout:

<?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>

Here is the method from my Activity, which loads the new view:

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

How come, the new layout is not shown, until the thread has finished loading the camera?

EDIT: I've tried Thread.sleep(200) within the Thread to sleep for some time... When I do that, the new Layout is shown immedeately, but the Camera never starts...

like image 807
Entreco Avatar asked Aug 19 '11 08:08

Entreco


1 Answers

Allright, the problem is that I used my SurfaceView within the xml layout. The moment you call: setContentView(your_layout) -> the XML file is inflated. That means, the SurfaceView is inflated as well. That, again, means that the SurfaceView onSurfaceCreated Methods is called, which triggers opening the Camera etc.

So, this whole process takes a while, hence, your previous Activity (e.g. the one launching the Activity with the SurfaceView) seems to be unresponsive...

My solution, of creating the CameraView in a BG thread solves the inresponsiveness. But failed to show the Camera output in the SurfaceView.

The solution is to remove your SurfaceView from your xml. This will start your activity immedeately (since the SurfaceView & Camera are not instantiated). Once your new Activities layout is loaded, you can programmatically add a new SurfaceView to your screen. Off course, this takes time as well, but your UI switches to the new activity quickly, and you can show a loader while the SurfaceView and Camera are loading!

SO: REMOVE THE SURFACEVIEW FROM THE XML -> ADD IT PROGRAMATICALLY: Launch 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 (just a button to launch the new activity)

<?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>

Here's the Second Activity (Which contains the 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);
    }
}

And here's the second Activity's layout (WITHOUT A 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>

Finally, to complete the answer, here's the code for the CameraView(). It really is just a simple implementation to get open the Camera and Display the contents:

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;
        }   
    }
}
like image 134
Entreco Avatar answered Sep 28 '22 02:09

Entreco