1. 程式人生 > >使用 RecyclerView 實現 Gallery 畫廊效果,並控制 Item 停留位置

使用 RecyclerView 實現 Gallery 畫廊效果,並控制 Item 停留位置

RecyclerView 作為一個列表滑動控制元件,我們都知道它既可以橫向滑動,也可以豎直滑動,可以實現線性佈局管理,瀑布流佈局管理,還有 GridView 佈局管理。其實我們可以控制其 Item 的停留位置,並使其實現畫廊效果。如果大家熟悉 SnapHelper 的話,估計大家就都會了。

什麼是 SnapHelper

SnapHelper 的實現原理就是是監聽 RecyclerView.OnFlingListener 中的 onFling 介面。support library 中只提供了一個繼承類 LinearSnapHelper ,LinearSnapHelper 是抽象類 SnapHelper 的具體實現。
通過 LinearSnapHelper,我們就可以使 RecyclerView 實現類似 ViewPager 的功能,無論怎麼滑動最終都會停留在列表頁面正中間。

SnapHelper 和 ViewPager 的區別就是 ViewPager 一次只能滑動一頁,而 RecyclerView + SnapHelper 的方式可以實現一次滑動好幾頁。

效果如下:

居中實現方式

使用 SnapHelper 配合 RecyclerView 實現控制 Item 位置居中顯示,非常簡單,官方預設提供的 LinearSnapHelper 就是居中的,我們直接使用即可。

程式碼如下:

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false
); recyclerView.setLayoutManager(linearLayoutManager); new LinearSnapHelper().attachToRecyclerView(recyclerView);

自定義 SnapHelper

官方提供的預設是居中顯示,其實我們也可以自定義,比如:靠左顯示,讓可見的第一個 Item 居左顯示。

效果圖如下

自定義 SnapHelper ,一般需要實現兩個方法:
* int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) 當拖拽或滑動結束時會回撥該方法,返回一個out = int[2],out[0]x軸,out[1] y軸,這就是我們需要修改的位置偏移量
* View findSnapView(RecyclerView.LayoutManager layoutManager) 該方法返回上面方法中需要的 targetView 。

程式碼如下

public class CustomSnapHelper extends LinearSnapHelper {
    private OrientationHelper mHorizontalHelper;

    @Override
    public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) {
        int[] out = new int[2];
        //判斷支援水平滾動,修改水平方向的位置,是修改的out[0]的值
        if (layoutManager.canScrollHorizontally()) {
            out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager));
        } else {
            out[0] = 0;
        }
        return out;
    }

    private int distanceToStart(View targetView, OrientationHelper helper) {
        return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding();
    }

    @Override
    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
        return findStartView(layoutManager, getHorizontalHelper(layoutManager));
    }

    private View findStartView(RecyclerView.LayoutManager layoutManager,
                               OrientationHelper helper) {

        if (layoutManager instanceof LinearLayoutManager) {
            int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
            int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
            if (firstChild == RecyclerView.NO_POSITION) {
                return null;
            }
            //這行的作用是如果是最後一個,翻到最後一條,解決顯示不全的問題
            if (lastChild == layoutManager.getItemCount() - 1) {
                return layoutManager.findViewByPosition(lastChild);
            }

            View child = layoutManager.findViewByPosition(firstChild);
            //獲取偏左顯示的Item
            if (helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2
                    && helper.getDecoratedEnd(child) > 0) {
                return child;
            } else {
                return layoutManager.findViewByPosition(firstChild + 1);
            }
        }

        return super.findSnapView(layoutManager);
    }


    private OrientationHelper getHorizontalHelper(
            RecyclerView.LayoutManager layoutManager) {
        if (mHorizontalHelper == null) {
            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
        }
        return mHorizontalHelper;
    }
}

呼叫自定義的 SnapHelper 程式碼如下,配合 RecyclerView:

CustomSnapHelper mMySnapHelper = new CustomSnapHelper();
mMySnapHelper.attachToRecyclerView(rv);

最後,其實垂直方向也可以實現哦,大家可以嘗試一下垂直方向的使用方式是不是非常簡單。

歡迎大家關注我的技術分享公眾號:非著名程式設計師(smart_android)。技術文章均先首發於我的技術分享的微信公眾號。