Android 在 RecyclerView 中快速滚动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26370289/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:19:21  来源:igfitidea点击:

Snappy scrolling in RecyclerView

androidandroid-recyclerview

提问by Catherine

I am trying to use the new RecyclerViewclass for a scenario where I want the component to snap to a specific element when scrolling (The old Android Gallerycomes to mind as an example of such a list with a center-locked item).

我正在尝试将新RecyclerView类用于我希望组件在滚动时捕捉到特定元素的场景(旧的 AndroidGallery被认为是具有中心锁定项目的此类列表的示例)。

This is the approach that I am taking thus far:

这是我目前采取的方法:

I have an interface, ISnappyLayoutManager, which contains a method, getPositionForVelocity, which calculates at which position the view should end the scrolling given the initial fling velocity.

我有一个接口,ISnappyLayoutManager,它包含一个方法,getPositionForVelocity,它计算视图应该在给定初始滑动速度的哪个位置结束滚动。

public interface ISnappyLayoutManager {
    int getPositionForVelocity(int velocityX, int velocityY);  
}

Then I have a class, SnappyRecyclerView, which subclasses RecyclerViewand overrides its fling() method in such a manner as to fling the view the exact right amount:

然后我有一个类,SnappyRecyclerView它子类化RecyclerView并覆盖它的 fling() 方法,以便将视图抛出正确的数量:

public final class SnappyRecyclerView extends RecyclerView {

    /** other methods deleted **/

    @Override
    public boolean fling(int velocityX, int velocityY) {
        LayoutManager lm = getLayoutManager();

        if (lm instanceof ISnappyLayoutManager) {
            super.smoothScrollToPosition(((ISnappyLayoutManager) getLayoutManager())
                    .getPositionForVelocity(velocityX, velocityY));
        }
        return true;
    }
}

I am not very happy with this approach for several reasons. First of all, it seems counter to the philosophy of the 'RecyclerView' to have to subclass it to implement a certain type of scrolling. Second, if I want to just use the default LinearLayoutManager, this becomes somewhat complex as I have to mess around with its internals in order to understand its current scroll state and calculate out exactly where this scrolls to. Finally, this doesn't even take care of all the possible scroll scenarios, as if you move the list and then pause and then lift a finger, no fling event occurs (the velocity is too low) and so the list remains in a halfway position. This can possibly be taken care of by adding an on scroll state listener to the RecyclerView, but that also feels very hacky.

由于几个原因,我对这种方法不太满意。首先,必须将其子类化以实现某种类型的滚动似乎与“RecyclerView”的哲学背道而驰。其次,如果我只想使用 default LinearLayoutManager,这会变得有些复杂,因为我必须弄乱其内部结构以了解其当前滚动状态并准确计算出滚动到的位置。最后,这甚至没有考虑到所有可能的滚动场景,就像您移动列表然后暂停然后抬起手指一样,没有发生 fling 事件(速度太低),因此列表保持在一半位置。这可以通过向 中添加滚动状态侦听器来解决RecyclerView,但这也感觉很hacky。

I feel like I must be missing something. Is there a better way to do this?

我觉得我一定错过了什么。有一个更好的方法吗?

回答by razzledazzle

With LinearSnapHelper, this is now very easy.

有了LinearSnapHelper,这现在很容易。

All you need to do is this:

您需要做的就是:

SnapHelper helper = new LinearSnapHelper();
helper.attachToRecyclerView(recyclerView);

It's that simple! Note that LinearSnapHelperwas added in the Support Library starting from version 24.2.0.

就是这么简单!请注意,LinearSnapHelper从版本 24.2.0 开始,它已添加到支持库中。

Meaning you have to add this to your app module's build.gradle

这意味着您必须将此添加到您的应用程序模块的 build.gradle

compile "com.android.support:recyclerview-v7:24.2.0"

Edit: AndroidX LinearSnapHelper

编辑:AndroidX LinearSnapHelper

回答by Catherine

