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