ViewDragHelper解读

来源:互联网 发布:武装突袭3低配优化 编辑:程序博客网 时间:2024/05/22 13:54

精彩推荐

Android ViewDragHelper完全解析 自定义ViewGroup神
模仿探探首页卡片左右滑动效果,滑动流畅,卡片view无限重生

案例效果图

这里写图片描述

案例注释

public class SlideLayout extends ViewGroup {    private List<CardItemView> viewList = new ArrayList<>(); // 存放的是每一层的view,从顶到底    private List<View> releasedViewList = new ArrayList<>(); // 手指松开后存放的view列表    private final ViewDragHelper mDragHelper; // 拖拽工具类    private int initCenterViewX = 0, initCenterViewY = 0; // 最初时,中间View的x位置,y位置    private int allWidth = 0; // 面板的宽度    private int allHeight = 0; // 面板的高度    private int childWith = 0; // 每一个子View对应的宽度    private static final float SCALE_STEP = 0.08f; // view叠加缩放的步长    private static final int MAX_SLIDE_DISTANCE_LINKAGE = 400; // 水平距离+垂直距离,超过这个值,则下一层view完成向上一层view的过渡    private View bottomLayout; // 卡片下边的三个按钮布局    private int bottomMarginTop = 40;//底部按钮和图片的间距    private int yOffsetStep = 40; // view叠加垂直偏移量的步长    private static final int X_VEL_THRESHOLD = 900;    private static final int X_DISTANCE_THRESHOLD = 300;    public static final int VANISH_TYPE_LEFT = 0;//左侧按钮点击事件    public static final int VANISH_TYPE_RIGHT = 1;//右侧按钮点击事件    private Object obj1 = new Object();    private CardSwitchListener cardSwitchListener; // 回调接口    private List<CardDataItem> dataList; // 存储的数据链表    private int isShowing = 0; // 当前正在显示的小项    private View leftBtn, rightBtn;    private boolean btnLock = false;    private GestureDetectorCompat moveDetector;    private OnClickListener btnListener;    public SlideLayout(Context context) {        this(context, null);    }    public SlideLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.card);        bottomMarginTop = (int) array.getDimension(R.styleable.card_bottomMarginTop, bottomMarginTop);        yOffsetStep = (int) array.getDimension(R.styleable.card_yOffsetStep, yOffsetStep);        array.recycle();        mDragHelper = ViewDragHelper.create(this, 10, new DragHelperCallback());        moveDetector = new GestureDetectorCompat(context, new MoveDetector());        btnListener = new View.OnClickListener() {            @Override            public void onClick(View view) {                if (view instanceof ImageView) {                    // 点击的是卡片                    if (null != cardSwitchListener && view.getScaleX() > 1 - SCALE_STEP) {                        cardSwitchListener.onItemClick(view, isShowing);                    }                } else {                    // 点击的是bottomLayout里面的一些按钮                    btnLock = true;                    int type = -1;                    if (view == leftBtn) {                        type = VANISH_TYPE_LEFT;                    } else if (view == rightBtn) {                        type = VANISH_TYPE_RIGHT;                    }                    vanishOnBtnClick(type);                }            }        };    }    //手势监听    class MoveDetector extends GestureDetector.SimpleOnGestureListener {        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy) {            // 拖动了,touch不往下传递            return Math.abs(dy) + Math.abs(dx) > 5;        }    }    //拖动事件处理核心方法    public class DragHelperCallback extends ViewDragHelper.Callback {        //是否可以拖动view        @Override        public boolean tryCaptureView(View child, int pointerId) {            // 如果数据List为空,或者子View不可见,则不予处理            if (child == bottomLayout || dataList == null || dataList.size() == 0                    || child.getVisibility() != View.VISIBLE || child.getScaleX() <= 1.0f - SCALE_STEP) {                // 一般来讲,如果拖动的是第三层、或者第四层的View,则直接禁止                // 此处用getScale的用法来巧妙回避                return false;            }            if (btnLock) {                return false;            }            // 只捕获顶部view(rotation=0)            int childIndex = viewList.indexOf(child);            if (childIndex > 0) {                return false;            }            return true;        }        //松手之后View的处理        @Override        public void onViewReleased(View releasedChild, float xvel, float yvel) {            animToSide(releasedChild, xvel, yvel);        }        //View位置发生变化时        @Override        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {            // 调用offsetLeftAndRight导致viewPosition改变,会调到此处,所以此处对index做保护处理            int index = viewList.indexOf(changedView);            if (index + 2 > viewList.size()) {                return;            }            processLinkageView(changedView);        }        //View滑动的X坐标        @Override        public int clampViewPositionHorizontal(View child, int left, int dx) {            return left;        }        //View滑动的Y坐标        @Override        public int clampViewPositionVertical(View child, int top, int dy) {            return top;        }    }    //重测View的大小    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        measureChildren(widthMeasureSpec, heightMeasureSpec);        int maxWidth = MeasureSpec.getSize(widthMeasureSpec);        int maxHeight = MeasureSpec.getSize(heightMeasureSpec);        //设置模式和大小        setMeasuredDimension(                resolveSizeAndState(maxWidth, widthMeasureSpec, 0),                resolveSizeAndState(maxHeight, heightMeasureSpec, 0));        allWidth = getMeasuredWidth();        allHeight = getMeasuredHeight();    }    //放置布局    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        // 布局卡片view        int size = viewList.size();        for (int i = 0; i < size; i++) {            View viewItem = viewList.get(i);            int childHeight = viewItem.getMeasuredHeight();            viewItem.layout(left, top, right, top + childHeight);            int offset = yOffsetStep * i;            float scale = 1 - SCALE_STEP * i;            if (i > 2) {                // 备用的view                offset = yOffsetStep * 2;                scale = 1 - SCALE_STEP * 2;            }            viewItem.offsetTopAndBottom(offset);            viewItem.setScaleX(scale);            viewItem.setScaleY(scale);        }        // 布局底部按钮的View        if (null != bottomLayout) {            int layoutTop = viewList.get(0).getMeasuredHeight() + bottomMarginTop;            bottomLayout.layout(left, layoutTop, right, layoutTop                    + bottomLayout.getMeasuredHeight());        }        // 初始化一些中间参数        initCenterViewX = viewList.get(0).getLeft();        initCenterViewY = viewList.get(0).getTop();        childWith = viewList.get(0).getMeasuredWidth();    }    //设置卡片操作回调    public void setCardSwitchListener(CardSwitchListener cardSwitchListener) {        this.cardSwitchListener = cardSwitchListener;    }    //顶层卡片View位置改变,底层的位置需要调整    private void processLinkageView(View changedView) {        int changeViewLeft = changedView.getLeft();        int changeViewTop = changedView.getTop();        int distance = Math.abs(changeViewTop - initCenterViewY)                + Math.abs(changeViewLeft - initCenterViewX);        float rate = distance / (float) MAX_SLIDE_DISTANCE_LINKAGE;        float rate1 = rate;        float rate2 = rate - 0.2f;        if (rate > 1) {            rate1 = 1;        }        if (rate2 < 0) {            rate2 = 0;        } else if (rate2 > 1) {            rate2 = 1;        }        ajustLinkageViewItem(changedView, rate1, 1);        ajustLinkageViewItem(changedView, rate2, 2);    }    //由index对应view变成index-1对应的view    private void ajustLinkageViewItem(View changedView, float rate, int index) {        int changeIndex = viewList.indexOf(changedView);        int initPosY = yOffsetStep * index;        float initScale = 1 - SCALE_STEP * index;        int nextPosY = yOffsetStep * (index - 1);        float nextScale = 1 - SCALE_STEP * (index - 1);        int offset = (int) (initPosY + (nextPosY - initPosY) * rate);        float scale = initScale + (nextScale - initScale) * rate;        View ajustView = viewList.get(changeIndex + index);        ajustView.offsetTopAndBottom(offset - ajustView.getTop()                + initCenterViewY);        ajustView.setScaleX(scale);        ajustView.setScaleY(scale);    }    //处理是否拦截滑动事件    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev);        boolean moveFlag = moveDetector.onTouchEvent(ev);        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {            // ACTION_DOWN的时候就对view重新排序            orderViewStack();            // 保存初次按下时arrowFlagView的Y坐标            // action_down时就让mDragHelper开始工作,否则有时候导致异常            mDragHelper.processTouchEvent(ev);        }        return shouldIntercept && moveFlag;    }    //处理是否处理滑动事件    @Override    public boolean onTouchEvent(MotionEvent event) {        mDragHelper.processTouchEvent(event);        return true;    }    //对View重新排序    private void orderViewStack() {        synchronized (obj1) {            if (releasedViewList.size() == 0) {                return;            }            CardItemView changedView = (CardItemView) releasedViewList.get(0);            if (changedView.getLeft() == initCenterViewX) {                releasedViewList.remove(0);                return;            }            // 1. 消失的卡片View位置重置,由于大多手机会重新调用onLayout函数,所以此处大可以不做处理,不信你注释掉看看            changedView.offsetLeftAndRight(initCenterViewX - changedView.getLeft());            changedView.offsetTopAndBottom(initCenterViewY - changedView.getTop() + yOffsetStep * 2);            float scale = 1.0f - SCALE_STEP * 2;            changedView.setScaleX(scale);            changedView.setScaleY(scale);            // 2. 卡片View在ViewGroup中的顺次调整            int num = viewList.size();            for (int i = num - 1; i > 0; i--) {                View tempView = viewList.get(i);                tempView.bringToFront();//调整到前面来            }            // 3. changedView填充新数据            int newIndex = isShowing + 4;            if (newIndex < dataList.size()) {                CardDataItem dataItem = dataList.get(newIndex);                changedView.fillData(dataItem);            } else {                changedView.setVisibility(View.INVISIBLE);            }            // 4. viewList中的卡片view的位次调整            viewList.remove(changedView);            viewList.add(changedView);            releasedViewList.remove(0);            // 5. 更新showIndex、接口回调            if (isShowing + 1 < dataList.size()) {                isShowing++;            }            if (null != cardSwitchListener) {                cardSwitchListener.onShow(isShowing);            }        }    }    @Override    public void computeScroll() {        if (mDragHelper.continueSettling(true)) {            ViewCompat.postInvalidateOnAnimation(this);        } else {            // 动画结束            synchronized (this) {                if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {                    orderViewStack();                    btnLock = false;                }            }        }    }    //松手时处理滑动到边缘的动画    private void animToSide(View changedView, float xvel, float yvel) {        int finalX = initCenterViewX;        int finalY = initCenterViewY;        int flyType = -1;        // 1. 下面这一坨计算finalX和finalY,要读懂代码需要建立一个比较清晰的数学模型才能理解,不信拉倒        int dx = changedView.getLeft() - initCenterViewX;        int dy = changedView.getTop() - initCenterViewY;        if (dx == 0) {            // 由于dx作为分母,此处保护处理            dx = 1;        }        if (xvel > X_VEL_THRESHOLD || dx > X_DISTANCE_THRESHOLD) {            finalX = allWidth;            finalY = dy * (childWith + initCenterViewX) / dx + initCenterViewY;            flyType = VANISH_TYPE_RIGHT;        } else if (xvel < -X_VEL_THRESHOLD || dx < -X_DISTANCE_THRESHOLD) {            finalX = -childWith;            finalY = dy * (childWith + initCenterViewX) / (-dx) + dy                    + initCenterViewY;            flyType = VANISH_TYPE_LEFT;        }        // 如果斜率太高,就折中处理        if (finalY > allHeight) {            finalY = allHeight;        } else if (finalY < -allHeight / 2) {            finalY = -allHeight / 2;        }        // 如果没有飞向两侧,而是回到了中间,需要谨慎处理        if (finalX != initCenterViewX) {            releasedViewList.add(changedView);        }        // 2. 启动动画        if (mDragHelper.smoothSlideViewTo(changedView, finalX, finalY)) {            ViewCompat.postInvalidateOnAnimation(this);        }        // 3. 消失动画即将进行,listener回调        if (flyType >= 0 && cardSwitchListener != null) {            cardSwitchListener.onCardVanish(isShowing, flyType);        }    }    //卡片回调接口    public interface CardSwitchListener {        /**         * 新卡片显示回调         *         * @param index 最顶层显示的卡片的index         */        public void onShow(int index);        /**         * 卡片飞向两侧回调         *         * @param index 飞向两侧的卡片数据index         * @param type  飞向哪一侧{@link #VANISH_TYPE_LEFT}或{@link #VANISH_TYPE_RIGHT}         */        public void onCardVanish(int index, int type);        /**         * 卡片点击事件         *         * @param cardImageView 卡片上的图片view         * @param index         点击到的index         */        public void onItemClick(View cardImageView, int index);    }    //本来想写成Adapter适配,想想还是算了,这种比较简单    public void fillData(List<CardDataItem> dataList) {        this.dataList = dataList;        int num = viewList.size();        for (int i = 0; i < num; i++) {            CardItemView itemView = viewList.get(i);            itemView.fillData(dataList.get(i));            itemView.setVisibility(View.VISIBLE);        }        if (null != cardSwitchListener) {            cardSwitchListener.onShow(0);        }    }    //布局加载完成    @Override    protected void onFinishInflate() {        super.onFinishInflate();        // 渲染完成,初始化卡片view列表        viewList.clear();        int num = getChildCount();        for (int i = num - 1; i >= 0; i--) {            View childView = getChildAt(i);            if (childView.getId() == R.id.card_bottom_layout) {                bottomLayout = childView;                initBottomLayout();            } else {                CardItemView viewItem = (CardItemView) childView;                viewItem.setTag(i + 1);                viewItem.imageView.setOnClickListener(btnListener);                viewList.add(viewItem);            }        }    }    //初始化底部按钮    private void initBottomLayout() {        leftBtn = bottomLayout.findViewById(R.id.card_left_btn);        rightBtn = bottomLayout.findViewById(R.id.card_right_btn);        leftBtn.setOnClickListener(btnListener);        rightBtn.setOnClickListener(btnListener);    } //点击按钮消失动画    //按钮事件处理    private void vanishOnBtnClick(int type) {        synchronized (obj1) {            View animateView = viewList.get(0);            if (animateView.getVisibility() != View.VISIBLE || releasedViewList.contains(animateView)) {                return;            }            int finalX = 0;            if (type == VANISH_TYPE_LEFT) {                finalX = -childWith;            } else if (type == VANISH_TYPE_RIGHT) {                finalX = allWidth;            }            if (finalX != 0) {                releasedViewList.add(animateView);                if (mDragHelper.smoothSlideViewTo(animateView, finalX, initCenterViewY + allHeight)) {                    ViewCompat.postInvalidateOnAnimation(this);                }            }            if (type >= 0 && cardSwitchListener != null) {                cardSwitchListener.onCardVanish(isShowing, type);            }        }    }}
0 0
原创粉丝点击