CarouselView.java revision 7cc5787b49708f63aeb818bdd06efed3d4229925
1/* 2 * Copyright (C) 2010 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 com.android.ex.carousel; 18 19import android.view.View; 20import com.android.ex.carousel.CarouselRS.CarouselCallback; 21 22import android.content.Context; 23import android.content.res.Resources; 24import android.graphics.Bitmap; 25import android.graphics.PixelFormat; 26import android.graphics.Bitmap.Config; 27import android.renderscript.FileA3D; 28import android.renderscript.Float4; 29import android.renderscript.Mesh; 30import android.renderscript.RSSurfaceView; 31import android.renderscript.RenderScriptGL; 32import android.util.AttributeSet; 33import android.util.Log; 34import android.view.MotionEvent; 35import android.view.SurfaceHolder; 36 37/** 38 * <p> 39 * This class represents the basic building block for using a 3D Carousel. The Carousel is 40 * basically a scene of cards and slots. The spacing between cards is dictated by the number 41 * of slots and the radius. The number of visible cards dictates how far the Carousel can be moved. 42 * If the number of cards exceeds the number of slots, then the Carousel will continue to go 43 * around until the last card can be seen. 44 */ 45public abstract class CarouselView extends RSSurfaceView { 46 private static final boolean USE_DEPTH_BUFFER = true; 47 private static final String TAG = "CarouselView"; 48 private static final boolean DBG = false; 49 private CarouselRS mRenderScript; 50 private RenderScriptGL mRS; 51 private Context mContext; 52 private boolean mTracking; 53 54 CarouselController mController; 55 56 // Note: remember to update carousel.rs when changing the values below 57 public static class DetailAlignment { 58 /** Detail is centered vertically with respect to the card **/ 59 public static final int CENTER_VERTICAL = 1; 60 /** Detail is aligned with the top edge of the carousel view **/ 61 public static final int VIEW_TOP = 1 << 1; 62 /** Detail is aligned with the bottom edge of the carousel view (not yet implemented) **/ 63 public static final int VIEW_BOTTOM = 1 << 2; 64 /** Detail is positioned above the card (not yet implemented) **/ 65 public static final int ABOVE = 1 << 3; 66 /** Detail is positioned below the card **/ 67 public static final int BELOW = 1 << 4; 68 /** Mask that selects those bits that control vertical alignment **/ 69 public static final int VERTICAL_ALIGNMENT_MASK = 0xff; 70 71 /** 72 * Detail is centered horizontally with respect to either the top or bottom 73 * extent of the card, depending on whether the detail is above or below the card. 74 */ 75 public static final int CENTER_HORIZONTAL = 1 << 8; 76 /** 77 * Detail is aligned with the left edge of either the top or the bottom of 78 * the card, depending on whether the detail is above or below the card. 79 */ 80 public static final int LEFT = 1 << 9; 81 /** 82 * Detail is aligned with the right edge of either the top or the bottom of 83 * the card, depending on whether the detail is above or below the card. 84 * (not yet implemented) 85 */ 86 public static final int RIGHT = 1 << 10; 87 /** Mask that selects those bits that control horizontal alignment **/ 88 public static final int HORIZONTAL_ALIGNMENT_MASK = 0xff00; 89 } 90 91 public static class Info { 92 public Info(int _resId) { resId = _resId; } 93 public int resId; // resource for renderscript resource (e.g. R.raw.carousel) 94 } 95 96 public abstract Info getRenderScriptInfo(); 97 98 public CarouselView(Context context) { 99 this(context, new CarouselController()); 100 } 101 102 public CarouselView(Context context, CarouselController controller) { 103 this(context, null, controller); 104 } 105 106 /** 107 * Constructor used when this widget is created from a layout file. 108 */ 109 public CarouselView(Context context, AttributeSet attrs) { 110 this(context, attrs, new CarouselController()); 111 } 112 113 public CarouselView(Context context, AttributeSet attrs, CarouselController controller) { 114 super(context, attrs); 115 mContext = context; 116 mController = controller; 117 boolean useDepthBuffer = true; 118 ensureRenderScript(); 119 // TODO: add parameters to layout 120 121 setOnLongClickListener(new View.OnLongClickListener() { 122 public boolean onLongClick(View v) { 123 if (interpretLongPressEvents()) { 124 mController.onLongPress(); 125 return true; 126 } else { 127 return false; 128 } 129 } 130 }); 131 } 132 133 private void ensureRenderScript() { 134 if (mRS == null) { 135 RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); 136 if (USE_DEPTH_BUFFER) { 137 sc.setDepth(16, 24); 138 } 139 mRS = createRenderScript(sc); 140 } 141 if (mRenderScript == null) { 142 mRenderScript = new CarouselRS(mRS, mContext.getResources(), 143 getRenderScriptInfo().resId); 144 mRenderScript.resumeRendering(); 145 } 146 mController.setRS(mRS, mRenderScript); 147 } 148 149 @Override 150 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 151 super.surfaceChanged(holder, format, w, h); 152 mController.onSurfaceChanged(); 153 } 154 155 public CarouselController getController() { 156 return mController; 157 } 158 159 public void setController(CarouselController controller) { 160 mController = controller; 161 mController.setRS(mRS, mRenderScript); 162 } 163 164 /** 165 * Do I want to interpret the long-press gesture? If so, long-presses will cancel the 166 * current selection and call the appropriate callbacks. Otherwise, a long press will 167 * not be handled any way other than as a continued drag. 168 * 169 * @return True if we interpret long-presses 170 */ 171 public boolean interpretLongPressEvents() { 172 return false; 173 } 174 175 /** 176 * Loads geometry from a resource id. 177 * 178 * @param resId 179 * @return the loaded mesh or null if it cannot be loaded 180 */ 181 public Mesh loadGeometry(int resId) { 182 return mController.loadGeometry(mContext.getResources(), resId); 183 } 184 185 /** 186 * Load A3D file from resource. If resId == 0, will clear geometry for this item. 187 * @param n 188 * @param resId 189 */ 190 public void setGeometryForItem(int n, Mesh mesh) { 191 mController.setGeometryForItem(n, mesh); 192 } 193 194 /** 195 * Set the number of slots around the Carousel. Basically equivalent to the poles horses 196 * might attach to on a real Carousel. 197 * 198 * @param n the number of slots 199 */ 200 public void setSlotCount(int n) { 201 mController.setSlotCount(n); 202 } 203 204 /** 205 * Sets the number of visible slots around the Carousel. This is primarily used as a cheap 206 * form of clipping. The Carousel will never show more than this many cards. 207 * @param n the number of visible slots 208 */ 209 public void setVisibleSlots(int n) { 210 mController.setVisibleSlots(n); 211 } 212 213 /** 214 * Set the number of cards to pre-load that are outside of the visible region, as determined by 215 * setVisibleSlots(). This number gets added to the number of visible slots and used to 216 * determine when resources for cards should be loaded. This number should be small (n <= 4) 217 * for systems with limited texture memory or views that show more than half dozen cards in the 218 * view. 219 * 220 * @param n the number of cards; should be even, so the count is the same on each side 221 */ 222 public void setPrefetchCardCount(int n) { 223 mController.setPrefetchCardCount(n); 224 } 225 226 /** 227 * Set the number of detail textures that can be visible at one time. 228 * 229 * @param n the number of slots 230 */ 231 public void setVisibleDetails(int n) { 232 mController.setVisibleDetails(n); 233 } 234 235 /** 236 * Sets how detail textures are aligned with respect to the card. 237 * 238 * @param alignment a bitmask of DetailAlignment flags. 239 */ 240 public void setDetailTextureAlignment(int alignment) { 241 mController.setDetailTextureAlignment(alignment); 242 } 243 244 /** 245 * Set whether blending is enabled while drawing the card textures. This should be true when 246 * translucent cards need to be supported, and false when all cards are fully opaque. Setting 247 * to false provides a performance boost. 248 * 249 * @param enabled True to enable blending, and false to disable it. 250 */ 251 public void setDrawCardsWithBlending(boolean enabled) { 252 mController.setDrawCardsWithBlending(enabled); 253 } 254 255 /** 256 * Set whether to draw a ruler from the card to the detail texture 257 * 258 * @param drawRuler True to draw a ruler, false to draw nothing where the ruler would go. 259 */ 260 public void setDrawRuler(boolean drawRuler) { 261 mController.setDrawRuler(drawRuler); 262 } 263 264 /** 265 * This dictates how many cards are in the deck. If the number of cards is greater than the 266 * number of slots, then the Carousel goes around n / slot_count times. 267 * 268 * Can be called again to increase or decrease the number of cards. 269 * 270 * @param n the number of cards to create. 271 */ 272 public void createCards(int n) { 273 mController.createCards(n); 274 } 275 276 public int getCardCount() { 277 return mController.getCardCount(); 278 } 279 280 /** 281 * This sets the texture on card n. It should only be called in response to 282 * {@link CarouselCallback#onRequestTexture(int)}. Since there's no guarantee 283 * that a given texture is still on the screen, replacing this texture should be done 284 * by first setting it to null and then waiting for the next 285 * {@link CarouselCallback#onRequestTexture(int)} to swap it with the new one. 286 * 287 * @param n the card given by {@link CarouselCallback#onRequestTexture(int)} 288 * @param bitmap the bitmap image to show 289 */ 290 public void setTextureForItem(int n, Bitmap bitmap) { 291 mController.setTextureForItem(n, bitmap); 292 } 293 294 /** 295 * This sets the detail texture that floats above card n. It should only be called in response 296 * to {@link CarouselCallback#onRequestDetailTexture(int)}. Since there's no guarantee 297 * that a given texture is still on the screen, replacing this texture should be done 298 * by first setting it to null and then waiting for the next 299 * {@link CarouselCallback#onRequestDetailTexture(int)} to swap it with the new one. 300 * 301 * @param n the card to set detail texture for 302 * @param offx an optional offset to apply to the texture (in pixels) from top of detail line 303 * @param offy an optional offset to apply to the texture (in pixels) from top of detail line 304 * @param loffx an optional offset to apply to the line (in pixels) from left edge of card 305 * @param loffy an optional offset to apply to the line (in pixels) from top of screen 306 * @param bitmap the bitmap to show as the detail 307 */ 308 public void setDetailTextureForItem(int n, float offx, float offy, float loffx, float loffy, 309 Bitmap bitmap) { 310 mController.setDetailTextureForItem(n, offx, offy, loffx, loffy, bitmap); 311 } 312 313 /** 314 * Sets the bitmap to show on a card when the card draws the very first time. 315 * Generally, this bitmap will only be seen during the first few frames of startup 316 * or when the number of cards are changed. It can be ignored in most cases, 317 * as the cards will generally only be in the loading or loaded state. 318 * 319 * @param bitmap 320 */ 321 public void setDefaultBitmap(Bitmap bitmap) { 322 mController.setDefaultBitmap(bitmap); 323 } 324 325 /** 326 * Sets the bitmap to show on the card while the texture is loading. It is set to this 327 * value just before {@link CarouselCallback#onRequestTexture(int)} is called and changed 328 * when {@link CarouselView#setTextureForItem(int, Bitmap)} is called. It is shared by all 329 * cards. 330 * 331 * @param bitmap 332 */ 333 public void setLoadingBitmap(Bitmap bitmap) { 334 mController.setLoadingBitmap(bitmap); 335 } 336 337 /** 338 * Sets background to specified color. If a background texture is specified with 339 * {@link CarouselView#setBackgroundBitmap(Bitmap)}, then this call has no effect. 340 * 341 * @param red the amount of red 342 * @param green the amount of green 343 * @param blue the amount of blue 344 * @param alpha the amount of alpha 345 */ 346 public void setBackgroundColor(float red, float green, float blue, float alpha) { 347 mController.setBackgroundColor(red, green, blue, alpha); 348 } 349 350 /** 351 * Can be used to optionally set the background to a bitmap. When set to something other than 352 * null, this overrides {@link CarouselView#setBackgroundColor(Float4)}. 353 * 354 * @param bitmap 355 */ 356 public void setBackgroundBitmap(Bitmap bitmap) { 357 mController.setBackgroundBitmap(bitmap); 358 } 359 360 /** 361 * Can be used to optionally set a "loading" detail bitmap. Typically, this is just a black 362 * texture with alpha = 0 to allow details to slowly fade in. 363 * 364 * @param bitmap 365 */ 366 public void setDetailLoadingBitmap(Bitmap bitmap) { 367 mController.setDetailLoadingBitmap(bitmap); 368 } 369 370 /** 371 * This texture is used to draw a line from the card alongside the texture detail. The line 372 * will be as wide as the texture. It can be used to give the line glow effects as well as 373 * allowing other blending effects. It is typically one dimensional, e.g. 3x1. 374 * 375 * @param bitmap 376 */ 377 public void setDetailLineBitmap(Bitmap bitmap) { 378 mController.setDetailLineBitmap(bitmap); 379 } 380 381 /** 382 * This geometry will be shown when no geometry has been loaded for a given slot. If not set, 383 * a quad will be drawn in its place. It is shared for all cards. 384 * 385 * @param mesh 386 */ 387 public void setDefaultGeometry(Mesh mesh) { 388 mController.setDefaultGeometry(mesh); 389 } 390 391 /** 392 * This is an intermediate version of the object to show while geometry is loading. If not set, 393 * a quad will be drawn in its place. It is shared for all cards. 394 * 395 * @param mesh 396 */ 397 public void setLoadingGeometry(Mesh mesh) { 398 mController.setLoadingGeometry(mesh); 399 } 400 401 /** 402 * Sets the callback for receiving events from RenderScript. 403 * 404 * @param callback 405 */ 406 public void setCallback(CarouselCallback callback) 407 { 408 mController.setCallback(callback); 409 } 410 411 /** 412 * Sets the startAngle for the Carousel. The start angle is the first position of the first 413 * slot draw. Cards will be drawn from this angle in a counter-clockwise manner around the 414 * Carousel. 415 * 416 * @param angle the angle, in radians. 417 */ 418 public void setStartAngle(float angle) 419 { 420 mController.setStartAngle(angle); 421 } 422 423 public void setRadius(float radius) { 424 mController.setRadius(radius); 425 } 426 427 public void setCardRotation(float cardRotation) { 428 mController.setCardRotation(cardRotation); 429 } 430 431 public void setCardsFaceTangent(boolean faceTangent) { 432 mController.setCardsFaceTangent(faceTangent); 433 } 434 435 public void setSwaySensitivity(float swaySensitivity) { 436 mController.setSwaySensitivity(swaySensitivity); 437 } 438 439 public void setFrictionCoefficient(float frictionCoefficient) { 440 mController.setFrictionCoefficient(frictionCoefficient); 441 } 442 443 public void setDragFactor(float dragFactor) { 444 mController.setDragFactor(dragFactor); 445 } 446 447 public void setLookAt(float[] eye, float[] at, float[] up) { 448 mController.setLookAt(eye, at, up); 449 } 450 451 /** 452 * This sets the number of cards in the distance that will be shown "rezzing in". 453 * These alpha values will be faded in from the background to the foreground over 454 * 'n' cards. A floating point value is used to allow subtly changing the rezzing in 455 * position. 456 * 457 * @param n the number of cards to rez in. 458 */ 459 public void setRezInCardCount(float n) { 460 mController.setRezInCardCount(n); 461 } 462 463 /** 464 * This sets the duration (in ms) that a card takes to fade in when loaded via a call 465 * to {@link CarouselView#setTextureForItem(int, Bitmap)}. The timer starts the 466 * moment {@link CarouselView#setTextureForItem(int, Bitmap)} is called and continues 467 * until all of the cards have faded in. Note: using large values will extend the 468 * animation until all cards have faded in. 469 * 470 * @param t 471 */ 472 public void setFadeInDuration(long t) { 473 mController.setFadeInDuration(t); 474 } 475 476 @Override 477 protected void onDetachedFromWindow() { 478 super.onDetachedFromWindow(); 479 mRenderScript = null; 480 if (mRS != null) { 481 mRS = null; 482 destroyRenderScript(); 483 } 484 mController.setRS(mRS, mRenderScript); 485 } 486 487 @Override 488 protected void onAttachedToWindow() { 489 super.onAttachedToWindow(); 490 ensureRenderScript(); 491 } 492 493 @Override 494 public boolean onTouchEvent(MotionEvent event) { 495 super.onTouchEvent(event); 496 final int action = event.getAction(); 497 final float x = event.getX(); 498 final float y = event.getY(); 499 500 if (mRenderScript == null) { 501 return true; 502 } 503 504 switch (action) { 505 case MotionEvent.ACTION_DOWN: 506 mTracking = true; 507 mController.onTouchStarted(x, y); 508 break; 509 510 case MotionEvent.ACTION_MOVE: 511 if (mTracking) { 512 mController.onTouchMoved(x, y); 513 } 514 break; 515 516 case MotionEvent.ACTION_UP: 517 mController.onTouchStopped(x, y); 518 mTracking = false; 519 break; 520 } 521 522 return true; 523 } 524} 525