SurfaceViewpublic 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.
The transparent region that makes the surface visible is based on the
layout positions in the view hierarchy. If the post-layout transform
properties are used to draw a sibling view on top of the SurfaceView, the
view may not be properly composited with the surface.
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 into 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 | final ArrayList | mCallbacks | final int[] | mLocation | final ReentrantLock | mSurfaceLock | final Surface | mSurface | final Surface | mNewSurface | boolean | mDrawingStopped | final WindowManager.LayoutParams | mLayout | IWindowSession | mSession | MyWindow | mWindow | final android.graphics.Rect | mVisibleInsets | final android.graphics.Rect | mWinFrame | final android.graphics.Rect | mOverscanInsets | final android.graphics.Rect | mContentInsets | final android.graphics.Rect | mStableInsets | final android.content.res.Configuration | mConfiguration | static final int | KEEP_SCREEN_ON_MSG | static final int | GET_NEW_SURFACE_MSG | static final int | UPDATE_WINDOW_MSG | int | mWindowType | boolean | mIsCreating | final android.os.Handler | mHandler | final ViewTreeObserver.OnScrollChangedListener | mScrollChangedListener | boolean | mRequestedVisible | boolean | mWindowVisibility | boolean | mViewVisibility | int | mRequestedWidth | int | mRequestedHeight | int | mRequestedFormat | boolean | mHaveFrame | boolean | mSurfaceCreated | long | mLastLockTime | boolean | mVisible | int | mLeft | int | mTop | int | mWidth | int | mHeight | int | mFormat | final android.graphics.Rect | mSurfaceFrame | int | mLastSurfaceWidth | int | mLastSurfaceHeight | boolean | mUpdateWindowNeeded | boolean | mReportDrawNeeded | private android.content.res.CompatibilityInfo.Translator | mTranslator | private final ViewTreeObserver.OnPreDrawListener | mDrawListener | private boolean | mGlobalListenersAdded | private final SurfaceHolder | mSurfaceHolder |
Constructors Summary |
---|
public SurfaceView(android.content.Context context)
super(context);
init();
| public SurfaceView(android.content.Context context, android.util.AttributeSet attrs)
super(context, attrs);
init();
| public SurfaceView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();
| public SurfaceView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)
super(context, attrs, defStyleAttr, defStyleRes);
init();
|
Methods Summary |
---|
protected void | dispatchDraw(android.graphics.Canvas canvas)
if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
// if SKIP_DRAW is cleared, draw() has already punched a hole
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// punch a whole in the view-hierarchy below us
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
}
super.dispatchDraw(canvas);
| public void | draw(android.graphics.Canvas canvas)
if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
// draw() is not called when SKIP_DRAW is set
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
// punch a whole in the view-hierarchy below us
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
}
super.draw(canvas);
| public boolean | gatherTransparentRegion(android.graphics.Region region)
if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
return super.gatherTransparentRegion(region);
}
boolean opaque = true;
if ((mPrivateFlags & PFLAG_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 SurfaceHolder | getHolder()Return the SurfaceHolder providing access and control over this
SurfaceView's underlying surface.
return mSurfaceHolder;
| private SurfaceHolder.Callback[] | getSurfaceCallbacks()
SurfaceHolder.Callback callbacks[];
synchronized (mCallbacks) {
callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
mCallbacks.toArray(callbacks);
}
return callbacks;
| void | handleGetNewSurface()
updateWindow(false, false);
| private void | init()
setWillNotDraw(true);
| public boolean | isFixedSize()Check to see if the surface has fixed size dimensions or if the surface's
dimensions are dimensions are dependent on its current layout.
return (mRequestedWidth != -1 || mRequestedHeight != -1);
| protected void | onAttachedToWindow()
super.onAttachedToWindow();
mParent.requestTransparentRegion(this);
mSession = getWindowSession();
mLayout.token = getWindowToken();
mLayout.setTitle("SurfaceView");
mViewVisibility = getVisibility() == VISIBLE;
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnScrollChangedListener(mScrollChangedListener);
observer.addOnPreDrawListener(mDrawListener);
mGlobalListenersAdded = true;
}
| protected void | onDetachedFromWindow()
if (mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.removeOnScrollChangedListener(mScrollChangedListener);
observer.removeOnPreDrawListener(mDrawListener);
mGlobalListenersAdded = false;
}
mRequestedVisible = false;
updateWindow(false, false);
mHaveFrame = false;
if (mWindow != null) {
try {
mSession.remove(mWindow);
} catch (RemoteException ex) {
// Not much we can do here...
}
mWindow = null;
}
mSession = null;
mLayout.token = null;
super.onDetachedFromWindow();
| protected void | onMeasure(int widthMeasureSpec, int heightMeasureSpec)
int width = mRequestedWidth >= 0
? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
: getDefaultSize(0, widthMeasureSpec);
int height = mRequestedHeight >= 0
? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
: getDefaultSize(0, heightMeasureSpec);
setMeasuredDimension(width, height);
| protected void | onWindowVisibilityChanged(int visibility)
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
updateWindow(false, false);
| protected boolean | setFrame(int left, int top, int right, int bottom)
boolean result = super.setFrame(left, top, right, bottom);
updateWindow(false, false);
return result;
| public void | setSecure(boolean isSecure)Control whether the surface view's content should be treated as secure,
preventing it from appearing in screenshots or from being viewed on
non-secure displays.
Note that this must be set before the surface view's containing
window is attached to the window manager.
See {@link android.view.Display#FLAG_SECURE} for details.
if (isSecure) {
mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
} else {
mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
}
| public void | setVisibility(int visibility)
super.setVisibility(visibility);
mViewVisibility = visibility == VISIBLE;
boolean newRequestedVisible = mWindowVisibility && mViewVisibility;
if (newRequestedVisible != mRequestedVisible) {
// our base class (View) invalidates the layout only when
// we go from/to the GONE state. However, SurfaceView needs
// to request a re-layout when the visibility changes at all.
// This is needed because the transparent region is computed
// as part of the layout phase, and it changes (obviously) when
// the visibility changes.
requestLayout();
}
mRequestedVisible = newRequestedVisible;
updateWindow(false, false);
| public void | setWindowType(int type)Hack to allow special layering of windows. The type is one of the
types in WindowManager.LayoutParams. This is a hack so:
mWindowType = type;
| public void | setZOrderMediaOverlay(boolean isMediaOverlay)Control whether the surface view's surface is placed on top of another
regular surface view in the window (but still behind the window itself).
This is typically used to place overlays on top of an underlying media
surface view.
Note that this must be set before the surface view's containing
window is attached to the window manager.
Calling this overrides any previous call to {@link #setZOrderOnTop}.
mWindowType = isMediaOverlay
? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
: WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
| public void | setZOrderOnTop(boolean onTop)Control whether the surface view's surface is placed on top of its
window. Normally it is placed behind the window, to allow it to
(for the most part) appear to composite with the views in the
hierarchy. By setting this, you cause it to be placed above the
window. This means that none of the contents of the window this
SurfaceView is in will be visible on top of its surface.
Note that this must be set before the surface view's containing
window is attached to the window manager.
Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
if (onTop) {
mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
// ensures the surface is placed below the IME
mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
| protected void | updateWindow(boolean force, boolean redrawNeeded)
if (!mHaveFrame) {
return;
}
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null) {
mTranslator = viewRoot.mTranslator;
}
if (mTranslator != null) {
mSurface.setCompatibilityTranslator(mTranslator);
}
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;
if (force || creating || formatChanged || sizeChanged || visibleChanged
|| mLeft != mLocation[0] || mTop != mLocation[1]
|| mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
if (DEBUG) 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;
// Scaling/Translate window's layout here because mLayout is not used elsewhere.
// Places the window relative
mLayout.x = mLeft;
mLayout.y = mTop;
mLayout.width = getWidth();
mLayout.height = getHeight();
if (mTranslator != null) {
mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
}
mLayout.format = mRequestedFormat;
mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_SCALED
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
;
if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
mLayout.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
}
mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
if (mWindow == null) {
Display display = getDisplay();
mWindow = new MyWindow(this);
mLayout.type = mWindowType;
mLayout.gravity = Gravity.START|Gravity.TOP;
mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
mStableInsets);
}
boolean realSizeChanged;
boolean reportDrawNeeded;
int relayoutResult;
mSurfaceLock.lock();
try {
mUpdateWindowNeeded = false;
reportDrawNeeded = mReportDrawNeeded;
mReportDrawNeeded = false;
mDrawingStopped = !visible;
if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
relayoutResult = mSession.relayout(
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
visible ? VISIBLE : GONE,
WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mConfiguration, mNewSurface);
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mReportDrawNeeded = true;
}
if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
+ ", vis=" + visible + ", frame=" + mWinFrame);
mSurfaceFrame.left = 0;
mSurfaceFrame.top = 0;
if (mTranslator == null) {
mSurfaceFrame.right = mWinFrame.width();
mSurfaceFrame.bottom = mWinFrame.height();
} else {
float appInvertedScale = mTranslator.applicationInvertedScale;
mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
}
final int surfaceWidth = mSurfaceFrame.right;
final int surfaceHeight = mSurfaceFrame.bottom;
realSizeChanged = mLastSurfaceWidth != surfaceWidth
|| mLastSurfaceHeight != surfaceHeight;
mLastSurfaceWidth = surfaceWidth;
mLastSurfaceHeight = surfaceHeight;
} finally {
mSurfaceLock.unlock();
}
try {
redrawNeeded |= creating | reportDrawNeeded;
SurfaceHolder.Callback callbacks[] = null;
final boolean surfaceChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
mSurfaceCreated = false;
if (mSurface.isValid()) {
if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
callbacks = getSurfaceCallbacks();
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
}
}
mSurface.transferFrom(mNewSurface);
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
}
if (creating || formatChanged || sizeChanged
|| visibleChanged || realSizeChanged) {
if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
+ " w=" + myWidth + " h=" + myHeight);
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
}
}
if (redrawNeeded) {
if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
} finally {
mIsCreating = false;
if (redrawNeeded) {
if (DEBUG) Log.i(TAG, "finishedDrawing");
mSession.finishDrawing(mWindow);
}
mSession.performDeferredDestroy(mWindow);
}
} catch (RemoteException ex) {
}
if (DEBUG) Log.v(
TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
" w=" + mLayout.width + " h=" + mLayout.height +
", frame=" + mSurfaceFrame);
}
|
|