I am trying to achieve horizontal scroll mechanism on AndroidTV like Netflix I tried to use SnapHelper without success and also override LinearLayoutManager smoothScroll it looks nice but is not perfect and also bit lag. When I am perform long press, the movement doesn't smoothie and sometimes its stuck. I am receiving focus callback from the next item while I am tapping left or right on the controller and then using recyclerview.smoothScroolToPostion. anyone can help me with that?
recyclerView.setAdapter(new MyAdapter(new FocusListener() {
@Override
public void dispatchFocusChanged(View view, boolean gainFocus) {
if(gainFocus) {
int position = recyclerView.getChildAdapterPosition(view);
recyclerView.smoothScrollToPosition(position);
}
}
}));
LinearLayoutManager
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
LinearSmoothScroller smoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
//This returns the milliseconds it takes to
//scroll one pixel.
@Override
protected float calculateSpeedPerPixel
(DisplayMetrics displayMetrics) {
return 500f/displayMetrics.densityDpi;
}
@Override
protected int getHorizontalSnapPreference() {
return SNAP_TO_END;
}
@Override
protected int calculateTimeForDeceleration(int dx) {
return (int) Math.ceil(calculateTimeForScrolling(dx) / .3356);
}
@Override
protected int calculateTimeForScrolling(int dx) {
return 200;//super.calculateTimeForScrolling(dx);
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
SnapHelper
public class Snap extends LinearSnapHelper {
private Context context;
private OrientationHelper orientationHelper;
private Scroller scroller;
private int maxScrollDistance = 0;
private final static int MAX_SCROLL_ON_FLING_DURATION_MS = 1000;
@Override
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
if (recyclerView != null) {
context = recyclerView.getContext();
scroller = new Scroller(context, new DecelerateInterpolator());
} else {
scroller = null;
context = null;
}
super.attachToRecyclerView(recyclerView);
}
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
return findFirstView(layoutManager, getHelper(layoutManager));
}
public View findFirstView(RecyclerView.LayoutManager layoutManager, OrientationHelper orientationHelper) {
if (layoutManager == null) return null;
int childCount = layoutManager.getChildCount();
if (childCount == 0) return null;
int absClosest = Integer.MAX_VALUE;
View closestView = null;
int start = orientationHelper.getStartAfterPadding();
for (int i = 0; i < childCount; i++) {
View child = layoutManager.getChildAt(i);
int childStart = orientationHelper.getDecoratedStart(child);
int absDistanceToStart = Math.abs(childStart - start);
if (absDistanceToStart < absClosest) {
absClosest = absDistanceToStart;
closestView = child;
}
}
return closestView;
}
private OrientationHelper getHelper(RecyclerView.LayoutManager layoutManager) {
if (orientationHelper == null) {
orientationHelper = OrientationHelper.createHorizontalHelper(layoutManager);
}
return orientationHelper;
}
@Override
public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) {
int[] out = new int[2];
out[0] = distanceToStart(targetView, getHelper(layoutManager));
return out;
}
@Override
public int[] calculateScrollDistance(int velocityX, int velocityY) {
int[] out = new int[2];
OrientationHelper helper = orientationHelper;
if (null == helper)
return out;
if (maxScrollDistance == 0) {
maxScrollDistance = (helper.getEndAfterPadding() - helper.getStartAfterPadding()) / 2;
}
scroller.fling(0, 0, velocityX, velocityY, -maxScrollDistance, maxScrollDistance, 0, 0);
out[0] = scroller.getFinalX();
out[1] = scroller.getFinalY();
return out;
}
private int distanceToStart(View targetView, OrientationHelper helper) {
int childStart = helper.getDecoratedStart(targetView) + convertDpToPixel(40);
int containerStart = helper.getStartAfterPadding();
return 0;//childStart - containerStart;
}
public int convertDpToPixel(float dp) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return (int) Math.round(px);
}
@Nullable
@Override
protected RecyclerView.SmoothScroller createScroller(final RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)
return super.createScroller(layoutManager);
Context context = this.context;
return new LinearSmoothScroller(context) {
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, targetView);
int dx = snapDistance[0];
int dy = snapDistance[1];
int dt = calculateTimeForDeceleration(Math.abs(dx));
int time = Math.max(1, Math.min(MAX_SCROLL_ON_FLING_DURATION_MS, dt));
action.update(dx, dy, time, mDecelerateInterpolator);
}
};
}}