I ended up coming up with something slightly different than the above. It's not ideal, but it's working acceptably well for me, and may be helpful to someone else. I won't accept this answer in the hopes that someone else comes along with something better and less hacky (and it's possible that I'm misunderstanding the RecyclerViewimplementation and missing some simple way of doing this, but in the meantime, this is good enough for government work!)

我最终想出了与上述略有不同的东西。这并不理想,但它对我来说效果很好,并且可能对其他人有帮助。我不会接受这个答案,希望其他人能提出更好、更简单的东西(而且我可能误解了RecyclerView实现并遗漏了一些简单的方法,但与此同时,这已经足够了为政府工作!)

The basics of the implementation are these: The scrolling in a RecyclerViewis sort of split up between the RecyclerViewand the LinearLayoutManager. There are two cases that I need to handle:

实现的基础是这些: a 中的滚动RecyclerView有点在RecyclerView和之间分开LinearLayoutManager。我需要处理两种情况:

  1. The user flings the view. The default behavior is that the RecyclerViewpasses the fling to an internal Scrollerwhich then performs the scrolling magic. This is problematic because then the RecyclerViewusually settles in an unsnapped position. I solve this by overriding the RecyclerViewfling()implementation and instead of flinging, smoothscroll the LinearLayoutManagerto a position.
  2. The user lifts their finger with insufficient velocity to initiate a scroll. No fling occurs in this case. I want to detect this case in the event that the view is not in a snapped position. I do this by overriding the onTouchEventmethod.
  1. 用户抛出视图。默认行为是RecyclerView将 fling 传递给内部Scroller,然后执行滚动魔术。这是有问题的,因为RecyclerView通常会固定在未咬合的位置。我通过覆盖RecyclerViewfling()实现来解决这个问题,而不是抛出,平滑滚动LinearLayoutManager到一个位置。
  2. 用户抬起手指的速度不足以启动滚动。在这种情况下不会发生抛掷。如果视图不在捕捉位置,我想检测这种情况。我通过覆盖该onTouchEvent方法来做到这一点。

The SnappyRecyclerView:

SnappyRecyclerView

public final class SnappyRecyclerView extends RecyclerView {

    public SnappyRecyclerView(Context context) {
        super(context);
    }

    public SnappyRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SnappyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean fling(int velocityX, int velocityY) {
        final LayoutManager lm = getLayoutManager();        

      if (lm instanceof ISnappyLayoutManager) {
            super.smoothScrollToPosition(((ISnappyLayoutManager) getLayoutManager())
                    .getPositionForVelocity(velocityX, velocityY));
            return true;
        }
        return super.fling(velocityX, velocityY);
    }        

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // We want the parent to handle all touch events--there's a lot going on there, 
        // and there is no reason to overwrite that functionality--bad things will happen.
        final boolean ret = super.onTouchEvent(e);
        final LayoutManager lm = getLayoutManager();        

      if (lm instanceof ISnappyLayoutManager
                && (e.getAction() == MotionEvent.ACTION_UP || 
                    e.getAction() == MotionEvent.ACTION_CANCEL)
                && getScrollState() == SCROLL_STATE_IDLE) {
            // The layout manager is a SnappyLayoutManager, which means that the 
            // children should be snapped to a grid at the end of a drag or 
            // fling. The motion event is either a user lifting their finger or 
            // the cancellation of a motion events, so this is the time to take 
            // over the scrolling to perform our own functionality.
            // Finally, the scroll state is idle--meaning that the resultant 
            // velocity after the user's gesture was below the threshold, and 
            // no fling was performed, so the view may be in an unaligned state 
            // and will not be flung to a proper state.
            smoothScrollToPosition(((ISnappyLayoutManager) lm).getFixScrollPos());
        }        

      return ret;
    }
}

An interface for snappy layout managers:

快速布局管理器的界面:

/**
 * An interface that LayoutManagers that should snap to grid should implement.
 */
public interface ISnappyLayoutManager {        

    /**
     * @param velocityX
     * @param velocityY
     * @return the resultant position from a fling of the given velocity.
     */
    int getPositionForVelocity(int velocityX, int velocityY);        

