实现垂直和水平拖拉查看更多详情页的自定义控件,用来满足类似电商app详情页面拖动查看更多需求...

按照国际惯例咱们先看效果图

额···图片貌似太大了,各位将就着看吧。
GayHub传送门: github.com/workdawn/Dr…

相关特性

支持大于两个子页面

支持竖向和横向两个方向的拖拉

支持弹性拖拉

支持常见的ListView、ScrollView、HorizontalScrollView、NestedScrollView、RecyclerView、ViewPager、WebView等控件的组合使用

支持页面进入监听、支持滚动监听(仅监听第一个页面)、支持拖拽监听

支持根据id跳转到指定页面

更多特性和相关使用细节请到项目使用页面查看

项目简单介绍

原理

DragToDetail继承自LinearLayout,通过覆写LinearLayout的三个相关触摸事件来达到内部多个子控件的拖动配合。在拖动过程中最重要的一点就是对控件可滑动与否的判断,好在Android在v4包中给我们提供了ViewCompat.canScrollVertically(target, direction)ViewCompat.canScrollHorizontally(target, direction)这两个方法,第一个方法是用来判断控件是否可以在垂直方向滚动的,有两个参数target和direction,其中target表示当前滑动view,direction表示可以滑动的方向,负数表示是否可以向下滑动,正数表示是否可以向上滑动。第二个方法用来判断控件是否可以再水平方向继续滚动,参数和第一个方法的意思相同,这里就不再多多叙述了,读者可以自行查看相关源码。DragToDetail中有关是否可以继续在垂直方向滑动的代码片段如下:

/**
     * 判断控件是否可以在垂直方向滚动
     * @param target 控件
     * @param direction 滑动方向,正值为向上,负值为向下
     * @param event 事件
     * @return 能否滚动
     */
    private boolean canScrollVertically(View target, int direction, MotionEvent event){
        if(currentTargetView == null) return false;
        if(target instanceof ViewPager){
            ViewPager viewPager = (ViewPager) target;
            View currentPagerView = null;
            if(viewPager.getAdapter() instanceof DragFragmentPagerAdapter){
                currentPagerView = ((DragFragmentPagerAdapter) viewPager.getAdapter()).getCurrentView();
            } else if(viewPager.getAdapter() instanceof DragFragmentStatePagerAdapter){
                currentPagerView = ((DragFragmentStatePagerAdapter) viewPager.getAdapter()).getCurrentView();
            }
            return currentPagerView != null && canScrollVertically(currentPagerView, direction, event);
        } else if ((target instanceof AbsListView) ||
                (target instanceof RecyclerView) ||
                (target instanceof ScrollView) ||
                (target instanceof NestedScrollView) ||
                (target instanceof WebView)){
            return ViewCompat.canScrollVertically(target, direction);
        } else {
            if((target instanceof ViewGroup) && ((ViewGroup) target).getChildCount() > 0){
                ViewGroup vg = (ViewGroup) target;
                for (int i = 0; i < vg.getChildCount(); i++) {
                    View v = vg.getChildAt(i);
                    if(checkTouchRange(v, event)){
                        return canScrollVertically(v, direction, event);
                    }
                }
            } else {
                return ViewCompat.canScrollVertically(target, direction);
            }
        }
        return false;
    }
复制代码

水平方向的判断读者可以自行下载源码查看,其实原理一样,唯一的区别就是当target控件为ViewPager的时候,水平方向的判断依据是ViewPager是否滑动到最后一页面,垂直方向的判断需要根据Pager内部View来决定。

有关弹性滑动当然就是使用Scroller来实现了,在滑动过程中会持续监听手指滑动距离,当滑动距离大于设置的临界距离时,放开手指触发滑动到下一个页面,如果滑动距离小于设置的临界距离则直接回弹。临界距离可以通过属性reboundPercent来设置,这是一个float类型的数值,用来表示一个百分比,DragToDetail中有关弹性滑动代码片段如下(这里只截取垂直方向的处理):

case VERTICAL:
                startY = getScrollY();
                float yVelocity = mVelocityTracker.getYVelocity();
                int currentTargetViewHeight = currentTargetView.getMeasuredHeight();
                float baseHeight = currentTargetViewHeight * mReboundPercent;
                int accumulateHeight = accumulateViewHeight();
                int difference = startY - accumulateHeight;
                if(Math.abs(yVelocity) >= mMaxFlingVelocity){
                    if(yVelocity > 0){
                        //上一页
                        if(startY <= currentTargetViewHeight){
                            dy = -startY;
                        } else {
                            dy = -startY + currentTargetViewHeight;
                        }
                        changeCurrentTargetIndexAndView(false);
                    } else {
                        //下一页
                        dy = (accumulateHeight + currentTargetViewHeight) - startY;
                        changeCurrentTargetIndexAndView(true);
                    }
                } else {
                    if(Math.abs(difference) >= baseHeight){
                        //跳转到另外一个页面
                        if(difference < 0){
                            //上一页
                            dy = -(currentTargetViewHeight - Math.abs(difference));
                            changeCurrentTargetIndexAndView(false);
                        } else {
                            //下一页
                            dy = currentTargetViewHeight - Math.abs(difference);
                            changeCurrentTargetIndexAndView(true);
                        }
                    } else {
                        //回弹
                        dy = -difference;
                    }
                }
                break;
复制代码

DragToDetail中有关跳转到指定页面的原理,其实就是通过调用Scroller的startScroll,通过跳转的页面id计算出需要滑动的距离和跳转的方向,这里有一个需要注意的地方,从前向后跳的时候需要处理下中间跨度页面的滚动问题,因为如果不处理中间页面的滚动,那么跳转完页面后在回来,前面得页面可能无法滑动到底部,只能看到上半部分。这就是源码中之所以需要调用processEnd(View v)方法的原因。

最后,其实说到最后来看这个控件并没有什么偏避的知识点,都是一些对滑动事件和滚动监听的处理,这些东西平时在工作中多注意下,应该都不是问题,如果想要了解更多内容可以自行下载源码查看哦。。。

再来一遍GayHub地址:github.com/workdawn/Dr…

喜欢的话给个star吧!!!

相关参考:

juejin.im/post/5878dd…

blog.csdn.net/qifengdeqin…

转载于:https://juejin.im/post/5a364a655188252a3d3814c8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值