CarouselView.java revision b378af500b36226635b6343b1d5009ee9af44fc1
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 com.android.ex.carousel.CarouselRS.CarouselCallback; 20 21import android.content.Context; 22import android.content.res.Resources; 23import android.graphics.Bitmap; 24import android.graphics.PixelFormat; 25import android.graphics.Bitmap.Config; 26import android.renderscript.FileA3D; 27import android.renderscript.Float4; 28import android.renderscript.Mesh; 29import android.renderscript.RSSurfaceView; 30import android.renderscript.RenderScriptGL; 31import android.util.AttributeSet; 32import android.util.Log; 33import android.view.MotionEvent; 34import android.view.SurfaceHolder; 35 36/** 37 * <p> 38 * This class represents the basic building block for using a 3D Carousel. The Carousel is 39 * basically a scene of cards and slots. The spacing between cards is dictated by the number 40 * of slots and the radius. The number of visible cards dictates how far the Carousel can be moved. 41 * If the number of cards exceeds the number of slots, then the Carousel will continue to go 42 * around until the last card can be seen. 43 */ 44public abstract class CarouselView extends RSSurfaceView { 45 private static final boolean USE_DEPTH_BUFFER = true; 46 private final int DEFAULT_SLOT_COUNT = 10; 47 private final float DEFAULT_RADIUS = 20.0f; 48 private final int DEFAULT_VISIBLE_DETAIL_COUNT = 3; 49 private final float DEFAULT_SWAY_SENSITIVITY = 0.0f; 50 private final float DEFAULT_FRICTION_COEFFICIENT = 10.0f; 51 private final float DEFAULT_DRAG_FACTOR = 0.25f; 52 private static final String TAG = "CarouselView"; 53 private static final boolean DBG = false; 54 private CarouselRS mRenderScript; 55 private RenderScriptGL mRS; 56 private Context mContext; 57 private boolean mTracking; 58 59 // These shadow the state of the renderer in case the surface changes so the surface 60 // can be restored to its previous state. 61 private Bitmap mDefaultBitmap; 62 private Bitmap mLoadingBitmap; 63 private Bitmap mBackgroundBitmap; 64 private Bitmap mDefaultLineBitmap = Bitmap.createBitmap( 65 new int[] {0x80ffffff, 0xffffffff, 0x80ffffff}, 0, 3, 3, 1, Bitmap.Config.ARGB_4444); 66 private Mesh mDefaultGeometry; 67 private Mesh mLoadingGeometry; 68 private int mCardCount = 0; 69 private int mVisibleSlots = 0; 70 private int mVisibleDetails = DEFAULT_VISIBLE_DETAIL_COUNT; 71 private boolean mDrawDetailBelowCard = false; 72 private boolean mDrawRuler = true; 73 private float mStartAngle; 74 private float mRadius = DEFAULT_RADIUS; 75 private float mCardRotation = 0.0f; 76 private float mSwaySensitivity = DEFAULT_SWAY_SENSITIVITY; 77 private float mFrictionCoefficient = DEFAULT_FRICTION_COEFFICIENT; 78 private float mDragFactor = DEFAULT_DRAG_FACTOR; 79 private int mSlotCount = DEFAULT_SLOT_COUNT; 80 private float mEye[] = { 20.6829f, 2.77081f, 16.7314f }; 81 private float mAt[] = { 14.7255f, -3.40001f, -1.30184f }; 82 private float mUp[] = { 0.0f, 1.0f, 0.0f }; 83 private Float4 mBackgroundColor = new Float4(0.0f, 0.0f, 0.0f, 1.0f); 84 private CarouselCallback mCarouselCallback; 85 private float mRezInCardCount = 0.0f; 86 private long mFadeInDuration = 250L; 87 private Bitmap mDetailLoadingBitmap = Bitmap.createBitmap( 88 new int[] {0}, 0, 1, 1, 1, Bitmap.Config.ARGB_4444); 89 90 public static class Info { 91 public Info(int _resId) { resId = _resId; } 92 public int resId; // resource for renderscript resource (e.g. R.raw.carousel) 93 } 94 95 public abstract Info getRenderScriptInfo(); 96 97 public CarouselView(Context context) { 98 this(context, null); 99 } 100 101 /** 102 * Constructor used when this widget is created from a layout file. 103 */ 104 public CarouselView(Context context, AttributeSet attrs) { 105 super(context, attrs); 106 mContext = context; 107 boolean useDepthBuffer = true; 108 ensureRenderScript(); 109 // TODO: add parameters to layout 110 } 111 112 private void ensureRenderScript() { 113 if (mRS == null) { 114 mRS = createRenderScript(USE_DEPTH_BUFFER); 115 } 116 mRenderScript = new CarouselRS(); 117 mRenderScript.init(mRS, getResources(), getRenderScriptInfo().resId); 118 } 119 120 @Override 121 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 122 super.surfaceChanged(holder, format, w, h); 123 //mRS.contextSetSurface(w, h, holder.getSurface()); 124 mRenderScript.init(mRS, getResources(), getRenderScriptInfo().resId); 125 setSlotCount(mSlotCount); 126 createCards(mCardCount); 127 setVisibleSlots(mVisibleSlots); 128 setVisibleDetails(mVisibleDetails); 129 setDrawDetailBelowCard(mDrawDetailBelowCard); 130 setDrawRuler(mDrawRuler); 131 setCallback(mCarouselCallback); 132 setDefaultBitmap(mDefaultBitmap); 133 setLoadingBitmap(mLoadingBitmap); 134 setDefaultGeometry(mDefaultGeometry); 135 setLoadingGeometry(mLoadingGeometry); 136 setBackgroundColor(mBackgroundColor.x, mBackgroundColor.y, mBackgroundColor.z, 137 mBackgroundColor.w); 138 setBackgroundBitmap(mBackgroundBitmap); 139 setDetailLineBitmap(mDefaultLineBitmap); 140 setStartAngle(mStartAngle); 141 setRadius(mRadius); 142 setCardRotation(mCardRotation); 143 setSwaySensitivity(mSwaySensitivity); 144 setFrictionCoefficient(mFrictionCoefficient); 145 setDragFactor(mDragFactor); 146 setLookAt(mEye, mAt, mUp); 147 setRezInCardCount(mRezInCardCount); 148 setFadeInDuration(mFadeInDuration); 149 setDetailLoadingBitmap(mDetailLoadingBitmap); 150 } 151 152 /** 153 * Loads geometry from a resource id. 154 * 155 * @param resId 156 * @return the loaded mesh or null if it cannot be loaded 157 */ 158 public Mesh loadGeometry(int resId) { 159 Resources res = mContext.getResources(); 160 FileA3D model = FileA3D.createFromResource(mRS, res, resId); 161 FileA3D.IndexEntry entry = model.getIndexEntry(0); 162 if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) { 163 return null; 164 } 165 return (Mesh) entry.getObject(); 166 } 167 168 /** 169 * Load A3D file from resource. If resId == 0, will clear geometry for this item. 170 * @param n 171 * @param resId 172 */ 173 public void setGeometryForItem(int n, Mesh mesh) { 174 if (mRenderScript != null) { 175 mRenderScript.setGeometry(n, mesh); 176 } 177 } 178 179 /** 180 * Set the number of slots around the Carousel. Basically equivalent to the poles horses 181 * might attach to on a real Carousel. 182 * 183 * @param n the number of slots 184 */ 185 public void setSlotCount(int n) { 186 mSlotCount = n; 187 if (mRenderScript != null) { 188 mRenderScript.setSlotCount(n); 189 } 190 } 191 192 /** 193 * Sets the number of visible slots around the Carousel. This is primarily used as a cheap 194 * form of clipping. The Carousel will never show more than this many cards. 195 * @param n the number of visible slots 196 */ 197 public void setVisibleSlots(int n) { 198 mVisibleSlots = n; 199 if (mRenderScript != null) { 200 mRenderScript.setVisibleSlots(n); 201 } 202 } 203 204 /** 205 * Set the number of detail textures that can be visible at one time. 206 * 207 * @param n the number of slots 208 */ 209 public void setVisibleDetails(int n) { 210 mVisibleDetails = n; 211 if (mRenderScript != null) { 212 mRenderScript.setVisibleDetails(n); 213 } 214 } 215 216 /** 217 * Set whether to draw the detail texture above or below the card. 218 * 219 * @param below False for above, true for below. 220 */ 221 public void setDrawDetailBelowCard(boolean below) { 222 mDrawDetailBelowCard = below; 223 if (mRenderScript != null) { 224 mRenderScript.setDrawDetailBelowCard(below); 225 } 226 } 227 228 /** 229 * Set whether to draw a ruler from the card to the detail texture 230 * 231 * @param drawRuler True to draw a ruler, false to draw nothing where the ruler would go. 232 */ 233 public void setDrawRuler(boolean drawRuler) { 234 mDrawRuler = drawRuler; 235 if (mRenderScript != null) { 236 mRenderScript.setDrawRuler(drawRuler); 237 } 238 } 239 240 /** 241 * This dictates how many cards are in the deck. If the number of cards is greater than the 242 * number of slots, then the Carousel goes around n / slot_count times. 243 * 244 * Can be called again to increase or decrease the number of cards. 245 * 246 * @param n the number of cards to create. 247 */ 248 public void createCards(int n) { 249 mCardCount = n; 250 if (mRenderScript != null) { 251 mRenderScript.createCards(n); 252 } 253 } 254 255 public int getCardCount() { 256 return mCardCount; 257 } 258 259 /** 260 * This sets the texture on card n. It should only be called in response to 261 * {@link CarouselCallback#onRequestTexture(int)}. Since there's no guarantee 262 * that a given texture is still on the screen, replacing this texture should be done 263 * by first setting it to null and then waiting for the next 264 * {@link CarouselCallback#onRequestTexture(int)} to swap it with the new one. 265 * 266 * @param n the card given by {@link CarouselCallback#onRequestTexture(int)} 267 * @param bitmap the bitmap image to show 268 */ 269 public void setTextureForItem(int n, Bitmap bitmap) { 270 // Also check against mRS, to handle the case where the result is being delivered by a 271 // background thread but the sender no longer exists. 272 if (mRenderScript != null && mRS != null) { 273 if (DBG) Log.v(TAG, "setTextureForItem(" + n + ")"); 274 mRenderScript.setTexture(n, bitmap); 275 if (DBG) Log.v(TAG, "done"); 276 } 277 } 278 279 /** 280 * This sets the detail texture that floats above card n. It should only be called in response 281 * to {@link CarouselCallback#onRequestDetailTexture(int)}. Since there's no guarantee 282 * that a given texture is still on the screen, replacing this texture should be done 283 * by first setting it to null and then waiting for the next 284 * {@link CarouselCallback#onRequestDetailTexture(int)} to swap it with the new one. 285 * 286 * @param n the card to set detail texture for 287 * @param offx an optional offset to apply to the texture (in pixels) from top of detail line 288 * @param offy an optional offset to apply to the texture (in pixels) from top of detail line 289 * @param loffx an optional offset to apply to the line (in pixels) from left edge of card 290 * @param loffy an optional offset to apply to the line (in pixels) from top of screen 291 * @param bitmap the bitmap to show as the detail 292 */ 293 public void setDetailTextureForItem(int n, float offx, float offy, float loffx, float loffy, 294 Bitmap bitmap) { 295 if (mRenderScript != null) { 296 if (DBG) Log.v(TAG, "setDetailTextureForItem(" + n + ")"); 297 mRenderScript.setDetailTexture(n, offx, offy, loffx, loffy, bitmap); 298 if (DBG) Log.v(TAG, "done"); 299 } 300 } 301 302 /** 303 * Sets the bitmap to show on a card when the card draws the very first time. 304 * Generally, this bitmap will only be seen during the first few frames of startup 305 * or when the number of cards are changed. It can be ignored in most cases, 306 * as the cards will generally only be in the loading or loaded state. 307 * 308 * @param bitmap 309 */ 310 public void setDefaultBitmap(Bitmap bitmap) { 311 mDefaultBitmap = bitmap; 312 if (mRenderScript != null) { 313 mRenderScript.setDefaultBitmap(bitmap); 314 } 315 } 316 317 /** 318 * Sets the bitmap to show on the card while the texture is loading. It is set to this 319 * value just before {@link CarouselCallback#onRequestTexture(int)} is called and changed 320 * when {@link CarouselView#setTextureForItem(int, Bitmap)} is called. It is shared by all 321 * cards. 322 * 323 * @param bitmap 324 */ 325 public void setLoadingBitmap(Bitmap bitmap) { 326 mLoadingBitmap = bitmap; 327 if (mRenderScript != null) { 328 mRenderScript.setLoadingBitmap(bitmap); 329 } 330 } 331 332 /** 333 * Sets background to specified color. If a background texture is specified with 334 * {@link CarouselView#setBackgroundBitmap(Bitmap)}, then this call has no effect. 335 * 336 * @param red the amount of red 337 * @param green the amount of green 338 * @param blue the amount of blue 339 * @param alpha the amount of alpha 340 */ 341 public void setBackgroundColor(float red, float green, float blue, float alpha) { 342 mBackgroundColor = new Float4(red, green, blue, alpha); 343 if (mRenderScript != null) { 344 mRenderScript.setBackgroundColor(mBackgroundColor); 345 } 346 } 347 348 /** 349 * Can be used to optionally set the background to a bitmap. When set to something other than 350 * null, this overrides {@link CarouselView#setBackgroundColor(Float4)}. 351 * 352 * @param bitmap 353 */ 354 public void setBackgroundBitmap(Bitmap bitmap) { 355 mBackgroundBitmap = bitmap; 356 if (mRenderScript != null) { 357 mRenderScript.setBackgroundTexture(bitmap); 358 } 359 } 360 361 /** 362 * Can be used to optionally set a "loading" detail bitmap. Typically, this is just a black 363 * texture with alpha = 0 to allow details to slowly fade in. 364 * 365 * @param bitmap 366 */ 367 public void setDetailLoadingBitmap(Bitmap bitmap) { 368 mDetailLoadingBitmap = bitmap; 369 if (mRenderScript != null) { 370 mRenderScript.setDetailLoadingTexture(bitmap); 371 } 372 } 373 374 /** 375 * This texture is used to draw a line from the card alongside the texture detail. The line 376 * will be as wide as the texture. It can be used to give the line glow effects as well as 377 * allowing other blending effects. It is typically one dimensional, e.g. 3x1. 378 * 379 * @param bitmap 380 */ 381 public void setDetailLineBitmap(Bitmap bitmap) { 382 mDefaultLineBitmap = bitmap; 383 if (mRenderScript != null) { 384 mRenderScript.setDetailLineTexture(bitmap); 385 } 386 } 387 388 /** 389 * This geometry will be shown when no geometry has been loaded for a given slot. If not set, 390 * a quad will be drawn in its place. It is shared for all cards. 391 * 392 * @param mesh 393 */ 394 public void setDefaultGeometry(Mesh mesh) { 395 mDefaultGeometry = mesh; 396 if (mRenderScript != null) { 397 mRenderScript.setDefaultGeometry(mesh); 398 } 399 } 400 401 /** 402 * This is an intermediate version of the object to show while geometry is loading. If not set, 403 * a quad will be drawn in its place. It is shared for all cards. 404 * 405 * @param mesh 406 */ 407 public void setLoadingGeometry(Mesh mesh) { 408 mLoadingGeometry = mesh; 409 if (mRenderScript != null) { 410 mRenderScript.setLoadingGeometry(mesh); 411 } 412 } 413 414 /** 415 * Sets the callback for receiving events from RenderScript. 416 * 417 * @param callback 418 */ 419 public void setCallback(CarouselCallback callback) 420 { 421 mCarouselCallback = callback; 422 if (mRenderScript != null) { 423 mRenderScript.setCallback(callback); 424 } 425 } 426 427 /** 428 * Sets the startAngle for the Carousel. The start angle is the first position of the first 429 * slot draw. Cards will be drawn from this angle in a counter-clockwise manner around the 430 * Carousel. 431 * 432 * @param angle the angle, in radians. 433 */ 434 public void setStartAngle(float angle) 435 { 436 mStartAngle = angle; 437 if (mRenderScript != null) { 438 mRenderScript.setStartAngle(angle); 439 } 440 } 441 442 public void setRadius(float radius) { 443 mRadius = radius; 444 if (mRenderScript != null) { 445 mRenderScript.setRadius(radius); 446 } 447 } 448 449 public void setCardRotation(float cardRotation) { 450 mCardRotation = cardRotation; 451 if (mRenderScript != null) { 452 mRenderScript.setCardRotation(cardRotation); 453 } 454 } 455 456 public void setSwaySensitivity(float swaySensitivity) { 457 mSwaySensitivity = swaySensitivity; 458 if (mRenderScript != null) { 459 mRenderScript.setSwaySensitivity(swaySensitivity); 460 } 461 } 462 463 public void setFrictionCoefficient(float frictionCoefficient) { 464 mFrictionCoefficient = frictionCoefficient; 465 if (mRenderScript != null) { 466 mRenderScript.setFrictionCoefficient(frictionCoefficient); 467 } 468 } 469 470 public void setDragFactor(float dragFactor) { 471 mDragFactor = dragFactor; 472 if (mRenderScript != null) { 473 mRenderScript.setDragFactor(dragFactor); 474 } 475 } 476 477 public void setLookAt(float[] eye, float[] at, float[] up) { 478 mEye = eye; 479 mAt = at; 480 mUp = up; 481 if (mRenderScript != null) { 482 mRenderScript.setLookAt(eye, at, up); 483 } 484 } 485 486 public void requestFirstCardPosition() { 487 if (mRenderScript != null) { 488 mRenderScript.requestFirstCardPosition(); 489 } 490 } 491 492 /** 493 * This sets the number of cards in the distance that will be shown "rezzing in". 494 * These alpha values will be faded in from the background to the foreground over 495 * 'n' cards. A floating point value is used to allow subtly changing the rezzing in 496 * position. 497 * 498 * @param n the number of cards to rez in. 499 */ 500 public void setRezInCardCount(float n) { 501 mRezInCardCount = n; 502 if (mRenderScript != null) { 503 mRenderScript.setRezInCardCount(n); 504 } 505 } 506 507 /** 508 * This sets the duration (in ms) that a card takes to fade in when loaded via a call 509 * to {@link CarouselView#setTextureForItem(int, Bitmap)}. The timer starts the 510 * moment {@link CarouselView#setTextureForItem(int, Bitmap)} is called and continues 511 * until all of the cards have faded in. Note: using large values will extend the 512 * animation until all cards have faded in. 513 * 514 * @param t 515 */ 516 public void setFadeInDuration(long t) { 517 mFadeInDuration = t; 518 if (mRenderScript != null) { 519 mRenderScript.setFadeInDuration(t); 520 } 521 } 522 523 @Override 524 protected void onDetachedFromWindow() { 525 super.onDetachedFromWindow(); 526 mRenderScript = null; 527 if (mRS != null) { 528 mRS = null; 529 destroyRenderScript(); 530 } 531 } 532 533 @Override 534 protected void onAttachedToWindow() { 535 super.onAttachedToWindow(); 536 ensureRenderScript(); 537 } 538 539 @Override 540 public boolean onTouchEvent(MotionEvent event) { 541 final int action = event.getAction(); 542 final float x = event.getX(); 543 final float y = event.getY(); 544 545 if (mRenderScript == null) { 546 return true; 547 } 548 549 switch (action) { 550 case MotionEvent.ACTION_DOWN: 551 mTracking = true; 552 mRenderScript.doStart(x, y); 553 break; 554 555 case MotionEvent.ACTION_MOVE: 556 if (mTracking) { 557 mRenderScript.doMotion(x, y); 558 } 559 break; 560 561 case MotionEvent.ACTION_UP: 562 mRenderScript.doStop(x, y); 563 mTracking = false; 564 break; 565 } 566 567 return true; 568 } 569} 570