Android层叠卡片式banner的实现

Android层叠卡片式banner的实现

效果图如下:

在这里插入图片描述

背景

由于公司VIP模块项目需要,本着对ui设计师的尊重,需要实现以上效果图,在网上找了很多博客,都不能满足上面的需求,所以就只能自己硬着头皮自定义了,下面就是我自定义的view代码,做个记录:

package cn.com.cunw.familydesk.view.vipBanner;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.widget.RelativeLayout;
import android.widget.Scroller;

import java.util.ArrayList;

import cn.com.cunw.familydesk.R;

/**
 * @Description:
 * @Author: wuJie
 * @CreateDate: 2020/1/4 10:14
 * Copyright (C), 2015-2020,
 */
public class CoverFlowView extends RelativeLayout {

    public enum CoverFlowGravity {
        TOP, BOTTOM, CENTER_VERTICAL
    }

    public enum CoverFlowLayoutMode {
        MATCH_PARENT, WRAP_CONTENT
    }

    protected CoverFlowGravity mGravity;

    protected CoverFlowLayoutMode mLayoutMode;

    private Scroller mScroller;
    /**
     * To store reflections need to remove
     */
    private ArrayList<View> removeViewArray;

    private SparseArray<View> showViewArray;

    private int paddingLeft;
    private int paddingRight;
    private int paddingTop;
    private int paddingBottom;

    private int mWidth; // 控件的宽度

    private float reflectHeightFraction;
    private int reflectGap;

    private int mChildHeight; // child的高度
    private int mChildTranslateY;
    //private int mReflectionTranslateY;

    private int mVisibleChildCount; // 一屏显示的图片数量

    protected int VISIBLE_VIEWS = 3; // the visible views left and right 左右两边显示的个数

    private ICoverFlowAdapter mAdapter;

    private float mOffset;
    //private int mLastOffset;

    private final int ALPHA_DATUM = 200; // 基础alphaֵ
    private int STANDARD_ALPHA;
    // 基础缩放值
    //private static final float CARD_SCALE = 0.15f;

    private static float MOVE_POS_MULTIPLE = 3.0f;
    private static final int TOUCH_MINIMUM_MOVE = 5;
    private static final float MOVE_SPEED_MULTIPLE = 1;
    private static final float MAX_SPEED = 6.0f;
    private static final float FRICTION = 10.0f;

    private VelocityTracker mVelocity;

    private int firstIndex = 0;

    public CoverFlowView(Context context) {
        super(context);
        init();
    }