    /**
     * @return the position this list must scroll to to fix a state where the 
     * views are not snapped to grid.
     */
    int getFixScrollPos();        

}

And here is an example of a LayoutManagerthat subclasses the LinearLayoutManagerto result in a LayoutManagerwith smooth scrolling:

下面是一个将 aLayoutManager子类化LinearLayoutManager以产生LayoutManager平滑滚动的 a 示例:

public class SnappyLinearLayoutManager extends LinearLayoutManager implements ISnappyLayoutManager {
    // These variables are from android.widget.Scroller, which is used, via ScrollerCompat, by
    // Recycler View. The scrolling distance calculation logic originates from the same place. Want
    // to use their variables so as to approximate the look of normal Android scrolling.
    // Find the Scroller fling implementation in android.widget.Scroller.fling().
    private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
    private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
    private static double FRICTION = 0.84;

    private double deceleration;

    public SnappyLinearLayoutManager(Context context) {
        super(context);
        calculateDeceleration(context);
    }

    public SnappyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        calculateDeceleration(context);
    }

    private void calculateDeceleration(Context context) {
        deceleration = SensorManager.GRAVITY_EARTH // g (m/s^2)
                * 39.3700787 // inches per meter
                // pixels per inch. 160 is the "default" dpi, i.e. one dip is one pixel on a 160 dpi
                // screen
                * context.getResources().getDisplayMetrics().density * 160.0f * FRICTION;
    }

    @Override
    public int getPositionForVelocity(int velocityX, int velocityY) {
        if (getChildCount() == 0) {
            return 0;
        }
        if (getOrientation() == HORIZONTAL) {
            return calcPosForVelocity(velocityX, getChildAt(0).getLeft(), getChildAt(0).getWidth(),
                    getPosition(getChildAt(0)));
        } else {
            return calcPosForVelocity(velocityY, getChildAt(0).getTop(), getChildAt(0).getHeight(),
                    getPosition(getChildAt(0)));
        }
    }

    private int calcPosForVelocity(int velocity, int scrollPos, int childSize, int currPos) {
        final double dist = getSplineFlingDistance(velocity);

        final double tempScroll = scrollPos + (velocity > 0 ? dist : -dist);

        if (velocity < 0) {
            // Not sure if I need to lower bound this here.
            return (int) Math.max(currPos + tempScroll / childSize, 0);
        } else {
            return (int) (currPos + (tempScroll / childSize) + 1);
        }
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position) {
        final LinearSmoothScroller linearSmoothScroller =
                new LinearSmoothScroller(recyclerView.getContext()) {

                    // I want a behavior where the scrolling always snaps to the beginning of 
                    // the list. Snapping to end is also trivial given the default implementation. 
                    // If you need a different behavior, you may need to override more
                    // of the LinearSmoothScrolling methods.
                    protected int getHorizontalSnapPreference() {
                        return SNAP_TO_START;
                    }

                    protected int getVerticalSnapPreference() {
                        return SNAP_TO_START;
                    }

                    @Override
                    public PointF computeScrollVectorForPosition(int targetPosition) {
                        return SnappyLinearLayoutManager.this
                                .computeScrollVectorForPosition(targetPosition);
                    }
                };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }

    private double getSplineFlingDistance(double velocity) {
        final double l = getSplineDeceleration(velocity);
        final double decelMinusOne = DECELERATION_RATE - 1.0;
        return ViewConfiguration.getScrollFriction() * deceleration
                * Math.exp(DECELERATION_RATE / decelMinusOne * l);
    }

    private double getSplineDeceleration(double velocity) {
        return Math.log(INFLEXION * Math.abs(velocity)
                / (ViewConfiguration.getScrollFriction() * deceleration));
    }

    /**
     * This implementation obviously doesn't take into account the direction of the 
     * that preceded it, but there is no easy way to get that information without more
     * hacking than I was willing to put into it.
     */
    @Override
    public int getFixScrollPos() {
        if (this.getChildCount() == 0) {
            return 0;
        }

        final View child = getChildAt(0);
        final int childPos = getPosition(child);

        if (getOrientation() == HORIZONTAL
                && Math.abs(child.getLeft()) > child.getMeasuredWidth() / 2) {
            // Scrolled first view more than halfway offscreen
            return childPos + 1;
        } else if (getOrientation() == VERTICAL
                && Math.abs(child.getTop()) > child.getMeasuredWidth() / 2) {
            // Scrolled first view more than halfway offscreen
            return childPos + 1;
        }
        return childPos;
    }

}

