javaandroidcoverflow

android coverflow without gallery


Hello I have been searching for cover flow android that DOES NOT use gallery . Please don't offer solution that uses gallery such as fancyflow or neil davis because they are using Gallery and its being predicated

Is there something like that at all ?


Solution

  • CoverFlowView.java

    import java.lang.ref.WeakReference;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    import java.util.SortedSet;
    import java.util.TreeSet;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Camera;
    import android.graphics.Matrix;
    import android.util.AttributeSet;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.Animation;
    import android.view.animation.Transformation;
    import android.widget.FrameLayout;
    import android.widget.HorizontalScrollView;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    
    public class CoverFlowView extends LinearLayout {
    public static class Config {
        public int COVER_SPACING = 60;
        public int CENTER_COVER_OFFSET = 130;
        public float SIDE_COVER_ANGLE = 40;
        public int SIDE_COVER_ZPOSITION = 120;
        public float REFLECTION_FRACTION = 0.15f;
        public long FOCUS_ANIMATION_DURATION = 200;
        public int COVER_BUFFER = 6;
        public long BLUR_ANIMATION_DURATION = 120;
        public int DROP_SHADOW_RADIUS = 15;
        public boolean HORIZONTAL_SCROLLBAR_ENABLED = false;
        public boolean FADING_EDGES_ENABLED = true;
        public float IMAGE_SCALE_X = 1;
        public float IMAGE_SCALE_Y = 1;
        public long LONG_CLICK_DURATION = 2000;
    }
    
    Config mConfig = new Config();
    WeakReference<DataSource> mDataSource;
    WeakReference<Listener> mListener;
    Set<CoverFlowItem> mOffscreenCovers = new HashSet<CoverFlowItem>();
    Map<Integer, CoverFlowItem> mOnscreenCovers = new HashMap<Integer, CoverFlowItem>();
    Map<Integer, Bitmap> mCoverImages = new HashMap<Integer, Bitmap>();
    Map<Integer, Integer> mCoverImageHeights = new HashMap<Integer, Integer>();
    Bitmap mDefaultBitmap;
    int mDefaultBitmapHeight;
    float mDefaultImageHeight;
    ScrollView mScrollView;
    ViewGroup mItemContainer;
    Thread mLongClickThread;
    boolean mCancelNextClick;
    
    int mLowerVisibleCover = -1;
    int mUpperVisibleCover = -1;
    int mNumberOfImages;
    int mBeginningCover;
    CoverFlowItem mSelectedCoverView = null;
    
    int mHalfScreenHeight;
    int mHalfScreenWidth;
    
    boolean mIsSingleTap;
    boolean mIsDraggingCover;
    float mStartScrollX;
    float mStartX;
    
    SortedSet<Integer> mTouchedCovers = new TreeSet<Integer>();
    
    public CoverFlowView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs);
        setUpInitialState();
    }
    
    public CoverFlowView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setUpInitialState();
    }
    
    public CoverFlowView(Context context) {
        super(context);
        setUpInitialState();
    }
    
    public Config getConfig() {
        return mConfig;
    }
    
    void setUpInitialState() {
    
        // Create the scrollView
        mScrollView = new ScrollView(getContext()) {
            // Catch trackball events
            @Override
            public boolean onTrackballEvent(MotionEvent event) {
                return CoverFlowView.this.onTrackballEvent(event);
            }
    
        };
        mScrollView.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                return CoverFlowView.this.onTouchEvent(event);
            }
        });
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
                LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        mScrollView.setLayoutParams(params);
        mScrollView
                .setHorizontalScrollBarEnabled(mConfig.HORIZONTAL_SCROLLBAR_ENABLED);
        mScrollView
                .setHorizontalFadingEdgeEnabled(mConfig.FADING_EDGES_ENABLED);
        addView(mScrollView);
    
        // Create an intermediate LinearLayout
        LinearLayout linearLayout = new LinearLayout(getContext());
        mScrollView.addView(linearLayout);
    
        // Create the item container
        mItemContainer = new FrameLayout(getContext());
        linearLayout.addView(mItemContainer);
    
    }
    
    CoverFlowItem coverForIndex(int coverIndex) {
        CoverFlowItem coverItem = dequeueReusableCover();
        if (null == coverItem) {
            coverItem = new CoverFlowItem(getContext());
            coverItem.setScaleType(ImageView.ScaleType.FIT_XY);
            coverItem.setScaleX(mConfig.IMAGE_SCALE_X);
            coverItem.setScaleY(mConfig.IMAGE_SCALE_Y);
            coverItem.setOnTouchListener(new OnTouchListener() {
                public boolean onTouch(View v, MotionEvent event) {
                    onTouchItem((CoverFlowItem) v, event);
                    return false;
                }
            });
        }
        coverItem.setNumber(coverIndex);
        return coverItem;
    }
    
    void updateCoverBitmap(CoverFlowItem cover) {
        int coverNumber = cover.getNumber();
        Bitmap bitmap = mCoverImages.get(coverNumber);
        if (null != bitmap) {
            Integer coverImageHeight = mCoverImageHeights.get(coverNumber);
            if (null != coverImageHeight)
                cover.setImageBitmap(bitmap, coverImageHeight,
                        mConfig.REFLECTION_FRACTION);
        } else {
            cover.setImageBitmap(mDefaultBitmap, mDefaultBitmapHeight,
                    mConfig.REFLECTION_FRACTION);
            mDataSource.get().requestBitmapForIndex(this, coverNumber);
        }
    }
    
    void layoutCover(CoverFlowItem cover, int selectedCover, boolean animated) {
        if (null == cover)
            return;
    
        int coverNumber = cover.getNumber();
        int newX = mHalfScreenWidth + cover.getNumber() * mConfig.COVER_SPACING
                - (int) (cover.getCoverWidth() / 2.0f);
        int newY = mHalfScreenHeight - cover.getCoverHeight() / 2;
    
        ItemAnimation oldAnimation = (ItemAnimation) cover.getAnimation();
        float oldAngle = oldAnimation != null ? oldAnimation
                .getStopAngleDegrees() : 0;
        int oldZOffset = oldAnimation != null ? oldAnimation.getStopZOffset()
                : 0;
        int oldXOffset = oldAnimation != null ? oldAnimation.getStopXOffset()
                : 0;
    
        ItemAnimation anim = null;
    
        if (coverNumber < selectedCover) {
            if (oldAngle != mConfig.SIDE_COVER_ANGLE
                    || oldXOffset != -mConfig.CENTER_COVER_OFFSET
                    || oldZOffset != mConfig.SIDE_COVER_ZPOSITION) {
                anim = new ItemAnimation();
                anim.setRotation(oldAngle, mConfig.SIDE_COVER_ANGLE);
                anim.setViewDimensions(cover.getCoverWidth(),
                        cover.getOriginalCoverHeight());
                anim.setXTranslation(oldXOffset, -mConfig.CENTER_COVER_OFFSET);
                anim.setZTranslation(oldZOffset, mConfig.SIDE_COVER_ZPOSITION);
                if (animated)
                    anim.setDuration(mConfig.BLUR_ANIMATION_DURATION);
                else
                    anim.setStatic();
            }
        } else if (coverNumber > selectedCover) {
            if (oldAngle != -mConfig.SIDE_COVER_ANGLE
                    || oldXOffset != mConfig.CENTER_COVER_OFFSET
                    || oldZOffset != mConfig.SIDE_COVER_ZPOSITION) {
                anim = new ItemAnimation();
                anim.setRotation(oldAngle, -mConfig.SIDE_COVER_ANGLE);
                anim.setViewDimensions(cover.getCoverWidth(),
                        cover.getOriginalCoverHeight());
                anim.setXTranslation(oldXOffset, mConfig.CENTER_COVER_OFFSET);
                anim.setZTranslation(oldZOffset, mConfig.SIDE_COVER_ZPOSITION);
                if (animated)
                    anim.setDuration(mConfig.BLUR_ANIMATION_DURATION);
    
                else
                    anim.setStatic();
            }
        } else {
            if (oldAngle != 0 || oldXOffset != 0 || oldZOffset != 0) {
                anim = new ItemAnimation();
                anim.setRotation(oldAngle, 0);
                anim.setViewDimensions(cover.getCoverWidth(),
                        cover.getOriginalCoverHeight());
                anim.setXTranslation(oldXOffset, 0);
                anim.setZTranslation(oldZOffset, 0);
                if (animated)
                    anim.setDuration(mConfig.FOCUS_ANIMATION_DURATION);
                else
                    anim.setStatic();
                anim.setAnimationListener(new Animation.AnimationListener() {
                    public void onAnimationStart(Animation animation) {
                    }
    
                    public void onAnimationRepeat(Animation animation) {
                    }
    
                    public void onAnimationEnd(Animation animation) {
                        mSelectedCoverView.bringToFront();
                        layoutZ(mSelectedCoverView.getNumber(),
                                mLowerVisibleCover, mUpperVisibleCover);
                    }
                });
            }
        }
    
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                cover.getLayoutParams());
        params.setMargins(newX, newY, 0, 0);
        params.gravity = Gravity.LEFT | Gravity.TOP;
        cover.setLayoutParams(params);
    
        if (null != anim)
            cover.startAnimation(anim);
    }
    
    void layoutCovers(int selected, int lowerBound, int upperBound) {
        CoverFlowItem cover;
    
        for (int i = lowerBound; i <= upperBound; i++) {
            cover = mOnscreenCovers.get(i);
            layoutCover(cover, selected, true);
        }
    }
    
    void layoutZ(int selected, int lowerBound, int upperBound) {
        CoverFlowItem cover;
        for (int i = upperBound; i > selected; i--) {
            cover = mOnscreenCovers.get(i);
            if (null != cover)
                mItemContainer.bringChildToFront(cover);
        }
        for (int i = lowerBound; i <= selected; i++) {
            cover = mOnscreenCovers.get(i);
            if (null != cover)
                mItemContainer.bringChildToFront(cover);
        }
    
    }
    
    CoverFlowItem dequeueReusableCover() {
        CoverFlowItem item = null;
        if (!mOffscreenCovers.isEmpty()) {
            item = mOffscreenCovers.iterator().next();
            mOffscreenCovers.remove(item);
        }
        return item;
    }
    
    public void setBitmapForIndex(Bitmap bitmap, int index) {
        Bitmap bitmapWithReflection = CoverFlowItem
                .createReflectedBitmap(bitmap, mConfig.REFLECTION_FRACTION,
                        mConfig.DROP_SHADOW_RADIUS);
        setReflectedBitmapForIndex(bitmapWithReflection, index);
    }
    
    public void setReflectedBitmapForIndex(Bitmap bitmapWithReflection,
            int index) {
        mCoverImages.put(index, bitmapWithReflection);
        int originalHeight = (int) ((int) (bitmapWithReflection.getHeight() - 2 * mConfig.DROP_SHADOW_RADIUS) / (1 + mConfig.REFLECTION_FRACTION));
        mCoverImageHeights.put(index, originalHeight);
    
        // If this cover is onscreen, set its image and call layoutCover.
        CoverFlowItem cover = mOnscreenCovers.get(index);
        if (null != cover) {
            cover.setImageBitmap(bitmapWithReflection, originalHeight,
                    mConfig.REFLECTION_FRACTION);
            layoutCover(cover, mSelectedCoverView.getNumber(), false);
        }
    }
    
    public Bitmap[] getReflectedBitmaps() {
        Bitmap[] result = new Bitmap[mCoverImages.size()];
        for (int i = 0; i < result.length; i++)
            result[i] = mCoverImages.get(i);
        return result;
    }
    
    //
    // @Override
    // protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // mScrollView.layout(l, t, r, b);
    // }
    //
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(
                mNumberOfImages * mConfig.COVER_SPACING
                        + MeasureSpec.getSize(widthMeasureSpec),
                LayoutParams.FILL_PARENT);
        mItemContainer.setLayoutParams(params);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHalfScreenWidth = w / 2;
        mHalfScreenHeight = h / 2;
    
        int lowerBound = Math.max(-1,
                (mSelectedCoverView != null ? mSelectedCoverView.getNumber()
                        : 0) - mConfig.COVER_BUFFER);
        int upperBound = Math.min(mNumberOfImages - 1,
                (mSelectedCoverView != null ? mSelectedCoverView.getNumber()
                        : 0) + mConfig.COVER_BUFFER);
        layoutCovers(
                mSelectedCoverView != null ? mSelectedCoverView.getNumber() : 0,
                lowerBound, upperBound);
    
        centerOnSelectedCover(false);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        try {
            switch (event.getAction()) {
    
            case MotionEvent.ACTION_DOWN:
                mIsSingleTap = event.getPointerCount() == 1;
                final int index = mSelectedCoverView.getNumber();
                if (mIsSingleTap) {
                    mStartX = event.getX(0);
    
                    int lowest = mTouchedCovers.first();
                    int highest = mTouchedCovers.last();
    
                    if (lowest <= mSelectedCoverView.getNumber()
                            && highest >= mSelectedCoverView.getNumber()
                            && null != mListener && null != mListener.get()
                            && null == mLongClickThread) {
                        mLongClickThread = new Thread(new Runnable() {
                            public void run() {
                                try {
                                    Thread.sleep(getConfig().LONG_CLICK_DURATION);
                                    post(new Runnable() {
    
                                        public void run() {
                                            mListener.get()
                                                    .onSelectionLongClicked(
                                                            CoverFlowView.this,
                                                            index);
                                            mCancelNextClick = true;
                                        }
                                    });
                                } catch (InterruptedException e) {
                                }
    
                            }
                        });
                        mLongClickThread.start();
                        mCancelNextClick = false;
    
                    }
    
                }
    
                mBeginningCover = mSelectedCoverView.getNumber();
                mStartScrollX = event.getX(0) + mScrollView.getScrollX();
                break;
            case MotionEvent.ACTION_MOVE:
                int scrollOffset = (int) (mStartScrollX - event.getX(0));
                int xOffset = (int) Math.abs(event.getX(0) - mStartX);
    
                // If finger moves too much, not a single tap anymore:
                mIsSingleTap = mIsSingleTap && (xOffset < 20);
    
                if (!mIsSingleTap) {
                    // Cancel long click
                    if (null != mLongClickThread
                            && Thread.State.TERMINATED != mLongClickThread
                                    .getState()) {
                        mLongClickThread.interrupt();
                        mLongClickThread = null;
                    }
    
                    // Update the scroll position
                    mScrollView
                            .scrollTo(scrollOffset, mScrollView.getScrollY());
    
                    // Select new cover
                    int newCover = scrollOffset / mConfig.COVER_SPACING;
    
                    // make sure we're not out of bounds:
                    if (newCover < 0)
                        newCover = 0;
                    else if (newCover >= mNumberOfImages)
                        newCover = mNumberOfImages - 1;
    
                    // Select newCover if appropriate
                    if (newCover != mSelectedCoverView.getNumber()) {
                        setSelectedCover(newCover);
                        // Notify listener
                        if (null != mListener && null != mListener.get())
                            mListener.get().onSelectionChanging(this,
                                    mSelectedCoverView.getNumber());
                    }
                }
    
                break;
            case MotionEvent.ACTION_UP:
    
                // Cancel long click
                if (null != mLongClickThread) {
                    if (Thread.State.TERMINATED != mLongClickThread.getState())
                        mLongClickThread.interrupt();
                    mLongClickThread = null;
                }
    
                if (mIsSingleTap && 0 < mTouchedCovers.size()) {
                    int lowest = mTouchedCovers.first();
                    int highest = mTouchedCovers.last();
                    if (mSelectedCoverView.getNumber() < lowest)
                        setSelectedCover(lowest);
                    else if (mSelectedCoverView.getNumber() > highest)
                        setSelectedCover(highest);
                    else if (lowest <= mSelectedCoverView.getNumber()
                            && highest >= mSelectedCoverView.getNumber()
                            && null != mListener && null != mListener.get()) {
                        if (!mCancelNextClick) {
                            mListener.get().onSelectionClicked(this,
                                    mSelectedCoverView.getNumber());
                        }
                    }
    
                }
    
                mCancelNextClick = false;
    
                // Smooth scroll to the center of the cover
                mScrollView.smoothScrollTo(mSelectedCoverView.getNumber()
                        * mConfig.COVER_SPACING, mScrollView.getScrollY());
    
                if (mBeginningCover != mSelectedCoverView.getNumber()) {
                    // Notify listener
                    if (null != mListener && null != mListener.get())
                        mListener.get().onSelectionChanged(this,
                                mSelectedCoverView.getNumber());
                }
    
                // Clear touched covers
                mTouchedCovers.clear();
    
                break;
            }
        } catch (Exception e) {
    
        }
    
        return true;
    }
    
    @Override
    public boolean onTrackballEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE:
            int newCover = -1;
            if (event.getX(0) > 0)
                newCover = mSelectedCoverView.getNumber() + 1;
            else if (event.getX(0) < 0)
                newCover = mSelectedCoverView.getNumber() - 1;
    
            if (0 <= newCover && mNumberOfImages > newCover
                    && mSelectedCoverView.getNumber() != newCover) {
                setSelectedCover(newCover);
                mScrollView.smoothScrollTo(newCover * mConfig.COVER_SPACING,
                        mScrollView.getScrollY());
                // Notify listener
                if (null != mListener && null != mListener.get())
                    mListener.get().onSelectionChanged(this,
                            mSelectedCoverView.getNumber());
    
            }
            break;
        }
        return true;
    }
    
    void onTouchItem(CoverFlowItem cover, MotionEvent event) {
        mTouchedCovers.add(cover.getNumber());
    }
    
    public void clear() {
        mNumberOfImages = 0;
        mSelectedCoverView = null;
        mOffscreenCovers.clear();
        mOnscreenCovers.clear();
        mCoverImages.clear();
        mCoverImageHeights.clear();
        mDefaultBitmap = null;
        mLowerVisibleCover = -1;
        mUpperVisibleCover = -1;
    
        // Recreate the item container to force free memory
        LinearLayout parent = (LinearLayout) mItemContainer.getParent();
        parent.removeView(mItemContainer);
        mItemContainer = new FrameLayout(getContext());
        parent.addView(mItemContainer);
    
    }
    
    public void setNumberOfImages(int numberOfImages) {
        mNumberOfImages = numberOfImages;
    
        int lowerBound = Math.max(-1,
                (mSelectedCoverView != null ? mSelectedCoverView.getNumber()
                        : 0) - mConfig.COVER_BUFFER);
        int upperBound = Math.min(mNumberOfImages - 1,
                (mSelectedCoverView != null ? mSelectedCoverView.getNumber()
                        : 0) + mConfig.COVER_BUFFER);
        if (null != mSelectedCoverView)
            layoutCovers(mSelectedCoverView.getNumber(), lowerBound, upperBound);
        else
            setSelectedCover(0);
    
        centerOnSelectedCover(false);
    }
    
    public void setDefaultBitmap(Bitmap bitmap) {
        mDefaultBitmapHeight = null != bitmap ? bitmap.getHeight() : 0;
        mDefaultBitmap = bitmap;
    }
    
    public void setDataSource(DataSource dataSource) {
        mDataSource = new WeakReference<DataSource>(dataSource);
        setDefaultBitmap(dataSource.defaultBitmap());
    }
    
    public void setListener(Listener listener) {
        mListener = new WeakReference<Listener>(listener);
    }
    
    public void centerOnSelectedCover(final boolean animated) {
        if (null == mSelectedCoverView)
            return;
    
        final int offset = mConfig.COVER_SPACING
                * mSelectedCoverView.getNumber();
        mScrollView.post(new Runnable() {
            public void run() {
                if (animated)
                    mScrollView.smoothScrollTo(offset, 0);
                else
                    mScrollView.scrollTo(offset, 0);
            }
        });
    }
    
    public void setSelectedCover(int newSelectedCover) {
        if (null != mSelectedCoverView
                && newSelectedCover == mSelectedCoverView.getNumber())
            return;
    
        if (newSelectedCover >= mNumberOfImages)
            return;
    
        CoverFlowItem cover;
        int newLowerBound = Math
                .max(0, newSelectedCover - mConfig.COVER_BUFFER);
        int newUpperBound = Math.min(mNumberOfImages - 1, newSelectedCover
                + mConfig.COVER_BUFFER);
        if (null == mSelectedCoverView) {
            // Allocate and display covers from newLower to newUpper bounds.
            for (int i = newLowerBound; i <= newUpperBound; i++) {
                cover = coverForIndex(i);
                mOnscreenCovers.put(i, cover);
                updateCoverBitmap(cover);
                if (i == newSelectedCover) {
                    // We'll add it later
                    continue;
                } else if (i < newSelectedCover) {
                    mItemContainer.addView(cover);
                } else {
                    mItemContainer.addView(cover, 0);
                }
                layoutCover(cover, newSelectedCover, false);
            }
            // Add the selected cover
            cover = mOnscreenCovers.get(newSelectedCover);
            mItemContainer.addView(cover);
            layoutCover(cover, newSelectedCover, false);
    
            mLowerVisibleCover = newLowerBound;
            mUpperVisibleCover = newUpperBound;
            mSelectedCoverView = cover;
            return;
        } else {
            layoutZ(mSelectedCoverView.getNumber(), mLowerVisibleCover,
                    mUpperVisibleCover);
    
        }
    
        if ((newLowerBound > mUpperVisibleCover)
                || (newUpperBound < mLowerVisibleCover)) {
            // They do not overlap at all.
            // This does not animate--assuming it's programmatically set from
            // view controller.
            // Recycle all onscreen covers.
            for (int i = mLowerVisibleCover; i <= mUpperVisibleCover; i++) {
                cover = mOnscreenCovers.get(i);
                mOffscreenCovers.add(cover);
                mItemContainer.removeView(cover);
                mOnscreenCovers.remove(i);
            }
    
            // Move all available covers to new location.
            for (int i = newLowerBound; i <= newUpperBound; i++) {
                cover = coverForIndex(i);
                mOnscreenCovers.put(i, cover);
                updateCoverBitmap(cover);
                if (i == newSelectedCover) {
                    // We'll add it later
                    continue;
                } else if (i < newSelectedCover) {
                    mItemContainer.addView(cover);
                } else {
                    mItemContainer.addView(cover, 0);
                }
            }
            cover = mOnscreenCovers.get(newSelectedCover);
            mItemContainer.addView(cover);
    
            mLowerVisibleCover = newLowerBound;
            mUpperVisibleCover = newUpperBound;
            mSelectedCoverView = cover;
            layoutCovers(newSelectedCover, newLowerBound, newUpperBound);
    
            return;
    
        } else if (newSelectedCover > mSelectedCoverView.getNumber()) {
            // Move covers that are now out of range on the left to the right
            // side,
            // but only if appropriate (within the range set by newUpperBound).
            for (int i = mLowerVisibleCover; i < newLowerBound; i++) {
                cover = mOnscreenCovers.get(i);
                if (mUpperVisibleCover < newUpperBound) {
                    // Tack it on the right side.
                    mUpperVisibleCover++;
                    cover.setNumber(mUpperVisibleCover);
                    updateCoverBitmap(cover);
                    mOnscreenCovers.put(cover.getNumber(), cover);
                    layoutCover(cover, newSelectedCover, false);
                } else {
                    // Recycle this cover.
                    mOffscreenCovers.add(cover);
                    mItemContainer.removeView(cover);
                }
                mOnscreenCovers.remove(i);
            }
    
            mLowerVisibleCover = newLowerBound;
    
            // Add in any missing covers on the right up to the newUpperBound.
            for (int i = mUpperVisibleCover + 1; i <= newUpperBound; i++) {
                cover = coverForIndex(i);
                mOnscreenCovers.put(i, cover);
                updateCoverBitmap(cover);
                mItemContainer.addView(cover, 0);
                layoutCover(cover, newSelectedCover, false);
            }
            mUpperVisibleCover = newUpperBound;
        } else {
            // Move covers that are now out of range on the right to the left
            // side,
            // but only if appropriate (within the range set by newLowerBound).
            for (int i = mUpperVisibleCover; i > newUpperBound; i--) {
                cover = mOnscreenCovers.get(i);
                if (mLowerVisibleCover > newLowerBound) {
                    // Tack it on the left side.
                    mLowerVisibleCover--;
                    cover.setNumber(mLowerVisibleCover);
                    updateCoverBitmap(cover);
                    mOnscreenCovers.put(cover.getNumber(), cover);
                    layoutCover(cover, newSelectedCover, false);
    
                } else {
                    // Recycle this cover.
                    mOffscreenCovers.add(cover);
                    mItemContainer.removeView(cover);
                }
                mOnscreenCovers.remove(i);
            }
    
            mUpperVisibleCover = newUpperBound;
    
            // Add in any missing covers on the left down to the newLowerBound.
            for (int i = mLowerVisibleCover - 1; i >= newLowerBound; i--) {
                cover = coverForIndex(i);
                mOnscreenCovers.put(i, cover);
                updateCoverBitmap(cover);
                mItemContainer.addView(cover, 0);
                layoutCover(cover, newSelectedCover, false);
            }
    
            mLowerVisibleCover = newLowerBound;
        }
    
        if (mSelectedCoverView.getNumber() > newSelectedCover) {
            layoutCovers(newSelectedCover, newSelectedCover,
                    mSelectedCoverView.getNumber());
        } else if (newSelectedCover > mSelectedCoverView.getNumber()) {
            layoutCovers(newSelectedCover, mSelectedCoverView.getNumber(),
                    newSelectedCover);
        }
    
        mSelectedCoverView = mOnscreenCovers.get(newSelectedCover);
    
    }
    
    private static class ScrollView extends HorizontalScrollView {
    
        public ScrollView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public ScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public ScrollView(Context context) {
            super(context);
        }
    
    }
    
    public interface DataSource {
        public void requestBitmapForIndex(CoverFlowView coverFlow, int index);
    
        public Bitmap defaultBitmap();
    }
    
    public interface Listener {
        public void onSelectionChanging(CoverFlowView coverFlow, int index);
    
        public void onSelectionChanged(CoverFlowView coverFlow, int index);
    
        public void onSelectionClicked(CoverFlowView coverFlow, int index);
    
        public void onSelectionLongClicked(CoverFlowView coverFlow, int index);
    }
    
    public static class ItemAnimation extends Animation {
        private int mViewWidth;
        private int mViewHeight;
        private int mStartZOffset;
        private int mStopZOffset;
        private int mStartXOffset;
        private int mStopXOffset;
        private float mStopAngleDegrees = 0;
        // private double mStopAngleRadians = 0;
        private float mStartAngleDegrees = 0;
        private boolean mStatic = false;
    
        // private double mStartAngleRadians = 0;
    
        public ItemAnimation() {
            super();
            setFillAfter(true);
            setFillBefore(true);
        }
    
        public void setStatic() {
            mStatic = true;
            setDuration(0);
        }
    
        public void setRotation(float start, float stop) {
            mStartAngleDegrees = start;
            mStopAngleDegrees = stop;
        }
    
        public void setXTranslation(int start, int stop) {
            mStartXOffset = start;
            mStopXOffset = stop;
        }
    
        public void setZTranslation(int start, int stop) {
            mStartZOffset = start;
            mStopZOffset = stop;
        }
    
        public void setViewDimensions(int width, int height) {
            mViewWidth = width;
            mViewHeight = height;
        }
    
        public float getStopAngleDegrees() {
            return mStopAngleDegrees;
        }
    
        public float getStartAngleDegrees() {
            return mStartAngleDegrees;
        }
    
        public int getStartXOffset() {
            return mStartXOffset;
        }
    
        public int getStopXOffset() {
            return mStopXOffset;
        }
    
        public int getStopZOffset() {
            return mStopZOffset;
        }
    
        @Override
        protected void applyTransformation(float interpolatedTime,
                Transformation t) {
            t.setTransformationType(mStatic ? Transformation.TYPE_BOTH
                    : Transformation.TYPE_MATRIX);
    
            if (mStatic)
                t.setAlpha(interpolatedTime < 1.0f ? 0 : 1);
    
            float angleDegrees = mStartAngleDegrees + interpolatedTime
                    * (mStopAngleDegrees - mStartAngleDegrees);
            float zOffset = mStartZOffset + interpolatedTime
                    * (mStopZOffset - mStartZOffset);
            int xOffset = mStartXOffset
                    + (int) (interpolatedTime * (mStopXOffset - mStartXOffset));
            Matrix m = new Matrix();
            Camera camera = new Camera();
            camera.translate(0, 0, zOffset);
    
            camera.rotateY(angleDegrees);
    
            camera.getMatrix(m);
            m.preTranslate(-(mViewWidth / 2), -(mViewHeight / 2));
            m.postTranslate((mViewWidth / 2) + xOffset, (mViewHeight / 2));
    
            t.getMatrix().set(m);
            super.applyTransformation(interpolatedTime, t);
        }
    
    }
     }
    

    you can use this class

    in your activity`s layout file ( main_layout.xml)

      <your.packageName.CoverFlowView
        android:id="@+id/coverflow"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white" />
    

    in MainActivity,java

        private CoverFlowView mCoverflow;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
    
        // Find the coverflow
        mCoverflow = (CoverFlowView) findViewById(R.id.coverflow);
    
    
    }