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