回答by humblerookie

I've managed to find a cleaner way to do this. @Catherine (OP) let me know if this can be improved or you feel is an improvement over yours :)

我设法找到了一种更清洁的方法来做到这一点。@Catherine (OP) 让我知道这是否可以改进或者你觉得比你的有所改进:)

Here's the scroll listener I use.

这是我使用的滚动侦听器。

https://github.com/humblerookie/centerlockrecyclerview/

https://github.com/humblerookie/centerlockrecyclerview/

I've omitted some minor assumptions here like for eg.

我在这里省略了一些小假设,例如。

1) Initial and final paddings: First and last items in the horizontal scroll need to have initial and final paddings respectively set so that the initial and final views are at center when scrolled to first and last respectively.For eg in the onBindViewHolderyou could do something like this.

1)初始和最终填充:水平滚动中的第一个和最后一个项目需要分别设置初始和最终填充,以便初始和最终视图分别在滚动到第一个和最后一个时位于中心。例如在onBindViewHolder 中,您可以这样做像这样的东西。

@Override
public void onBindViewHolder(ReviewHolder holder, int position) {
holder.container.setPadding(0,0,0,0);//Resetpadding
     if(position==0){
//Only one element
            if(mData.size()==1){
                holder.container.setPadding(totalpaddinginit/2,0,totalpaddinginit/2,0);
            }
            else{
//>1 elements assign only initpadding
                holder.container.setPadding(totalpaddinginit,0,0,0);
            }
        }
        else
        if(position==mData.size()-1){
            holder.container.setPadding(0,0,totalpaddingfinal,0);
        } 
}

 public class ReviewHolder extends RecyclerView.ViewHolder {

    protected TextView tvName;
    View container;

    public ReviewHolder(View itemView) {
        super(itemView);
        container=itemView;
        tvName= (TextView) itemView.findViewById(R.id.text);
    }
}

The logic is prettty generic and one can use it for a lot of other cases. My case the recycler view is horizontal and stretches the entire horizontal width without margins( basically recyclerview's center X coordinate is the screen's center)or uneven paddings.

该逻辑非常通用,可以将其用于许多其他情况。在我的情况下,回收视图是水平的,并且在没有边距的情况下拉伸整个水平宽度(基本上回收视图的中心 X 坐标是屏幕的中心)或不均匀的填充。

Incase anyone is facing issue kindly comment.

如果有人遇到问题,请发表评论。

回答by Thomas R.

I also needed a snappy recycler view. I want to let the recycler view item snap to the left of a column. It ended up with implementing a SnapScrollListener which I set on the recycler view. This is my code:

我还需要一个活泼的回收者视图。我想让回收商查看项目对齐到列的左侧。它最终实现了我在回收器视图上设置的 SnapScrollListener。这是我的代码:

SnapScrollListener:

SnapScrollListener:

class SnapScrollListener extends RecyclerView.OnScrollListener {

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        if (RecyclerView.SCROLL_STATE_IDLE == newState) {
            final int scrollDistance = getScrollDistanceOfColumnClosestToLeft(mRecyclerView);
            if (scrollDistance != 0) {
                mRecyclerView.smoothScrollBy(scrollDistance, 0);
            }
        }
    }

}

Calculation of snap:

快照计算:

