Giới thiệu
Nhiều khi tôi dùng CH Play. Nhìn danh sách các ứng dụng nhưng không biết nó là một viewpager hay là một listview hay recyclerview. Thế là tôi mày mò thử tìm hiểu xem nó sử dụng component nào để xử lý việc vuốt danh sách item đấy giống như vuốt viewpager nó là cái gì. Thì cuối cùng tôi cũng tìm hiểu ra tính năng này đã được Android cung cấp và nó có tên là SnapHelper
SnapHelper là cái gì
SnapHelper là một class hỗ trợ việc bám các View con của RecyclerView. Ví dụ như bạn có thể nhìn thấy trong ứng dụng CH Play. Danh sách các item tiếp theo sẽ luôn luôn hiển thị ở cuối danh sách.
Android đã cung cấp thư viện để xử lý như trên ảnh. Class đó tên là LinearSnapHelper
nhưng class này chỉ hỗ trợ việc snapping item ra giữa.
Để sử dụng SnapHelper bạn chỉ cần viết theo như ở bên dưới
SnapHelper snapHelper = new LinearSnapHelper(); snapHelper.attachToRecyclerView(yourRecyclerView);
Nhưng nếu làm thế này thì vẫn chưa giống với RecyclerView ở trong CH Play. Các Item Snap ở vị trí bắt đầu cơ mà?
Bây giờ chúng ta đi vào custom lại class LinearSnapHelper
giống với hình trên nhé
Đầu tiên, tôi tạo 1 class có tên là StartSnapHelper
kế thừa lại từ LinearSnapHelper
. Và class này sẽ có override
các methods bên dưới nhé :
1. calculateDistanceToFinalSnap
Override method này để snap một điểm cụ thể cho view mong muốn snap hoặc một container view nằm trên trục. Method này được gọi khi SnapHelper
can thiệp vào quá trình fling
của người dùng và nó biết chính xác khoảng cách bắt buộc để scroll đến cái view tiếp theo.
@Override public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) { int[] out = new int[2]; if (layoutManager.canScrollHorizontally()) { out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager)); } else { out[0] = 0; } if (layoutManager.canScrollVertically()) { out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager)); } else { out[1] = 0; } return out; }
2. findSnapView
Override method này để cung cấp một View
cụ thể cho snapping
. Method này được gọi khi SnapHelper đã sẵn sàng để bắt đầu snap
và yêu cầu bắt buộc một View
mục tiêu để snap. Nó sẽ được gọi là khi hành động scroll recyclerview dừng lại.Nó cũng sẽ được gọi là khi SnapHelper đang chuẩn bị snap sau khi fling
recyclerview và yêu cầu phải có một view để tham chiếu từ các child view
hiện tại của recyclerView . Nếu method này trả về null, SnapHelper sẽ không snap bất kỳ một view nào nữa cả.
@Override public View findSnapView(RecyclerView.LayoutManager layoutManager) { if (layoutManager instanceof LinearLayoutManager) { if (layoutManager.canScrollHorizontally()) { return getStartView(layoutManager, getHorizontalHelper(layoutManager)); } else { return getStartView(layoutManager, getVerticalHelper(layoutManager)); } } return super.findSnapView(layoutManager); }
Và đây là cả class StartSnapHelper
/** * Created by amitshekhar on 15/01/17. */ public class StartSnapHelper extends LinearSnapHelper { private OrientationHelper mVerticalHelper, mHorizontalHelper; public StartSnapHelper() { } @Override public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException { super.attachToRecyclerView(recyclerView); } @Override public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) { int[] out = new int[2]; if (layoutManager.canScrollHorizontally()) { out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager)); } else { out[0] = 0; } if (layoutManager.canScrollVertically()) { out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager)); } else { out[1] = 0; } return out; } @Override public View findSnapView(RecyclerView.LayoutManager layoutManager) { if (layoutManager instanceof LinearLayoutManager) { if (layoutManager.canScrollHorizontally()) { return getStartView(layoutManager, getHorizontalHelper(layoutManager)); } else { return getStartView(layoutManager, getVerticalHelper(layoutManager)); } } return super.findSnapView(layoutManager); } private int distanceToStart(View targetView, OrientationHelper helper) { return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding(); } private View getStartView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) { if (layoutManager instanceof LinearLayoutManager) { int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); boolean isLastItem = ((LinearLayoutManager) layoutManager) .findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1; if (firstChild == RecyclerView.NO_POSITION || isLastItem) { return null; } View child = layoutManager.findViewByPosition(firstChild); if (helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2 && helper.getDecoratedEnd(child) > 0) { return child; } else { if (((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) { return null; } else { return layoutManager.findViewByPosition(firstChild + 1); } } } return super.findSnapView(layoutManager); } private OrientationHelper getVerticalHelper(RecyclerView.LayoutManager layoutManager) { if (mVerticalHelper == null) { mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager); } return mVerticalHelper; } private OrientationHelper getHorizontalHelper(RecyclerView.LayoutManager layoutManager) { if (mHorizontalHelper == null) { mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); } return mHorizontalHelper; } }
Bây giờ bạn có thể sử dụng nó vào trong RecyclerView của mình rồi :
SnapHelper startSnapHelper = new StartSnapHelper(); startSnapHelper.attachToRecyclerView(yourRecyclerView);
Đây là ví dụ về SnapHelper
Cảm ơn bạn đã đọc bài viết này của tôi.
HAPPY CODING!
Nguồn dịch từ Amit Shekhar via viblo