Android 自定义有速率差的联动ScrollView

这个是原创。。
项目需要实现类似京东金融首页联动滑动的效果,图如下


Android 自定义有速率差的联动ScrollView_第1张图片
555.gif

第一个想到的实现方式是上面使用horizontalScrollview,下面使用Viewpager,经过尝试之后发现二者API有限,不能达到理想效果。几经折腾,最后上下都使用了自定义的RecyclerView。效果图如下


Android 自定义有速率差的联动ScrollView_第2张图片
222.gif

现在来分析技术点,首先是上下联动,思路是在Recycleview的onScrolled回调方法中操作另一个Recycleview的滑动

@Override
    public void onScrolled(int dx, int dy) {
        super.onScrolled(dx, dy);
        sx = sx +dx;
        if (scrollViewListener != null && isMark) {
            scrollViewListener.onScrollChanged(this, sx, 0);
        }
    }

其中onScrollChanged方法在主页面中实现

@Override
    public void onScrollChanged(Object scrollView, int x, int y) {
        int width1 = CommonUtil.getScreenWidth(this) - DensityUtils.dip2px(this, 60);
        int width2 = CommonUtil.getScreenWidth(this);
        if (scrollView == rvHead) {
            rvFoot.setmark(false);
            rvFoot.scrollTo(x * width2 / width1, y);
        } else if (scrollView == rvFoot) {
            rvHead.setmark(false);

            rvHead.scrollTo(x * width1 / width2, y);
        }
        rvHead.setmark(true);
        rvFoot.setmark(true);
    }

上下View的滑动速率差即为上下RecyclerView中item的宽度差,上面view中item的宽度为屏幕宽度-60dp,详见对应的adapter。
由于RecyclerView中scrollTo方法没有实现,所以直接想到的是用scroolBy代替,但由于滑动回调返回的是Int值,经过速率差处理后精度丢失,得不到准确值,导致联动效果达不到,痛定思痛,最后还是自己来重写scrollTo方法

  @Override
    public void scrollTo(int x, int y) {
        scrollBy(x-sx,0);
    }

sx为自己在onScrolled方法中记录,具体见文末给出的源码
滑动之后,还要进行回调处理,以达到像viewPager那样的回弹效果,具体逻辑在自定义的RecyclerView中的回调方法onScrollStateChanged中实现

@Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                int mmSelected;
                //当控件停止滚动时,获取可视范围第一个item的位置,滚动调整控件以使选中的item刚好处于正中间
                int firstVisiblePos = mLayoutManager.findFirstVisibleItemPosition();
                if (firstVisiblePos == RecyclerView.NO_POSITION) {
                    return;
                }
                Rect rect = new Rect();
                mLayoutManager.findViewByPosition(firstVisiblePos).getHitRect(rect);
                Log.e("left1",rect.left+"");
                if (rect.left == 0)return;
                if (Math.abs(rect.left) > mItemWidth / 2) {
                    smoothScrollBy(rect.right - DensityUtils.dip2px(getContext(), 20), 0);
                    mmSelected = firstVisiblePos + 1;
                } else {
                    smoothScrollBy(rect.left - DensityUtils.dip2px(getContext(), 20), 0);
                    mmSelected = firstVisiblePos;
                }

为了让滑动效果更为自然且支持fling效果,本项目还重写了RecyclerView的fling方法,使得每次fling都恰好能滑动整数个item,大致思路为调整fling初始速率,代码如下

@Override
    public boolean fling(int velocityX, int velocityY) {
        int v;
        int touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        Log.e("=========","x "+velocityX+" s "+touchSlop);
        if (Math.abs(velocityX) <= 3*touchSlop) return false;
        mPhysicalCoeff = SensorManager.GRAVITY_EARTH   // g (m/s^2)
                * 39.37f               // inch/meter
                * getContext().getResources().getDisplayMetrics().density * 160.0f                 // pixels per inch
                * 0.84f;
        int firstVisiblePos = mLayoutManager.findFirstVisibleItemPosition();
        if (firstVisiblePos == RecyclerView.NO_POSITION) {
            return false;
        }
        Rect rect = new Rect();
        mLayoutManager.findViewByPosition(firstVisiblePos).getHitRect(rect);
        double n = getSplineFlingDistance(velocityX) / mItemWidth;
        int num = Double.valueOf(n).intValue();
        if (velocityX > 0)
            v = Double.valueOf(getVelocityByDistance(num * mItemWidth + Math.abs(rect.right)- DensityUtils.dip2px(getContext(), 20))).intValue();
        else
            v = Double.valueOf(getVelocityByDistance(num * mItemWidth + Math.abs(rect.left)+ DensityUtils.dip2px(getContext(), 20))).intValue();
        if (velocityX < 0) v = -v;
        return super.fling(v, velocityY);
    }

github地址 https://github.com/meiniepan/TogetherScrollView

你可能感兴趣的:(Android 自定义有速率差的联动ScrollView)