private int getScrollDistanceOfColumnClosestToLeft(final RecyclerView recyclerView) {
    final LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
    final RecyclerView.ViewHolder firstVisibleColumnViewHolder = recyclerView.findViewHolderForAdapterPosition(manager.findFirstVisibleItemPosition());
    if (firstVisibleColumnViewHolder == null) {
        return 0;
    }
    final int columnWidth = firstVisibleColumnViewHolder.itemView.getMeasuredWidth();
    final int left = firstVisibleColumnViewHolder.itemView.getLeft();
    final int absoluteLeft = Math.abs(left);
    return absoluteLeft <= (columnWidth / 2) ? left : columnWidth - absoluteLeft;
}

If the first visible view is scrolled more than the half width out of the screen, the next visible column is snapping to the left.

如果第一个可见视图滚动到屏幕外的一半宽度以上,则下一个可见列将向左对齐。

Setting the listener:

设置监听器:

mRecyclerView.addOnScrollListener(new SnapScrollListener());

回答by eDizzle

Here's a simpler hack for smooth scrolling to a certain position on a fling event:

这是一个更简单的技巧,用于在投掷事件中平滑滚动到某个位置:

@Override
public boolean fling(int velocityX, int velocityY) {

    smoothScrollToPosition(position);
    return super.fling(0, 0);
}

Override the fling method with a call to smoothScrollToPosition(int position), where "int position" is the position of the view you want in the adapter. You will need to get the value of the position somehow, but that's dependent on your needs and implementation.

通过调用 smoothScrollToPosition(int position) 覆盖 fling 方法,其中“int position”是您想要的视图在适配器中的位置。您需要以某种方式获得职位的价值,但这取决于您的需求和实施。

回答by Lauw

After messing around with RecyclerView for a bit, this is what I came up with so far and what I'm using right now. It has one minor flaw, but I won't spill the beans (yet) since you probably won't notice.

在与 RecyclerView 混为一谈之后,这就是我目前想到的以及我现在正在使用的。它有一个小缺陷,但我不会(还)洒豆子,因为您可能不会注意到。

https://gist.github.com/lauw/fc84f7d04f8c54e56d56

https://gist.github.com/lauw/fc84f7d04f8c54e56d56

It only supports horizontal recyclerviews and snaps to the center and can also scale down views based on how far they are from the center. Use as a replacement of RecyclerView.

它只支持水平回收视图并捕捉到中心,还可以根据它们离中心的远近缩小视图。用作 RecyclerView 的替代品。

Edit: 08/2016 Made it into a repository:
https://github.com/lauw/Android-SnappingRecyclerView
I'll just keep this up while working on a better implementation.

编辑:08/2016 将其放入存储库:
https: //github.com/lauw/Android-SnappingRecyclerView
我会在努力实现更好的实现的同时保持这一点。

回答by Nati Dykstein

A very simple approach for achieving a snap-to-position behavior -

实现对齐位置行为的一种非常简单的方法 -

    recyclerView.setOnScrollListener(new OnScrollListener() {
        private boolean scrollingUp;

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            // Or use dx for horizontal scrolling
            scrollingUp = dy < 0;
        }

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            // Make sure scrolling has stopped before snapping
            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                // layoutManager is the recyclerview's layout manager which you need to have reference in advance
                int visiblePosition = scrollingUp ? layoutManager.findFirstVisibleItemPosition()
                        : layoutManager.findLastVisibleItemPosition();
                int completelyVisiblePosition = scrollingUp ? layoutManager
                        .findFirstCompletelyVisibleItemPosition() : layoutManager
                        .findLastCompletelyVisibleItemPosition();
                // Check if we need to snap
                if (visiblePosition != completelyVisiblePosition) {
                    recyclerView.smoothScrollToPosition(visiblePosition);
                    return;
                }

        }
    });

The only small downside is that it will not snap backwards when you scroll less than half way of the partially visible cell - but if this doesn't bother you than it's a clean and simple solution.

唯一的小缺点是,当您滚动不到部分可见单元格的一半时,它不会向后弹跳 - 但如果这不会打扰您,那么它是一个干净而简单的解决方案。

回答by Prakhar1001

