RecyclerView의 빠른 스크롤
RecyclerView스크롤 할 때 구성 요소가 특정 요소에 스냅되도록하는 시나리오에 대해 새 클래스 를 사용하려고합니다 ( Gallery가운데 잠금 항목이있는 목록의 예로 이전 Android 가 떠 오릅니다).
이것이 내가 지금까지 취하는 접근 방식입니다.
나는 초기 플링 속도가 주어 졌을 때 뷰가 스크롤을 종료해야하는 위치를 계산 ISnappyLayoutManager하는 메서드를 포함 하는 인터페이스를 가지고 있습니다 getPositionForVelocity.
public interface ISnappyLayoutManager {
int getPositionForVelocity(int velocityX, int velocityY);
}
그런 다음 보기에 정확한 양을 표시하는 방식으로 fling () 메서드 SnappyRecyclerView를 하위 클래스로 지정 RecyclerView하고 재정의 하는 클래스 가 있습니다.
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;
}
}
나는 몇 가지 이유로이 접근 방식이별로 만족스럽지 않다. 우선, 특정 유형의 스크롤링을 구현하기 위해 하위 클래스를 만들어야한다는 'RecyclerView'의 철학에 반하는 것 같습니다. 둘째, default를 사용하고 싶다면 LinearLayoutManager현재 스크롤 상태를 이해하고 스크롤 위치를 정확히 계산하기 위해 내부를 엉망으로 만들어야하기 때문에 다소 복잡해집니다. 마지막으로 목록을 이동 한 다음 일시 중지했다가 손가락을 떼면 플링 이벤트가 발생하지 않고 (속도가 너무 낮음) 목록이 중간에 남아있는 것처럼 가능한 모든 스크롤 시나리오를 처리하지 않습니다. 위치. 이것은 스크롤 상태 리스너를에 추가하여 처리 할 수 RecyclerView있지만 매우 엉망인 것 같습니다.
뭔가 빠진 것 같아. 이 작업을 수행하는 더 좋은 방법이 있습니까?
를 사용하면 LinearSnapHelper이제 매우 쉽게 할 수 있습니다.
다음과 같이하면됩니다.
SnapHelper helper = new LinearSnapHelper();
helper.attachToRecyclerView(recyclerView);
간단합니다! 참고 LinearSnapHelper지원 라이브러리 버전 24.2.0부터 추가되었습니다.
이는 앱 모듈의 build.gradle
compile "com.android.support:recyclerview-v7:24.2.0"
나는 위와 약간 다른 것을 내놓았다. 이상적이지는 않지만 나를 위해 잘 작동하고 다른 사람에게 도움이 될 수 있습니다. 나는 다른 누군가가 더 좋고 덜 험난한 것을 함께 제공하기를 바라 면서이 대답을 받아들이지 않을 것입니다 (그리고 RecyclerView구현을 오해 하고 이것을하는 간단한 방법을 놓칠 가능성이 있지만 그동안 이것으로 충분합니다. 정부 업무를 위해!)
구현의 기본은 다음과 같다 : a의 스크롤 RecyclerView사이 분할 최대의 일종이다 RecyclerView와 LinearLayoutManager. 처리해야 할 두 가지 경우가 있습니다.
- 사용자가보기를 튕 깁니다. 기본 동작은
RecyclerView플링을 내부로 전달한Scroller다음 스크롤링 마법을 수행하는 것입니다. 이는RecyclerView일반적으로 스냅되지 않은 위치에 정착 하기 때문에 문제가 됩니다.RecyclerViewfling()구현 을 재정 의하여이 문제를 해결 하고 튕기는 대신 부드럽게 스크롤하여LinearLayoutManager위치로 이동합니다. - 사용자가 스크롤을 시작하기에 불충분 한 속도로 손가락을 뗍니다. 이 경우 플링이 발생하지 않습니다. 뷰가 스냅 된 위치에 있지 않은 경우이 경우를 감지하고 싶습니다.
onTouchEvent메서드 를 재정 의하여이를 수행합니다 .
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 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();
}
그리고 다음은 부드러운 스크롤 로 결과를 가져 LayoutManager오는를 하위 클래스 로 만드는 예제입니다 .LinearLayoutManagerLayoutManager
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;
}
}
나는 이것을 할 더 깨끗한 방법을 찾았습니다. @Catherine (OP)이 개선 될 수 있는지 또는 귀하의 것보다 개선되었다고 생각하는지 알려주십시오. :)
여기 내가 사용하는 스크롤 리스너가 있습니다.
https://github.com/humblerookie/centerlockrecyclerview/
예를 들어 여기에서 몇 가지 사소한 가정을 생략했습니다.
1) 초기 및 최종 패딩 제 1 및 수평 스크롤을 필요로하는 마지막 항목은에 첫 번째와 마지막 respectively.For 예를 스크롤 할 때 초기 및 최종 견해 중심에 있음을 각각도록 설정 초기 및 최종 패딩해야합니다 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);
}
}
논리는 매우 일반적이며 다른 많은 경우에 사용할 수 있습니다. 제 경우에는 리사이클 러 뷰가 수평이고 여백 (기본적으로 recyclerview의 중심 X 좌표는 화면의 중심) 또는 고르지 않은 패딩없이 전체 가로 너비를 늘립니다.
누군가가 문제에 직면하고 있다면 친절하게 의견을 말하십시오.
나는 또한 멋진 리사이클 러 뷰가 필요했습니다. 리사이클 러 뷰 항목이 열 왼쪽에 스냅되도록하고 싶습니다. 리사이클 러 뷰에서 설정 한 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);
}
}
}
}
스냅 계산 :
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;
}
첫 번째 표시되는보기가 화면에서 절반 너비 이상 스크롤되면 다음 표시되는 열이 왼쪽으로 스냅됩니다.
리스너 설정 :
mRecyclerView.addOnScrollListener(new SnapScrollListener());
다음은 플링 이벤트에서 특정 위치로 부드럽게 스크롤 할 수있는 간단한 방법입니다.
@Override
public boolean fling(int velocityX, int velocityY) {
smoothScrollToPosition(position);
return super.fling(0, 0);
}
smoothScrollToPosition (int position)에 대한 호출로 fling 메서드를 재정의합니다. 여기서 "int position"은 어댑터에서 원하는 뷰의 위치입니다. 당신은 어떻게 든 위치의 가치를 얻어야 할 것이지만 그것은 당신의 필요와 구현에 달려 있습니다.
RecyclerView를 조금 엉망으로 만든 후, 이것이 지금까지 생각해 낸 것이고 지금 사용하고있는 것입니다. 하나의 사소한 결함이 있지만 아마 눈치 채지 못할 것이므로 (아직) 콩을 흘리지 않을 것입니다.
https://gist.github.com/lauw/fc84f7d04f8c54e56d56
수평 recyclerview 만 지원하고 중앙에 스냅하며 중앙에서 얼마나 멀리 떨어져 있는지에 따라 뷰를 축소 할 수도 있습니다. RecyclerView의 대체물로 사용하세요.
편집 : 08/2016 저장소로 만들었습니다 :
https://github.com/lauw/Android-SnappingRecyclerView
더 나은 구현을 위해 작업하는 동안이 상태를 유지하겠습니다.
스냅-투-포지션 동작을 달성하기위한 매우 간단한 접근 방식-
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;
}
}
});
유일한 작은 단점은 부분적으로 보이는 셀의 절반 이하로 스크롤 할 때 뒤로 스냅되지 않는다는 것입니다. 그러나 이것이 당신을 괴롭히지 않으면 깨끗하고 간단한 솔루션입니다.
Snap Scroll은 이제 지원 라이브러리 24.2.0의 일부입니다.
https://developer.android.com/topic/libraries/support-library/revisions.html
관련 클래스 :
https://developer.android.com/reference/android/support/v7/widget/SnapHelper.html
https://developer.android.com/reference/android/support/v7/widget/LinearSnapHelper.html
다음은 좋은 예입니다.
https://rubensousa.github.io/2016/08/recyclerviewsnap
시작, 상단, 종료 또는 하단에 스냅 지원이 필요한 경우 GravitySnapHelper ( https://github.com/rubensousa/RecyclerViewSnap/blob/master/app/src/main/java/com/github/rubensousa/recyclerviewsnap/GravitySnapHelper)를 사용 하세요. .java ).
스냅 중심 :
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
GravitySnapHelper로 스냅 시작 :
startRecyclerView.setLayoutManager(new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false));
SnapHelper snapHelperStart = new GravitySnapHelper(Gravity.START);
snapHelperStart.attachToRecyclerView(startRecyclerView);
GravitySnapHelper로 상단 스냅 :
topRecyclerView.setLayoutManager(new LinearLayoutManager(this));
SnapHelper snapHelperTop = new GravitySnapHelper(Gravity.TOP);
snapHelperTop.attachToRecyclerView(topRecyclerView);
저는 RecyclerView의 수평 방향에 대한 작업 솔루션을 구현했습니다. 이는 첫 번째 MOVE 및 UP에서 좌표 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;
}}
나를 위해 잘 작동합니다. 문제가 있으면 알려주십시오.
겸손한 사람의 답변을 업데이트하려면 :
이 스크롤 리스너는 실제로 https://github.com/humblerookie/centerlockrecyclerview/ 중앙 잠금에 효과적입니다.
그러나 다음은 recyclerview의 시작과 끝에 패딩을 추가하여 요소를 중앙에 배치하는 더 간단한 방법입니다.
mRecycler.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int childWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CHILD_WIDTH_IN_DP, getResources().getDisplayMetrics());
int offset = (mRecycler.getWidth() - childWidth) / 2;
mRecycler.setPadding(offset, mRecycler.getPaddingTop(), offset, mRecycler.getPaddingBottom());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mRecycler.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
mRecycler.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
또 다른 깨끗한 옵션은 custom을 사용하는 것입니다. https://github.com/apptik/multiview/tree/master/layoutmanagersLayoutManager 를 확인할 수 있습니다.
개발 중이지만 꽤 잘 작동합니다. 스냅 샷 사용 가능 : https://oss.sonatype.org/content/repositories/snapshots/io/apptik/multiview/layoutmanagers/
예:
recyclerView.setLayoutManager(new SnapperLinearLayoutManager(getActivity()));
위의 모든 답변과 약간 다른 것이 필요했습니다.
주요 요구 사항은 다음과 같습니다.
- 사용자가 던지거나 손가락을 뗄 때도 똑같이 작동합니다.
- 기본 스크롤 메커니즘을 사용하여 일반
RecyclerView. - 중지되면 가장 가까운 스냅 포인트로 부드럽게 스크롤하기 시작합니다.
- 사용자 정의
LayoutManager또는RecyclerView. 그냥RecyclerView.OnScrollListener다음에 의해 연결되어있는recyclerView.addOnScrollListener(snapScrollListener). 이렇게하면 코드가 훨씬 깔끔해집니다.
그리고 귀하의 경우에 맞게 아래 예에서 쉽게 변경할 수있는 두 가지 매우 구체적인 요구 사항 :
- 수평으로 작동합니다.
- 항목의 왼쪽 가장자리를의 특정 지점에 스냅합니다
RecyclerView.
이 솔루션은 기본 LinearSmoothScroller. 차이점은 최종 단계에서 "대상 뷰"가 발견되면 오프셋 계산이 변경되어 특정 위치에 스냅된다는 것입니다.
public class SnapScrollListener extends RecyclerView.OnScrollListener {
private static final float MILLIS_PER_PIXEL = 200f;
/** The x coordinate of recycler view to which the items should be scrolled */
private final int snapX;
int prevState = RecyclerView.SCROLL_STATE_IDLE;
int currentState = RecyclerView.SCROLL_STATE_IDLE;
public SnapScrollListener(int snapX) {
this.snapX = snapX;
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
currentState = newState;
if(prevState != RecyclerView.SCROLL_STATE_IDLE && currentState == RecyclerView.SCROLL_STATE_IDLE ){
performSnap(recyclerView);
}
prevState = currentState;
}
private void performSnap(RecyclerView recyclerView) {
for( int i = 0 ;i < recyclerView.getChildCount() ; i ++ ){
View child = recyclerView.getChildAt(i);
final int left = child.getLeft();
int right = child.getRight();
int halfWidth = (right - left) / 2;
if (left == snapX) return;
if (left - halfWidth <= snapX && left + halfWidth >= snapX) { //check if child is over the snapX position
int adapterPosition = recyclerView.getChildAdapterPosition(child);
int dx = snapX - left;
smoothScrollToPositionWithOffset(recyclerView, adapterPosition, dx);
return;
}
}
}
private void smoothScrollToPositionWithOffset(RecyclerView recyclerView, int adapterPosition, final int dx) {
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if( layoutManager instanceof LinearLayoutManager) {
LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return ((LinearLayoutManager) layoutManager).computeScrollVectorForPosition(targetPosition);
}
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
final int distance = (int) Math.sqrt(dx * dx + dy * dy);
final int time = calculateTimeForDeceleration(distance);
if (time > 0) {
action.update(-dx, -dy, time, mDecelerateInterpolator);
}
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLIS_PER_PIXEL / displayMetrics.densityDpi;
}
};
scroller.setTargetPosition(adapterPosition);
layoutManager.startSmoothScroll(scroller);
}
}
참고 URL : https://stackoverflow.com/questions/26370289/snappy-scrolling-in-recyclerview
'Program Club' 카테고리의 다른 글
| 사용자 'root'@ 'localhost'에 대한 액세스가 거부되었습니다 (비밀번호 사용 : YES)-권한이 없습니까? (0) | 2020.11.06 |
|---|---|
| UITextView에서 프로그래밍 방식으로 iOS8 Quicktype 키보드 비활성화 (0) | 2020.11.06 |
| 왜 std :: cout을 작성해야하고 std :: << (0) | 2020.11.05 |
| 목록 이해력 및 생성기 표현식에서 산출 (0) | 2020.11.05 |
| R에서 수정시 복사 의미는 정확히 무엇이며 표준 소스는 어디에 있습니까? (0) | 2020.11.05 |