Android学习Android知识Android开发

项目需求讨论- 自定义滚轮(第二波新实现)

2017-07-13  本文已影响299人  青蛙要fly

大家好,在前段时间我写过用ScrollView实现了自定义滚轮,但是在循环的效果不是特别好

项目需求讨论-自定义滚轮

用ScrollView 循环有什么问题呢。

  1. 因为我们是重复建立数据,比如数据是[A,B,C,D,E,F],你可以做成假循环,比如变为[A,B,C,D,E,F][A,B,C,D,E,F][A,B,C,D,E,F],变为三遍,但是变到上面一组后,因为要重新回到中间,所以你会发现闪动一下的感觉,因为比如冲第一组的A(position为0)到了第二组的A(position为6)。
  2. 而且如果你手指快速的滑动,不停的滚动,你就会滑到顶部的位置。因为我们的是ScrollView 最后选中哪一项,才让它滚动到中间相应的那一项。
  3. 那有些人可能会说,那我就不只弄这几组。我就多弄几组不就好了。别人快速滑动也滑不到顶部了。Too young Too Simple。比如我用11组。但是你会发现,你的界面加载直接很久很久,因为ScrollView内的控件都直接要初始化好,因为你设置了11组。等于有66个Item在加载完。就会让界面卡死在那里。所以体验就更差了。

最后感谢黑马飞马同学给的意见。

对啊。我们的RecyclerView 是只会加载界面当前显示的Item,然后不管数量再多,也只是在复用相同的View而已。这样我们上面的问题不就解决了。因为比如我们建立一千组一万组数据,我不需要考虑要重新滚回中间,问题1和2就解决了。问题3因为RecyclerView 的特性,也被解决了。是一个很理想的循环滚动的滚轮。

于是就使用RecycleViewer来进行相关的开发。正式起航。


原理分析

  1. 滚轮的高度和Item的高度
    比如我们确定一个页面显示5项,item的布局高度为100dp,那滚轮高度就设定为500dp.

  2. 怎么确定RecyclerView 停止滚动
    自定义ScrollerListener 继承RecyclerView.OnScrollListener,复写里面的

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    super.onScrollStateChanged(recyclerView, newState);
    switch (newState) {
        case RecyclerView.SCROLL_STATE_IDLE:
        .....
        .....
        .....
        break;
    }
}

当state变为了RecyclerView.SCROLL_STATE_IDLE就说明了RecyclerView已经停止了。

3.比如只划一部分,如何让它自动滚到相应的Item(重点)

方法还是一样,通过当前获取到的滚到的Y值,然后除以每项的Item的高度,就能知道当前顶部是处于第几项,然后求余数就知道了当前顶部那项有多少是显示的。在上文我们ScrollView 中,我们使用的是getScrollY()方法来获取的,我本来在

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

        case RecyclerView.SCROLL_STATE_IDLE:
            recyclerView.getScrollY();
            
            break;
    }
}

所以我在onScrollStateChanged方法中通过getScrollY()方法去获取,多么Easy,哈哈,结果这次是我Too young Too simple,获取到的值一直为0。WTF!!然后就只能通过其他方式来获取滚动的距离。
获取滚动的距离:

public int getScollYDistance(RecyclerView recyclerView) {
    LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    int position = layoutManager.findFirstVisibleItemPosition();
    View firstVisiableChildView = layoutManager.findViewByPosition(position);
    int itemHeight = firstVisiableChildView.getHeight();
    return (position) * itemHeight - firstVisiableChildView.getTop();
}

我想这个大家应该看得懂吧。我画个图解释一下就可以了:

我来大致解释下:如上图所示,我们现在一个Item是100的高度,那我们现在滑到了第二个的20的位置,那是不是一共滑动了120的距离。因为我们当前获取到该手机界面上显示的第一个的position是1,说明position为0的已经被滑出去了。外加这个当前界面的显示的position为1的item有部分被滑出去,所以我们获取它的getTop值为-20,所以是不是正好是当前界面显示的第一个Item的position,乘以itemHeight,减去这个item 的getTop的值。(1 * 100 - (-20) = 120)

好的,我们已经解决了滚动距离的问题。那现在就是我们要让他滚动到一定距离,自动调整自己的位置,来正好显示某个Item项,而不会出现某个Item在界面上显示一半。


滚动后调整距离让RecyclerView 滚到特定的position位置:

我简单介绍,就只分二种情况来谈下(正好滑到一个标准的距离,让Item正好完全显示这种情况我就去除了):

  1. 顶部的Item有小于一半ItemHeight的距离滚到了屏幕的外面:


    这时候很简单,大家说获取到第一个Item的Position值,然后调用RecyclerView.smoothScrollToPosition(Position),跳到这个positionItem就可以了么。没错。这个是可以。但是调用这个方法,在接下去的第二种情况下就出现问题了。
  2. 顶部的Item有大于一半ItemHeight的距离滚到了屏幕外面:


这时候大家也知道,应该是让当前的屏幕内获取到的first Item 滚动出界面,所以大家一想就说获取第一个Item的Position值,然后调用RecyclerView.smoothScrollToPosition(Position + 1)不就可以了么。。完美!!。但是结果是不会滚动,原来这个方法当我们的Position + 1已经出现在屏幕上了。不管是不是第一个,不管处于屏幕的哪个位置,这个RecyclerView就不会滚动。我忍不住又一句 WHF!!。那应该怎么处理呢。

RecyclerView.ScrollBy方法
其实很简单。我直接抛弃了RecyclerView.smoothScrollToPosition方法,我们看到了,其实我们是不是可以通过判断,第一个Item有没有滚出一半的ItemHeight的距离在外面。无非是二种情况(假设一个ItemHeight为100):

  1. 已经有80滚动在外面了。我就通过ScrollBy 再向上过给它滚动20到外面。
  2. 已经有20滚动在外面了。我就通过ScrollBy 再向下返回20到里面。

所以代码如下:

private void smoothMoveToPosition(int n, RecyclerView mRecyclerView, LinearLayoutManager mLinearLayoutManager) {
    int firstItem = mLinearLayoutManager.findFirstVisibleItemPosition();
    if (firstItem == n) {
        int top = mRecyclerView.getChildAt(n - firstItem + 1).getTop();
        mRecyclerView.smoothScrollBy(0, -(itemHeight - top));

    } else {
        int top = mRecyclerView.getChildAt(n - firstItem).getTop();
        mRecyclerView.smoothScrollBy(0, top);
    }
}

SO EASY..妈妈再也不用担心我的学习了。步步高打火机,哪里不会点哪里


附上DEMO

上一篇下一篇

猜你喜欢

热点阅读