Android源码初探之ListView 的 smoothScrollByOffset()

本文探讨了在Android中,ListView如何根据Position实现平滑滚动到指定位置的功能。通过分析`smoothScrollByOffset()`方法,发现其内部不仅涉及简单的父类调用,还包含复杂的判断逻辑和动画计算,确保视图正确滚动到目标位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

剧情:
前几天参加了去哪儿网的泛前段的分享会,谈的大多是去哪儿网对FaceBook的ReactNative的优化。谈到ListView的优化问题,大致意思是reactnative在不知道控件高度的情况下不容易直接根据Position找到控件的位置。不嫌事大的小伙伴问了个问题:
请问原声的Android是如何根据Position找到ListView的位置的呢?
答曰:
原声是可以直接根据位置找到的。
全场:嗯嗯嗯嗯~。
宝宝第一个不服,我就不感觉原声有这么厉害。
看了下ListView的方法,大概大神们说的是这个方法。

 listview.smoothScrollToPosition(int position);

第一层的smoothScrollByOffset(int offset)仅仅是super了一下父类的方法。

  public void smoothScrollByOffset(int offset) {
        super.smoothScrollByOffset(offset);
    }

里面是什么呢?

/**
     * Allows RemoteViews to scroll relatively to a position.
     */
    void smoothScrollByOffset(int position) {
        int index = -1;
        if (position < 0) {
            index = getFirstVisiblePosition();//获取第一个可见的位置
        } else if (position > 0) {
            index = getLastVisiblePosition();
        }

        if (index > -1) {
            View child = getChildAt(index - getFirstVisiblePosition());
            if (child != null) {
                Rect visibleRect = new Rect();//声明一个矩形
                if (child.getGlobalVisibleRect(visibleRect)) {
                    // the child is partially visible
                    int childRectArea = child.getWidth() * child.getHeight();
                    int visibleRectArea = visibleRect.width() * visibleRect.height();
                    float visibleArea = (visibleRectArea / (float) childRectArea);
                    final float visibleThreshold = 0.75f;
                    if ((position < 0) && (visibleArea < visibleThreshold)) {
                        // the top index is not perceivably visible so offset
                        // to account for showing that top index as well
                        ++index;
                    } else if ((position > 0) && (visibleArea < visibleThreshold)) {
                        // the bottom index is not perceivably visible so offset
                        // to account for showing that bottom index as well
                        --index;
                    }
                }
                smoothScrollToPosition(Math.max(0, Math.min(getCount(), index + position)));
            }
        }
    }

大致意思是让一个比较远的View滚动到一个相对的位置。这个方法还是只进行了一些判断,包括上划下滑,最后一个可见区域如果不足百分之75 就取消掉等等。在进去一层。

public void smoothScrollToPosition(int position) {
        if (mPositionScroller == null) {
            mPositionScroller = createPositionScroller();
        }
        mPositionScroller.start(position);
    }

貌似终于要开始滑动了。
再进去一层。

@Override
        public void start(final int position) {
            stop();

            if (mDataChanged) {
                // Wait until we're back in a stable state to try this.
                mPositionScrollAfterLayout = new Runnable() {
                    @Override public void run() {
                        start(position);
                    }
                };
                return;
            }

            final int childCount = getChildCount();
            if (childCount == 0) {
                // Can't scroll without children.
                return;
            }

            final int firstPos = mFirstPosition;
            final int lastPos = firstPos + childCount - 1;

            int viewTravelCount;
            int clampedPosition = Math.max(0, Math.min(getCount() - 1, position));
            if (clampedPosition < firstPos) {
                viewTravelCount = firstPos - clampedPosition + 1;
                mMode = MOVE_UP_POS;
            } else if (clampedPosition > lastPos) {
                viewTravelCount = clampedPosition - lastPos + 1;
                mMode = MOVE_DOWN_POS;
            } else {
                scrollToVisible(clampedPosition, INVALID_POSITION, SCROLL_DURATION);
                return;
            }

            if (viewTravelCount > 0) {
                mScrollDuration = SCROLL_DURATION / viewTravelCount;
            } else {
                mScrollDuration = SCROLL_DURATION;
            }
            mTargetPos = clampedPosition;
            mBoundPos = INVALID_POSITION;
            mLastSeenPos = INVALID_POSITION;

            postOnAnimation(this);
        }

结合动画的源码,大致可以看出来,绝对不是不需要计算的,在源码中计算了每此需要滑动的距离,剩余的距离,滑动一格的时间,并且递归调用,直到到了指定的位置为止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值