美文网首页
Android滑动冲突解决方式(下拉刷新上拉加载更多,适配Rec

Android滑动冲突解决方式(下拉刷新上拉加载更多,适配Rec

作者: huisonma | 来源:发表于2018-01-10 13:54 被阅读0次

    一、Android事件的分发机制

        这里需要了解下Andorid事件的分发机制。事件分发一般是针对一组事件,即ACTION_DOWN > ACTION_UP 或 ACTION_DOWN > ACTION_MOVE... >ACTION_UP,其中涉及事件分发的主要方法有 dispatchTouchEvent(MotionEvent event)、onInterceptTouchEvent(MotionEvent event) (ViewGroup有,View没有)、onTouchEvent(MotionEvent event),而且事件分发是由上向下传递的,即先到parent,再到child,这里简单以 ViewGroup内包裹一个View为例,大致分析下其事件的分发流程(忽略Activity,Window的传递)

        事件首先会传递到ViewGroup.dispatchTouchEvent(MotionEvent event),然后会判断ViewGroup.onInterceptTouchEvent(MotionEvent event)的返回值:

        1.如果返回为false,即不拦截,事件则会传递给View.dispatchTouchEvent(MotionEvent event),由于这里View没有子View了,事件则传递给该View的View.onTouchEvent(MotionEvent event)处理,如果View.onTouchEvent(MotionEvent event)没有消耗该事件,则该事件会返回给ViewGroup.onTouchEvent(MotionEvent event)处理,如果View.onTouchEvent(MotionEvent event)消耗了该事件,则该事件不会再返回给ViewGroup,本次事件分发结束。

        2.如果ViewGroup.onInterceptTouchEvent(MotionEvent event)返回值为ture,即拦截事件,则事件将由ViewGroup.onTouchEvent(MotionEvent event)处理,本次事件分发结束。

        3.即使事件被ViewGroup拦截了,View也可以阻止ViewGroup对事件的拦截。可以通过getParent().requestDisallowInterceptTouchEvent(true)。

    大致流程图

        前面说到View可以阻止ViewGroup对事件的拦截,但除了ACTION_DOWN,也就是说,对一组事件,除了ACTION_DOWN,子View可以在ViewGroup.onInterceptTouchEvent(MotionEvent event)返回ture的情况下,获取事件的处理权,下面截图android25的源代码。

    @Override

        public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

            if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {

                // We're already in this state, assume our ancestors are too

                return;

            }

            if (disallowIntercept) {

                mGroupFlags |= FLAG_DISALLOW_INTERCEPT;

            } else {

                mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;

            }

            // Pass it up to our parent

            if (mParent != null) {

                mParent.requestDisallowInterceptTouchEvent(disallowIntercept);

            }

        }

    当actionMasked == MotionEvent.ACTION_DOWN||mFirstTouchTarget !=null时,会进行disallowIntercept的判断,而disallowIntercept取决于mGroupFlags,因为FLAG_DISALLOW_INTERCEPT是一个常量0x80000,而mGroupFlags的赋值是可以通过requestDisallowInterceptTouchEvent来改变的。当requestDisallowInterceptTouchEvent(true),disallowIntercept=true,此时不走onInterceptTouchEvent(ev)的判断,intercepted=false从而达到阻止ViewGroup拦截的效果。

    二、以RecyclerView下拉刷新上拉加载更多为例分析滑动冲突及解决

    以recyclerView为例,也可以换listView。默认状态时红色部分为可视部分,也就是顶部和底部隐藏看不见,这里我们选择FrameLayout作为容器,因此,FrameLayout和RecyclerView就会产生同向滑动冲突。只有recyclerView内容滑动到顶部并且手势为下滑时,header才会慢慢下滑到可视范围内,或recyclerView内容滑动到底部时,并且手势为上滑,footer才会慢慢上滑到可视范围内。

        private boolean intercept;

        private float lastInterceptY;

        @Override

        public boolean onInterceptTouchEvent(MotionEvent ev) {

            float curInterceptY = ev.getY();

            switch (ev.getAction()) {

                case MotionEvent.ACTION_DOWN:

                    intercept = false;

                    break;

                case MotionEvent.ACTION_MOVE:

                    if (isRefreshing || isLoadMore) {

                        intercept = false;

                    } else {

                        boolean isHeaderShow = headerParams.topMargin > -headerHeight;

                        boolean isFooterShow = footerParams.topMargin < height;

                        intercept = touchHelper != null && touchHelper.judgeIntercept(curInterceptY, lastInterceptY, isHeaderShow, isFooterShow, allowLoadMore);

                    }

                    break;

                case MotionEvent.ACTION_UP:

                    intercept = false;

                    break;

            }

            lastInterceptY = curInterceptY;

            return intercept;

        }

        @Override

        public boolean judgeIntercept(float curInterceptY, float lastInterceptY, boolean isHeaderShow, boolean isFooterShow, boolean allowLoadMore) {

            boolean intercept;

            int firstVisiblePos = layoutManager.findFirstVisibleItemPosition();

            View firstView = rv.getChildAt(firstVisiblePos);

            if (firstVisiblePos == 0 && firstView.getTop() == 0) {

                intercept = curInterceptY > lastInterceptY || isHeaderShow;

            } else {

                if (allowLoadMore && layoutManager.findLastVisibleItemPosition() == layoutManager.getItemCount() - 1) {

                    intercept = curInterceptY < lastInterceptY || isFooterShow;

                } else {

                    intercept = false;

                }

            }

            return intercept;

        }

    1.MotionEvent.ACTION_DOWN中,必须返回false,不拦截,一旦拦截,后续的ACTION_MOVE和ACTION_UP将直接交由FrameLayout的onTouchEvent(ev)处理

    2.MotionEvent.ACTION_UP中也必须返回false,因为一组事件以ACTION_UP结尾,则ACTION_UP这个事件必定会经过FrameLayout.onTouchEvent(ev),如果拦截了,则子View针对ACTION_UP需要处理的事情就无法完成。

    3.MotionEvent.ACTION_MOVE中根据实际情况判断是否拦截。

        private float moveDis;

        @Override

        public boolean onTouchEvent(MotionEvent ev) {

            if (touchHelper != null) {

                float curTouchY = ev.getY();

                switch (ev.getAction()) {

                    case MotionEvent.ACTION_DOWN:

                        break;

                    case MotionEvent.ACTION_MOVE:

                        moveDis = curTouchY - lastInterceptY;

                        if (Math.abs(moveDis) < touchSlop) {

                            break;

                        }

                        if (isRefreshing || isLoadMore) {

                            break;

                        }

                        moveDis = moveDis * kMoveFactor;

                        if (touchHelper.isContentSlideToTop()) {

                            updateHeaderMargin(moveDis);

                        } else if (touchHelper.isContentSlideToBottom()) {

                            updateFooterMargin(moveDis);

                        }

                        break;

                    case MotionEvent.ACTION_UP:

                        if (moveDis > 0) {

                            if (touchHelper.isContentSlideToTop()) {

                                if (headerParams.topMargin < 0) {

                                    scrollHeaderByAnimator(headerParams.topMargin, -headerHeight);

                                    if (header != null) {

                                        header.onPullToRefresh(moveDis);

                                    }

                                } else {

                                    scrollHeaderByAnimator(headerParams.topMargin, 0);

                                    if (header != null) {

                                        header.onRefreshing();

                                    }

                                    isRefreshing = true;

                                    if (listener != null) {

                                        listener.onRefresh();

                                    }

                                }

                            }

                        } else {

                            if (touchHelper.isContentSlideToBottom()) {

                                if (footerParams.topMargin > height - footerHeight) {

                                    scrollFooterByAnimator(false, footerParams.topMargin, height);

                                    if (footer != null) {

                                        footer.onPullToLoadMore(moveDis);

                                    }

                                } else {

                                    scrollFooterByAnimator(false, footerParams.topMargin, height - footerHeight);

                                    if (footer != null) {

                                        footer.onLoadMore();

                                    }

                                    isLoadMore = true;

                                    if (listener != null) {

                                        listener.onLoadMore();

                                    }

                                }

                            }

                        }

                        break;

                }

            }

            return true;

        }

    private void updateHeaderMargin(float moveDis) {

            moveDis = moveDis < 0 ? 0 : moveDis;

            headerParams.topMargin = (int) (-headerHeight + moveDis);

            headerVG.setLayoutParams(headerParams);

            setChildViewTopMargin((int) moveDis);

            if (header != null) {

                if (moveDis < headerHeight) {

                    header.onPullToRefresh(moveDis);

                } else {

                    header.onReleaseToRefresh(moveDis);

                }

            }

        }

        private void setChildViewTopMargin(int topMargin) {

            LayoutParams childParams = (LayoutParams) childView.getLayoutParams();

            childParams.topMargin = topMargin;

            childView.setLayoutParams(childParams);

        }

        private void updateFooterMargin(float moveDis) {

            moveDis = moveDis > 0 ? 0 : moveDis;

            footerParams.topMargin = (int) (height + moveDis);

            footerVG.setLayoutParams(footerParams);

            setChildViewBottomMargin((int) Math.abs(moveDis));

            scrollContentToBottom((int) -moveDis);

            if (footer != null) {

                if (Math.abs(moveDis) < footerHeight) {

                    footer.onPullToLoadMore(moveDis);

                } else {

                    footer.onReleaseToLoadMore(moveDis);

                }

            }

        }

        private void setChildViewBottomMargin(int bottomMargin) {

            LayoutParams childParams = (LayoutParams) childView.getLayoutParams();

            childParams.bottomMargin = bottomMargin;

            childView.setLayoutParams(childParams);

        }

        private void scrollContentToBottom(int deltaY) {

            if (childView instanceof RecyclerView) {

                childView.scrollBy(0, deltaY);

            } else if (childView instanceof ListView) {

                ((ListView) childView).smoothScrollBy(deltaY, 0);

            } else if (childView instanceof ScrollView) {

                childView.scrollBy(0, deltaY);

            }

        }

        private void scrollHeaderByAnimator(float startY, float endY) {

            ValueAnimator animator = ValueAnimator.ofFloat(startY, endY);

            animator.setDuration(kDuration);

            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override

                public void onAnimationUpdate(ValueAnimator animation) {

                    float floatValue = (float) animation.getAnimatedValue();

                    headerParams.topMargin = (int) floatValue;

                    headerVG.setLayoutParams(headerParams);

                    setChildViewTopMargin((int) (headerHeight + floatValue));

                }

            });

            animator.start();

        }

        private void scrollFooterByAnimator(final boolean isAuto, float startY, float endY) {

            ValueAnimator animator = ValueAnimator.ofFloat(startY, endY);

            animator.setDuration(kDuration);

            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override

                public void onAnimationUpdate(ValueAnimator animation) {

                    float floatValue = (float) animation.getAnimatedValue();

                    footerParams.topMargin = (int) floatValue;

                    footerVG.setLayoutParams(footerParams);

                    int bottomMargin = (int) (height - floatValue);

                    setChildViewBottomMargin(bottomMargin);

                    if (isAuto) {

                        scrollContentToBottom(bottomMargin);

                        if (footer != null) {

                            footer.onPullToLoadMore(bottomMargin);

                            if (bottomMargin == footerHeight) {

                                footer.onLoadMore();

                            }

                        }

                    }

                }

            });

            animator.start();

        }

    不完善点还待指正!!!

    源码下载:下拉刷新上拉加载更多 喜欢就star下吧!万分感谢!!!

    相关文章

      网友评论

          本文标题:Android滑动冲突解决方式(下拉刷新上拉加载更多,适配Rec

          本文链接:https://www.haomeiwen.com/subject/xizbnxtx.html