    public CoverFlowView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttributes(context, attrs);
        init();
    }

    public CoverFlowView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initAttributes(context, attrs);
        init();
    }

    private void initAttributes(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ImageCoverFlowView);
        int totalVisibleChildren = a.getInt(
                R.styleable.ImageCoverFlowView_visibleImage, 3);
        if (totalVisibleChildren % 2 == 0) { // 一屏幕必须是奇数显示
            throw new IllegalArgumentException("visible image must be an odd number");
        }

        VISIBLE_VIEWS = totalVisibleChildren >> 1; // 计算出左右两两边的显示个数

        reflectHeightFraction = a.getFraction(
                R.styleable.ImageCoverFlowView_reflectionHeight, 100, 0, 0.0f);

        if (reflectHeightFraction > 100) {
            reflectHeightFraction = 100;
        }
        reflectHeightFraction /= 100;
        reflectGap = a.getDimensionPixelSize(
                R.styleable.ImageCoverFlowView_reflectionGap, 0);

        mGravity = CoverFlowGravity.values()[a.getInt(
                R.styleable.ImageCoverFlowView_coverflowGravity,
                CoverFlowGravity.CENTER_VERTICAL.ordinal())];

        mLayoutMode = CoverFlowLayoutMode.values()[a.getInt(
                R.styleable.ImageCoverFlowView_coverflowLayoutMode,
                CoverFlowLayoutMode.WRAP_CONTENT.ordinal())];

        a.recycle();
    }

    private void init() {
        removeAllViews();
        setWillNotDraw(false);
        setClickable(true);

        if (mScroller == null) {
            mScroller = new Scroller(getContext(), new AccelerateDecelerateInterpolator());
        }
        if (showViewArray == null) {
            showViewArray = new SparseArray<View>();
        } else {
            showViewArray.clear();
        }
        if (removeViewArray == null) {
            removeViewArray = new ArrayList<View>();
        } else {
            removeViewArray.clear();
        }

        firstIndex = 0;
        mChildHeight = 0;
        mOffset = 0;
        //mLastOffset = -1;

        isFirstIn = true;
        lastMid = 1;
        isChange = true;

        // 计算透明度
        STANDARD_ALPHA = (255 - ALPHA_DATUM) / VISIBLE_VIEWS;

        if (mGravity == null) {
            mGravity = CoverFlowGravity.CENTER_VERTICAL;
        }

        if (mLayoutMode == null) {
            mLayoutMode = CoverFlowLayoutMode.WRAP_CONTENT;
        }

        // 一屏 显示的图片数量
        int visibleCount = (VISIBLE_VIEWS << 1) + 1;

        for (int i = 0; i < visibleCount && mAdapter != null && i < mAdapter.getCount(); ++i) {

            View convertView = null;
            if (removeViewArray.size() > 0) {
                convertView = removeViewArray.remove(0);
            }
            View view = mAdapter.getView(i, convertView, this);
            showViewArray.put(i, view);
            addView(view);
        }

        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (mAdapter == null || showViewArray.size() <= 0) {
            return;
        }

        paddingLeft = getPaddingLeft();
        paddingRight = getPaddingRight();
        paddingTop = getPaddingTop();
        paddingBottom = getPaddingBottom();

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 一屏显示的图片数量
        int visibleCount = (VISIBLE_VIEWS << 1) + 1;

        // 控件高度
        int availableHeight = heightSize - paddingTop - paddingBottom;

        int maxChildTotalHeight = 0;
        for (int i = 0; i < getChildCount() && i < visibleCount && i < showViewArray.size(); ++i) {

            //View view = showViewArray.get(i+firstIndex);
            View view = getChildAt(i);
            measureChild(view, widthMeasureSpec, heightMeasureSpec);

            //final int childHeight = ScreenUtil.dp2px(getContext(), 110);
            final int childHeight = view.getMeasuredHeight();
            final int childTotalHeight = (int) (childHeight + childHeight
                    * reflectHeightFraction + reflectGap);

            // 孩子的最大高度
            maxChildTotalHeight = (maxChildTotalHeight < childTotalHeight) ? childTotalHeight
                    : maxChildTotalHeight;
        }

        // 如果控件模式为确切值 或者 最大时
        if (heightMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.AT_MOST) {
            // if height which parent provided is less than child need, scale
            // child height to parent provide
            // 如果控件高度小于孩子控件高度,则缩放孩子高度为控件高度
            if (availableHeight < maxChildTotalHeight) {
                mChildHeight = availableHeight;
            } else {
                // if larger than, depends on layout mode
                // if layout mode is match_parent, scale child height to parent
                // provide
                // 如果是填充父窗体模式 则将孩子的高度 设为控件高度
                if (mLayoutMode == CoverFlowLayoutMode.MATCH_PARENT) {
                    mChildHeight = availableHeight;
                    // if layout mode is wrap_content, keep child's original
                    // height
                    // 如果是包裹内容 则将孩子的高度设为孩子允许的最大高度
                } else if (mLayoutMode == CoverFlowLayoutMode.WRAP_CONTENT) {
                    mChildHeight = maxChildTotalHeight;

                    // adjust parent's height
                    // 计算出控件的高度
                    if (heightMode == MeasureSpec.AT_MOST) {
                        heightSize = mChildHeight + paddingTop + paddingBottom;
                    }
                }
            }
        } else {
            // height mode is unspecified
            // 如果空间高度 没有明确定义
            // 如果孩子的模式为填充父窗体
            if (mLayoutMode == CoverFlowLayoutMode.MATCH_PARENT) {
                mChildHeight = availableHeight;
                // 如果孩子的模式为包裹内容
            } else if (mLayoutMode == CoverFlowLayoutMode.WRAP_CONTENT) {
                mChildHeight = maxChildTotalHeight;

                // 计算出控件的高度
                // adjust parent's height
                heightSize = mChildHeight + paddingTop + paddingBottom;
            }
        }

        // Adjust movement in y-axis according to gravity
        // 计算出孩子的原点 Y坐标
        if (mGravity == CoverFlowGravity.CENTER_VERTICAL) {// 竖直居中
            mChildTranslateY = (heightSize >> 1) - (mChildHeight >> 1);
        } else if (mGravity == CoverFlowGravity.TOP) {// 顶部对齐
            mChildTranslateY = paddingTop;
        } else if (mGravity == CoverFlowGravity.BOTTOM) {// 底部对齐
            mChildTranslateY = heightSize - paddingBottom - mChildHeight;
        }

        //mReflectionTranslateY = (int) (mChildTranslateY + mChildHeight - mChildHeight
        //        * reflectHeightFraction);

        setMeasuredDimension(widthSize, heightSize);
        mVisibleChildCount = visibleCount;
        mWidth = widthSize;

    }

    boolean isFirstIn = true; // 第一次初始化该控件
    int lastMid = 1;
    boolean isChange = true;

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mAdapter == null || mAdapter.getCount() <= 0 || showViewArray.size() <= 0) {
            return;
        }

        final float offset = mOffset;
        int i = 0;
        int mid = (int) Math.floor(offset + 0.5);

        //右边孩子的数量
        int rightChild = (mVisibleChildCount % 2 == 0) ? (mVisibleChildCount >> 1) - 1
                : mVisibleChildCount >> 1;
        //左边孩子的数量
        int leftChild = mVisibleChildCount >> 1;

        if (!isFirstIn) {
            if (lastMid + 1 == mid) {
                int actuallyPositionStart = getActuallyPosition(lastMid - leftChild);
                View view = showViewArray.get(actuallyPositionStart);
                showViewArray.remove(actuallyPositionStart);
                removeViewArray.add(view);
                removeView(view);

                View convertView = null;
                if (removeViewArray.size() > 0) {
                    convertView = removeViewArray.remove(0);
                }
                int actuallyPositionEnd = getActuallyPosition(mid + rightChild);

                //TODO
                View viewItem = mAdapter.getView(actuallyPositionEnd, convertView, this);

                showViewArray.put(actuallyPositionEnd, viewItem);
                addView(viewItem);

                isChange = true;

            } else if (lastMid - 1 == mid) {
                int actuallyPositionEnd = getActuallyPosition(lastMid + rightChild);
                View view = showViewArray.get(actuallyPositionEnd);
                showViewArray.remove(actuallyPositionEnd);
                removeViewArray.add(view);
                removeView(view);

                View convertView = null;
                if (removeViewArray.size() > 0) {
                    convertView = removeViewArray.remove(0);
                }
                int actuallyPositionStart = getActuallyPosition(mid - leftChild);

                //TODO
                View viewItem = mAdapter.getView(actuallyPositionStart, convertView, this);

                showViewArray.put(actuallyPositionStart, viewItem);
                addView(viewItem, 0);

                isChange = true;
            }

        } else {
            isFirstIn = false;
        }

        lastMid = mid;

        // draw the left children
        // 计算左边孩子的位置
        int startPos = mid - leftChild;
        for (i = startPos; i < mid; ++i) {
            //TODO  计算左边孩子的位置
            View child = layoutLeftChild(i, i - offset);

            if (isChange) {
                int actuallyPosition = getActuallyPosition(i);
                mAdapter.getData(child, actuallyPosition);
            }
        }

        // 计算 右边 和 中间
        int endPos = mid + rightChild;

        int j = -VISIBLE_VIEWS;
        for (i = endPos; i >= mid; --i, j = j + 2) {
            //TODO 计算右边和中间孩子的位置
            View child = layoutRightChild(i + j, i - offset);

            if (isChange) {
                int actuallyPosition = getActuallyPosition(i);
                mAdapter.getData(child, actuallyPosition);
            }
        }

        isChange = false;
    }

    private View layoutLeftChild(int position, float offset) {
        //获取实际的position
        int actuallyPosition = getActuallyPosition(position);
        View child = showViewArray.get(actuallyPosition);
        if (child != null) {
            makeChildTransfromer(child, actuallyPosition, offset);
        }
        return child;
    }

    private View layoutRightChild(int position, float offset) {

        //获取实际的position
        int actuallyPosition = getActuallyPosition(position);
        View child = showViewArray.get(actuallyPosition);
        if (child != null) {
            makeChildTransfromer(child, actuallyPosition, offset);
        }

        return child;
    }

    /**
     * 对 bitmap 进行伪 3d 变换
     *
     * @param child
     * @param position
     * @param offset
     */
    private void makeChildTransfromer(View child, int position, float offset) {
        //child.layout(0, 0, ScreenUtil.dp2px(getContext(), 200),ScreenUtil.dp2px(getContext(), 110));
        child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());

        float scale = 0;
        scale = 1 - Math.abs(offset) * 0.25f;

        // 延x轴移动的距离应该根据center图片决定
        float translateX = 0;

        final int originalChildHeight = (int) (mChildHeight - mChildHeight
                * reflectHeightFraction - reflectGap);

        //final int childTotalHeight = (int) (child.getHeight()
        //        + child.getHeight() * reflectHeightFraction + reflectGap);

        final float originalChildHeightScale = (float) originalChildHeight / child.getHeight();

        final float childHeightScale = originalChildHeightScale * scale;

        final int childWidth = (int) (child.getWidth() * childHeightScale);

        final int centerChildWidth = (int) (child.getWidth() * originalChildHeightScale);

        //final int centerChildWidth = (int) (child.getWidth() * childHeightScale);

        int leftSpace = ((mWidth >> 1) - paddingLeft) - (centerChildWidth >> 1);
        int rightSpace = (((mWidth >> 1) - paddingRight) - (centerChildWidth >> 1));

        //计算出水平方向的x坐标
        if (offset <= 0)
            translateX = ((float) leftSpace / VISIBLE_VIEWS)
                    * (VISIBLE_VIEWS + offset) + paddingLeft;

        else
            translateX = mWidth - ((float) rightSpace / VISIBLE_VIEWS)
                    * (VISIBLE_VIEWS - offset) - childWidth - paddingRight;

        //根据offset 算出透明度
        float alpha = 254 - Math.abs(offset) * STANDARD_ALPHA;

        child.setAlpha(0);
        if (alpha < 0) {
            alpha = 0;
        } else if (alpha > 254) {
            alpha = 254;
        }
        child.setAlpha(alpha / 254.0f);

        float adjustedChildTranslateY = 0;

        ObjectAnimator anim1 = ObjectAnimator.ofFloat(child, "scaleX", 1.0f, childHeightScale);
        ObjectAnimator anim2 = ObjectAnimator.ofFloat(child, "scaleY", 1.0f, childHeightScale);
        AnimatorSet animSet = new AnimatorSet();
        animSet.setDuration(0);
        //两个动画同时执行
        animSet.playTogether(anim1, anim2);
        child.setPivotX(0);
        child.setPivotY(child.getHeight() / 2);
        //显示的调用invalidate
        child.invalidate();
        animSet.setTarget(child);
        animSet.start();

        child.setTranslationX(translateX);
        child.setTranslationY(mChildTranslateY + adjustedChildTranslateY);

    }

    /**
     * 获取顶部Item position
     *
     * @return
     */
    public int getTopViewPosition() {
        return getActuallyPosition(lastMid);
    }

    /**
     * 获取顶部Item View
     *
     * @return
     */
    public View getTopView() {
        return showViewArray.get(getActuallyPosition(VISIBLE_VIEWS + lastMid));
    }

    /**
     * Convert draw-index to index in adapter
     *
     * @param position position to draw
     * @return
     */
    private int getActuallyPosition(int position) {
        if (mAdapter!=null){

        int max = mAdapter.getCount();
        position += VISIBLE_VIEWS;
        while (position < 0 || position >= max) {
            if (position < 0) {
                position += max;
            } else if (position >= max) {
                position -= max;
            }
         }
        }
        return position;
    }

    public void setAdapter(@NonNull ICoverFlowAdapter adapter) {
        mAdapter = adapter;
        init();
    }

    public ICoverFlowAdapter getAdapter() {
        return mAdapter;
    }

    private boolean onTouchMove = false; //是否正在执行触摸移动逻辑

    private View touchViewItem = null;
    private boolean isOnTopView = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (getParent() != null) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }

        if (isOnAnimator) {
            return false;
        }
        int action = event.getAction();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (mAdapter==null){
                    return false;
                }
                onTouchMove = true;
                if (mScroller.computeScrollOffset()) {
                    mScroller.abortAnimation();
                    invalidate();
                    requestLayout();
                }
                touchBegan(event);
                touchViewItem = getTopView();
                isOnTopView = inRangeOfView(touchViewItem, event);
                if (isOnTopView) {
                    sendLongClickAction();
                }
                return true;
            case MotionEvent.ACTION_MOVE:
                touchMoved(event);
                removeLongClickAction();
                touchViewItem = null;
                isOnTopView = false;
                return true;
            case MotionEvent.ACTION_UP:
                removeLongClickAction();
                if (isOnTopView && touchViewItem == getTopView()
                        && inRangeOfView(touchViewItem, event)) {

                    if (mTopViewClickListener != null) {
                        mTopViewClickListener.onClick(getTopViewPosition(), getTopView());
                    }
                }
                touchViewItem = null;
                isOnTopView = false;
                touchEnded(event);
                return true;
        }

        return false;
    }

    private Runnable longClickRunnable = null;

    /**
     * 发送长点击事件 Runnable
     */
    private void sendLongClickAction() {
        removeLongClickAction();
        longClickRunnable = new Runnable() {
            @Override
            public void run() {
                touchViewItem = null;
                isOnTopView = false;
                if (mTopViewLongClickListener != null) {
                    mTopViewLongClickListener.onLongClick(getTopViewPosition(), getTopView());
                }
            }
        };
        postDelayed(longClickRunnable, 600);
    }

    /**
     * 移除长点击事件 Runnable
     */
    private void removeLongClickAction() {
        if (longClickRunnable != null) {
            removeCallbacks(longClickRunnable);
            longClickRunnable = null;
        }
    }

    private boolean inRangeOfView(View view, MotionEvent ev) {
        Rect frame = new Rect();
        view.getHitRect(frame);
        if (frame.contains((int) ev.getX(), (int) ev.getY())) {
            return true;
        }
        return false;
    }

    private OnTopViewClickListener mTopViewClickListener;
    private OnTopViewLongClickListener mTopViewLongClickListener;
    private OnTopViewChangeListener mTopViewChangeListener;

    /**
     * 设置 TopView 的点击监听
     *
     * @param topViewClickListener
     */
    public void setOnTopViewClickListener(OnTopViewClickListener topViewClickListener) {
        this.mTopViewClickListener = topViewClickListener;
    }

    /**
     * 获取 TopView 的点击监听
     *
     * @return
     */
    public OnTopViewClickListener getOnTopViewClickListener() {
        return this.mTopViewClickListener;
    }

    /**
     * 设置 TopView 的长点击监听
     *
     * @param topViewLongClickListener
     */
    public void setOnTopViewLongClickListener(OnTopViewLongClickListener topViewLongClickListener) {
        this.mTopViewLongClickListener = topViewLongClickListener;
    }

    /**
     * 设置 TopView 的长点击监听
     *
     * @param mTopViewChangeListener
     */
    public void setOnTopViewChangeListener(OnTopViewChangeListener mTopViewChangeListener) {
        this.mTopViewChangeListener = mTopViewChangeListener;
    }

    /**
     * 获取 TopView 的长点击监听
     *
     * @return
     */
    public OnTopViewLongClickListener getOnTopViewLongClickListener() {
        return this.mTopViewLongClickListener;
    }

    public interface OnTopViewClickListener {

        void onClick(int position, View itemView);

    }

    public interface OnTopViewLongClickListener {

        void onLongClick(int position, View itemView);

    }

    public interface OnTopViewChangeListener {

        void onItemChange(int position);

    }

    private boolean mTouchMoved;
    private float mTouchStartPos;
    private float mTouchStartX;
    private float mTouchStartY;

    private long mStartTime;
    private float mStartOffset;

    private void touchBegan(MotionEvent event) {
        endAnimation();

        float x = event.getX();
        mTouchStartX = x;
        mTouchStartY = event.getY();
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartOffset = mOffset;

        mTouchMoved = false;

        mTouchStartPos = (x / mWidth) * MOVE_POS_MULTIPLE - 5;
        mTouchStartPos /= 2;

        mVelocity = VelocityTracker.obtain();
        mVelocity.addMovement(event);
    }

    private Runnable mAnimationRunnable;

    private void endAnimation() {
        if (mAnimationRunnable != null) {
            mOffset = (float) Math.floor(mOffset + 0.5);

            invalidate();
            requestLayout();

            removeCallbacks(mAnimationRunnable);
            mAnimationRunnable = null;
        }
    }

    private void touchMoved(MotionEvent event) {
        float pos = (event.getX() / mWidth) * MOVE_POS_MULTIPLE - 5;
        pos /= 2;

        if (!mTouchMoved) {
            float dx = Math.abs(event.getX() - mTouchStartX);
            float dy = Math.abs(event.getY() - mTouchStartY);
            if (dx < TOUCH_MINIMUM_MOVE && dy < TOUCH_MINIMUM_MOVE)
                return;
            mTouchMoved = true;
        }

        mOffset = mStartOffset + mTouchStartPos - pos;

        invalidate();
        requestLayout();
        mVelocity.addMovement(event);
    }

    private void touchEnded(MotionEvent event) {
        float pos = (event.getX() / mWidth) * MOVE_POS_MULTIPLE - 5;
        pos /= 2;

        if (mTouchMoved || (mOffset - Math.floor(mOffset)) != 0) {
            mStartOffset += mTouchStartPos - pos;
            mOffset = mStartOffset;

            mVelocity.addMovement(event);

            mVelocity.computeCurrentVelocity(1000);
            float speed = mVelocity.getXVelocity();

            speed = (speed / mWidth) * MOVE_SPEED_MULTIPLE;
            if (speed > MAX_SPEED)
                speed = MAX_SPEED;
            else if (speed < -MAX_SPEED)
                speed = -MAX_SPEED;

            startAnimation(-speed);

            if (mTopViewChangeListener != null) {
                mTopViewChangeListener.onItemChange(getTopViewPosition());
            }
        } else {
            Log.e("xyw", " touch ==>" + event.getX() + " , " + event.getY());
            onTouchMove = false;
        }

        mVelocity.clear();
        mVelocity.recycle();
    }

    private float mStartSpeed;
    private float mDuration;

    private void startAnimation(float speed) {
        if (mAnimationRunnable != null) {
            onTouchMove = false;
            return;
        }

        float delta = speed * speed / (FRICTION * 2);
        if (speed < 0)
            delta = -delta;

        float nearest = mStartOffset + delta;
        nearest = (float) Math.floor(nearest + 0.5f);

        mStartSpeed = (float) Math.sqrt(Math.abs(nearest - mStartOffset) * FRICTION * 2);
        if (nearest < mStartOffset)
            mStartSpeed = -mStartSpeed;

        mDuration = Math.abs(mStartSpeed / FRICTION);
        mStartTime = AnimationUtils.currentAnimationTimeMillis();

        mAnimationRunnable = new Runnable() {
            @Override
            public void run() {
                driveAnimation();
            }
        };
        post(mAnimationRunnable);
    }

    private void driveAnimation() {
        float elapsed = (AnimationUtils.currentAnimationTimeMillis() - mStartTime) / 1000.0f;
        if (elapsed >= mDuration) {
            endAnimation();
            onTouchMove = false;
        } else {
            updateAnimationAtElapsed(elapsed);
            post(mAnimationRunnable);
        }
    }

    private void updateAnimationAtElapsed(float elapsed) {
        if (elapsed > mDuration)
            elapsed = mDuration;

        float delta = Math.abs(mStartSpeed) * elapsed - FRICTION * elapsed * elapsed / 2;
        if (mStartSpeed < 0)
            delta = -delta;

        mOffset = mStartOffset + delta;
        invalidate();
        requestLayout();
    }

    @Override
    public void computeScroll() {
        super.computeScroll();

        // 算出移动到某一个虚拟点时 mOffset 的值,然后 invalidate 重绘
        if (mScroller.computeScrollOffset()) {
            final int currX = mScroller.getCurrX();
            mOffset = (float) currX / 100;

            invalidate();
        }
    }

    private boolean isOnAnimator = false; //是否正在进行点击切换动画

    /**
     * 翻到前页
     */
    public void gotoPrevious() {
        doAnimator(-1.0f);

    }

    /**
     * 前进到后一页
     */
    public void gotoForward() {
        doAnimator(1.0f);
    }

    public void doAnimator(float target) {
        if (isOnAnimator || onTouchMove) { //如果正在执行点击切换动画 或者 正在执行触摸移动
            return;
        }
        isOnAnimator = true;
        ValueAnimator animator = ValueAnimator.ofFloat(mOffset, mOffset + target);
        animator.setDuration(300).start();
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mOffset = (Float) animation.getAnimatedValue();

                invalidate();
                requestLayout();
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                isOnAnimator = false;
            }
        });
    }

//    @Override
//    public boolean dispatchTouchEvent(MotionEvent ev) {
//        if (ev.getAction() == MotionEvent.ACTION_MOVE) {
//            return true; // 禁止滑动
//        }
//        return super.dispatchTouchEvent(ev);
//    }
}


相关回调接口类
public interface ICoverFlowAdapter {

    int getCount();

    Object getItem(int position);

    long getItemId(int position);

    View getView(int position, View convertView, ViewGroup parent);

    void getData(View view, int position);
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值