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