If you need snapping support to start, top, end or bottom, use GravitySnapHelper(https://github.com/rubensousa/RecyclerViewSnap/blob/master/app/src/main/java/com/github/rubensousa/recyclerviewsnap/GravitySnapHelper.java).

如果您需要捕捉支持来开始、顶部、结束或底部,请使用 GravitySnapHelper( https://github.com/rubensousa/RecyclerViewSnap/blob/master/app/src/main/java/com/github/rubensousa/recyclerviewsnap/GravitySnapHelper .java)。

Snapping center:

捕捉中心:

SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

Snapping start with GravitySnapHelper:

使用 GravitySnapHelper 开始捕捉:

startRecyclerView.setLayoutManager(new LinearLayoutManager(this,
                LinearLayoutManager.HORIZONTAL, false));

SnapHelper snapHelperStart = new GravitySnapHelper(Gravity.START);
snapHelperStart.attachToRecyclerView(startRecyclerView);

Snapping top with GravitySnapHelper:

使用 GravitySnapHelper 捕捉顶部:

topRecyclerView.setLayoutManager(new LinearLayoutManager(this));

SnapHelper snapHelperTop = new GravitySnapHelper(Gravity.TOP);
snapHelperTop.attachToRecyclerView(topRecyclerView);

回答by Michail L

I have implemented a working solution for Horizontal orientation of RecyclerView, that just reads coordinates onTouchEvent, on first MOVE and on UP. On UP calculate the position we need to go to.

我已经为 RecyclerView 的水平方向实现了一个工作解决方案,它只读取坐标 onTouchEvent、第一次移动和向上。在 UP 上计算我们需要去的位置。

public final class SnappyRecyclerView extends RecyclerView {

private Point   mStartMovePoint = new Point( 0, 0 );
private int     mStartMovePositionFirst = 0;
private int     mStartMovePositionSecond = 0;

public SnappyRecyclerView( Context context ) {
    super( context );
}

public SnappyRecyclerView( Context context, AttributeSet attrs ) {
    super( context, attrs );
}

public SnappyRecyclerView( Context context, AttributeSet attrs, int defStyle ) {
    super( context, attrs, defStyle );
}


@Override
public boolean onTouchEvent( MotionEvent e ) {

    final boolean ret = super.onTouchEvent( e );
    final LayoutManager lm = getLayoutManager();
    View childView = lm.getChildAt( 0 );
    View childViewSecond = lm.getChildAt( 1 );

    if( ( e.getAction() & MotionEvent.ACTION_MASK ) == MotionEvent.ACTION_MOVE
            && mStartMovePoint.x == 0) {

        mStartMovePoint.x = (int)e.getX();
        mStartMovePoint.y = (int)e.getY();
        mStartMovePositionFirst = lm.getPosition( childView );
        if( childViewSecond != null )
            mStartMovePositionSecond = lm.getPosition( childViewSecond );

    }// if MotionEvent.ACTION_MOVE

    if( ( e.getAction() & MotionEvent.ACTION_MASK ) == MotionEvent.ACTION_UP ){

        int currentX = (int)e.getX();
        int width = childView.getWidth();

        int xMovement = currentX - mStartMovePoint.x;
        // move back will be positive value
        final boolean moveBack = xMovement > 0;

        int calculatedPosition = mStartMovePositionFirst;
        if( moveBack && mStartMovePositionSecond > 0 )
            calculatedPosition = mStartMovePositionSecond;

        if( Math.abs( xMovement ) > ( width / 3 )  )
            calculatedPosition += moveBack ? -1 : 1;

        if( calculatedPosition >= getAdapter().getItemCount() )
            calculatedPosition = getAdapter().getItemCount() -1;

        if( calculatedPosition < 0 || getAdapter().getItemCount() == 0 )
            calculatedPosition = 0;

        mStartMovePoint.x           = 0;
        mStartMovePoint.y           = 0;
        mStartMovePositionFirst     = 0;
        mStartMovePositionSecond    = 0;

        smoothScrollToPosition( calculatedPosition );
    }// if MotionEvent.ACTION_UP

    return ret;
}}

Works fine for me, let me know if something is wrong.

对我来说很好用,如果有什么问题请告诉我。