TextureView.java revision 451ce44a18e4c48f8a43aa250957f76967a35d31
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.Paint; 22import android.graphics.SurfaceTexture; 23import android.util.AttributeSet; 24import android.util.Log; 25 26/** 27 * <p>A TextureView can be used to display a content stream. Such a content 28 * stream can for instance be a video or an OpenGL scene. The content stream 29 * can come from the application's process as well as a remote process.</p> 30 * 31 * <p>TextureView can only be used in a hardware accelerated window. When 32 * rendered in software, TextureView will draw nothing.</p> 33 * 34 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate 35 * window but behaves as a regular View. This key difference allows a 36 * TextureView to be moved, transformed, animated, etc. For instance, you 37 * can make a TextureView semi-translucent by calling 38 * <code>myView.setAlpha(0.5f)</code>.</p> 39 * 40 * <p>Using a TextureView is simple: all you need to do is get its 41 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to 42 * render content. The following example demonstrates how to render the 43 * camera preview into a TextureView:</p> 44 * 45 * <pre> 46 * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener { 47 * private Camera mCamera; 48 * private TextureView mTextureView; 49 * 50 * protected void onCreate(Bundle savedInstanceState) { 51 * super.onCreate(savedInstanceState); 52 * 53 * mTextureView = new TextureView(this); 54 * mTextureView.setSurfaceTextureListener(this); 55 * 56 * setContentView(mTextureView); 57 * } 58 * 59 * public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 60 * mCamera = Camera.open(); 61 * 62 * try { 63 * mCamera.setPreviewTexture(surface); 64 * mCamera.startPreview(); 65 * } catch (IOException ioe) { 66 * // Something bad happened 67 * } 68 * } 69 * 70 * public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 71 * // Ignored, Camera does all the work for us 72 * } 73 * 74 * public void onSurfaceTextureDestroyed(SurfaceTexture surface) { 75 * mCamera.stopPreview(); 76 * mCamera.release(); 77 * } 78 * } 79 * </pre> 80 * 81 * <p>A TextureView's SurfaceTexture can be obtained either by invoking 82 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}. 83 * It is important to know that a SurfaceTexture is available only after the 84 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has 85 * been invoked.) It is therefore highly recommended you use a listener to 86 * be notified when the SurfaceTexture becomes available.</p> 87 * 88 * @see SurfaceView 89 * @see SurfaceTexture 90 */ 91public class TextureView extends View { 92 private HardwareLayer mLayer; 93 private SurfaceTexture mSurface; 94 private SurfaceTextureListener mListener; 95 96 private final Runnable mUpdateLayerAction = new Runnable() { 97 @Override 98 public void run() { 99 updateLayer(); 100 } 101 }; 102 private SurfaceTexture.OnFrameAvailableListener mUpdateListener; 103 104 /** 105 * Creates a new TextureView. 106 * 107 * @param context The context to associate this view with. 108 */ 109 public TextureView(Context context) { 110 super(context); 111 init(); 112 } 113 114 /** 115 * Creates a new TextureView. 116 * 117 * @param context The context to associate this view with. 118 * @param attrs The attributes of the XML tag that is inflating the view. 119 */ 120 @SuppressWarnings({"UnusedDeclaration"}) 121 public TextureView(Context context, AttributeSet attrs) { 122 super(context, attrs); 123 init(); 124 } 125 126 /** 127 * Creates a new TextureView. 128 * 129 * @param context The context to associate this view with. 130 * @param attrs The attributes of the XML tag that is inflating the view. 131 * @param defStyle The default style to apply to this view. If 0, no style 132 * will be applied (beyond what is included in the theme). This may 133 * either be an attribute resource, whose value will be retrieved 134 * from the current theme, or an explicit style resource. 135 */ 136 @SuppressWarnings({"UnusedDeclaration"}) 137 public TextureView(Context context, AttributeSet attrs, int defStyle) { 138 super(context, attrs, defStyle); 139 init(); 140 } 141 142 private void init() { 143 mLayerPaint = new Paint(); 144 } 145 146 @Override 147 protected void onAttachedToWindow() { 148 super.onAttachedToWindow(); 149 150 if (!isHardwareAccelerated()) { 151 Log.w("TextureView", "A TextureView or a subclass can only be " 152 + "used with hardware acceleration enabled."); 153 } 154 } 155 156 @Override 157 protected void onDetachedFromWindow() { 158 super.onDetachedFromWindow(); 159 160 if (isHardwareAccelerated() && mLayer != null) { 161 if (mListener != null) { 162 mListener.onSurfaceTextureDestroyed(mSurface); 163 } 164 165 mLayer.destroy(); 166 mSurface = null; 167 mLayer = null; 168 } 169 } 170 171 /** 172 * The layer type of a TextureView is ignored since a TextureView is always 173 * considered to act as a hardware layer. The optional paint supplied to this 174 * method will however be taken into account when rendering the content of 175 * this TextureView. 176 * 177 * @param layerType The ype of layer to use with this view, must be one of 178 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or 179 * {@link #LAYER_TYPE_HARDWARE} 180 * @param paint The paint used to compose the layer. This argument is optional 181 * and can be null. It is ignored when the layer type is 182 * {@link #LAYER_TYPE_NONE} 183 */ 184 @Override 185 public void setLayerType(int layerType, Paint paint) { 186 if (paint != mLayerPaint) { 187 mLayerPaint = paint; 188 invalidate(); 189 } 190 } 191 192 /** 193 * Always returns {@link #LAYER_TYPE_HARDWARE}. 194 */ 195 @Override 196 public int getLayerType() { 197 return LAYER_TYPE_HARDWARE; 198 } 199 200 /** 201 * Calling this method has no effect. 202 */ 203 @Override 204 public void buildLayer() { 205 } 206 207 /** 208 * Subclasses of TextureView cannot do their own rendering 209 * with the {@link Canvas} object. 210 * 211 * @param canvas The Canvas to which the View is rendered. 212 */ 213 @Override 214 public final void draw(Canvas canvas) { 215 super.draw(canvas); 216 } 217 218 /** 219 * Subclasses of TextureView cannot do their own rendering 220 * with the {@link Canvas} object. 221 * 222 * @param canvas The Canvas to which the View is rendered. 223 */ 224 @Override 225 protected final void onDraw(Canvas canvas) { 226 } 227 228 @Override 229 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 230 super.onSizeChanged(w, h, oldw, oldh); 231 if (mSurface != null) { 232 nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight()); 233 if (mListener != null) { 234 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight()); 235 } 236 } 237 } 238 239 @Override 240 HardwareLayer getHardwareLayer() { 241 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 242 return null; 243 } 244 245 if (mLayer == null) { 246 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(); 247 mSurface = mAttachInfo.mHardwareRenderer.createSuraceTexture(mLayer); 248 nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight()); 249 250 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { 251 @Override 252 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 253 // Per SurfaceTexture's documentation, the callback may be invoked 254 // from an arbitrary thread 255 post(mUpdateLayerAction); 256 } 257 }; 258 mSurface.setOnFrameAvailableListener(mUpdateListener); 259 260 if (mListener != null) { 261 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); 262 } 263 } 264 265 return mLayer; 266 } 267 268 @Override 269 protected void onVisibilityChanged(View changedView, int visibility) { 270 super.onVisibilityChanged(changedView, visibility); 271 272 if (mSurface != null) { 273 // When the view becomes invisible, stop updating it, it's a waste of CPU 274 // To cancel updates, the easiest thing to do is simply to remove the 275 // updates listener 276 if (visibility == VISIBLE) { 277 mSurface.setOnFrameAvailableListener(mUpdateListener); 278 updateLayer(); 279 } else { 280 mSurface.setOnFrameAvailableListener(null); 281 } 282 } 283 } 284 285 private void updateLayer() { 286 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 287 return; 288 } 289 290 mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), mSurface); 291 292 invalidate(); 293 } 294 295 /** 296 * Returns the {@link SurfaceTexture} used by this view. This method 297 * may return null if the view is not attached to a window. 298 */ 299 public SurfaceTexture getSurfaceTexture() { 300 return mSurface; 301 } 302 303 /** 304 * Returns the {@link SurfaceTextureListener} currently associated with this 305 * texture view. 306 * 307 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) 308 * @see SurfaceTextureListener 309 */ 310 public SurfaceTextureListener getSurfaceTextureListener() { 311 return mListener; 312 } 313 314 /** 315 * Sets the {@link SurfaceTextureListener} used to listen to surface 316 * texture events. 317 * 318 * @see #getSurfaceTextureListener() 319 * @see SurfaceTextureListener 320 */ 321 public void setSurfaceTextureListener(SurfaceTextureListener listener) { 322 mListener = listener; 323 } 324 325 /** 326 * This listener can be used to be notified when the surface texture 327 * associated with this texture view is available. 328 */ 329 public static interface SurfaceTextureListener { 330 /** 331 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. 332 * 333 * @param surface The surface returned by 334 * {@link android.view.TextureView#getSurfaceTexture()} 335 * @param width The width of the surface 336 * @param height The height of the surface 337 */ 338 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height); 339 340 /** 341 * Invoked when the {@link SurfaceTexture}'s buffers size changed. 342 * 343 * @param surface The surface returned by 344 * {@link android.view.TextureView#getSurfaceTexture()} 345 * @param width The new width of the surface 346 * @param height The new height of the surface 347 */ 348 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height); 349 350 /** 351 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed. 352 * After this method is invoked, no rendering should happen inside the surface 353 * texture. 354 * 355 * @param surface The surface about to be destroyed 356 */ 357 public void onSurfaceTextureDestroyed(SurfaceTexture surface); 358 } 359 360 private static native void nSetDefaultBufferSize(int surfaceTexture, int width, int height); 361} 362