FileDocCategorySizeDatePackage
SurfaceView.javaAPI DocAndroid 1.5 API22263Wed May 06 22:41:56 BST 2009android.view

SurfaceView

public class SurfaceView extends View
Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen

The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.

Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling {@link #getHolder}.

The Surface will be created for you while the SurfaceView's window is visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated} and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the Surface is created and destroyed as the window is shown and hidden.

One of the purposes of this class is to provide a surface in which a secondary thread can render in to the screen. If you are going to use it this way, you need to be aware of some threading semantics:

  • All SurfaceView and {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called from the thread running the SurfaceView's window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing thread.
  • You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()} and {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final boolean
localLOGV
final ArrayList
mCallbacks
final int[]
mLocation
final ReentrantLock
mSurfaceLock
final Surface
mSurface
boolean
mDrawingStopped
final WindowManager.LayoutParams
mLayout
IWindowSession
mSession
MyWindow
mWindow
final android.graphics.Rect
mVisibleInsets
final android.graphics.Rect
mWinFrame
final android.graphics.Rect
mContentInsets
static final int
KEEP_SCREEN_ON_MSG
static final int
GET_NEW_SURFACE_MSG
boolean
mIsCreating
final android.os.Handler
mHandler
boolean
mRequestedVisible
int
mRequestedWidth
int
mRequestedHeight
int
mRequestedFormat
int
mRequestedType
boolean
mHaveFrame
boolean
mDestroyReportNeeded
boolean
mNewSurfaceNeeded
long
mLastLockTime
boolean
mVisible
int
mLeft
int
mTop
int
mWidth
int
mHeight
int
mFormat
int
mType
final android.graphics.Rect
mSurfaceFrame
private SurfaceHolder
mSurfaceHolder
Constructors Summary
public SurfaceView(android.content.Context context)


       
        super(context);
        setWillNotDraw(true);
    
public SurfaceView(android.content.Context context, android.util.AttributeSet attrs)

        super(context, attrs);
        setWillNotDraw(true);
    
public SurfaceView(android.content.Context context, android.util.AttributeSet attrs, int defStyle)

        super(context, attrs, defStyle);
        setWillNotDraw(true);
    
Methods Summary
protected voiddispatchDraw(android.graphics.Canvas canvas)

        // if SKIP_DRAW is cleared, draw() has already punched a hole
        if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
            // punch a whole in the view-hierarchy below us
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
        // reposition ourselves where the surface is 
        mHaveFrame = true;
        updateWindow(false);
        super.dispatchDraw(canvas);
    
public voiddraw(android.graphics.Canvas canvas)

        // draw() is not called when SKIP_DRAW is set
        if ((mPrivateFlags & SKIP_DRAW) == 0) {
            // punch a whole in the view-hierarchy below us
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
        super.draw(canvas);
    
public booleangatherTransparentRegion(android.graphics.Region region)

        boolean opaque = true;
        if ((mPrivateFlags & SKIP_DRAW) == 0) {
            // this view draws, remove it from the transparent region
            opaque = super.gatherTransparentRegion(region);
        } else if (region != null) {
            int w = getWidth();
            int h = getHeight();
            if (w>0 && h>0) {
                getLocationInWindow(mLocation);
                // otherwise, punch a hole in the whole hierarchy
                int l = mLocation[0];
                int t = mLocation[1];
                region.op(l, t, l+w, t+h, Region.Op.UNION);
            }
        }
        if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
            opaque = false;
        }
        return opaque;
    
public SurfaceHoldergetHolder()
Return the SurfaceHolder providing access and control over this SurfaceView's underlying surface.

return
SurfaceHolder The holder of the surface.

        return mSurfaceHolder;
    
voidhandleGetNewSurface()

        mNewSurfaceNeeded = true;
        updateWindow(false);
    
protected voidonAttachedToWindow()

        super.onAttachedToWindow();
        mParent.requestTransparentRegion(this);
        mSession = getWindowSession();
        mLayout.token = getWindowToken();
        mLayout.setTitle("SurfaceView");
    
protected voidonDetachedFromWindow()

        mRequestedVisible = false;
        updateWindow(false);
        mHaveFrame = false;
        if (mWindow != null) {
            try {
                mSession.remove(mWindow);
            } catch (RemoteException ex) {
            }
            mWindow = null;
        }
        mSession = null;
        mLayout.token = null;

        super.onDetachedFromWindow();
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
        int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
        setMeasuredDimension(width, height);
    
protected voidonScrollChanged(int l, int t, int oldl, int oldt)

        super.onScrollChanged(l, t, oldl, oldt);
        updateWindow(false);
    
protected voidonSizeChanged(int w, int h, int oldw, int oldh)

        super.onSizeChanged(w, h, oldw, oldh);
        updateWindow(false);
    
protected voidonWindowVisibilityChanged(int visibility)

        super.onWindowVisibilityChanged(visibility);
        mRequestedVisible = visibility == VISIBLE;
        updateWindow(false);
    
private voidreportSurfaceDestroyed()

        if (mDestroyReportNeeded) {
            mDestroyReportNeeded = false;
            SurfaceHolder.Callback callbacks[];
            synchronized (mCallbacks) {
                callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
                mCallbacks.toArray(callbacks);
            }            
            for (SurfaceHolder.Callback c : callbacks) {
                c.surfaceDestroyed(mSurfaceHolder);
            }
        }
        super.onDetachedFromWindow();
    
private voidupdateWindow(boolean force)

        if (!mHaveFrame) {
            return;
        }
        
        int myWidth = mRequestedWidth;
        if (myWidth <= 0) myWidth = getWidth();
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
        
        getLocationInWindow(mLocation);
        final boolean creating = mWindow == null;
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
        final boolean visibleChanged = mVisible != mRequestedVisible
                || mNewSurfaceNeeded;
        final boolean typeChanged = mType != mRequestedType;
        if (force || creating || formatChanged || sizeChanged || visibleChanged
            || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]) {

            if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
                    + " format=" + formatChanged + " size=" + sizeChanged
                    + " visible=" + visibleChanged
                    + " left=" + (mLeft != mLocation[0])
                    + " top=" + (mTop != mLocation[1]));
            
            try {
                final boolean visible = mVisible = mRequestedVisible;
                mLeft = mLocation[0];
                mTop = mLocation[1];
                mWidth = myWidth;
                mHeight = myHeight;
                mFormat = mRequestedFormat;
                mType = mRequestedType;

                mLayout.x = mLeft;
                mLayout.y = mTop;
                mLayout.width = getWidth();
                mLayout.height = getHeight();
                mLayout.format = mRequestedFormat;
                mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                              | WindowManager.LayoutParams.FLAG_SCALED
                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                              ;

                mLayout.memoryType = mRequestedType;

                if (mWindow == null) {
                    mWindow = new MyWindow(this);
                    mLayout.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
                    mLayout.gravity = Gravity.LEFT|Gravity.TOP;
                    mSession.add(mWindow, mLayout,
                            mVisible ? VISIBLE : GONE, mContentInsets);
                }
                
                if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
                    reportSurfaceDestroyed();
                }
                
                mNewSurfaceNeeded = false;
                
                mSurfaceLock.lock();
                mDrawingStopped = !visible;
                final int relayoutResult = mSession.relayout(
                        mWindow, mLayout, mWidth, mHeight,
                        visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
                        mVisibleInsets, mSurface);
                if (localLOGV) Log.i(TAG, "New surface: " + mSurface
                        + ", vis=" + visible + ", frame=" + mWinFrame);
                mSurfaceFrame.left = 0;
                mSurfaceFrame.top = 0;
                mSurfaceFrame.right = mWinFrame.width();
                mSurfaceFrame.bottom = mWinFrame.height();
                mSurfaceLock.unlock();

                try {
                    if (visible) {
                        mDestroyReportNeeded = true;

                        SurfaceHolder.Callback callbacks[];
                        synchronized (mCallbacks) {
                            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
                            mCallbacks.toArray(callbacks);
                        }            

                        if (visibleChanged) {
                            mIsCreating = true;
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }
                        if (creating || formatChanged || sizeChanged
                                || visibleChanged) {
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
                            }
                        }
                    }
                } finally {
                    mIsCreating = false;
                    if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
                        mSession.finishDrawing(mWindow);
                    }
                }
            } catch (RemoteException ex) {
            }
            if (localLOGV) Log.v(
                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                " w=" + mLayout.width + " h=" + mLayout.height +
                ", frame=" + mSurfaceFrame);
        }