文章

SwipeRefreshLayout

SwipeRefreshLayout

SwipeRefreshLayout

SwipeRefreshLayout 案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 通过 setEnabled(false) 禁用下拉刷新
// swipeRefreshLayout.setEnabled(false);

// 设置手指在屏幕下拉多少距离会触发下拉刷新
swipeRefreshLayout.setDistanceToTriggerSync(300);

// 设置下拉出现小圆圈是否是缩放出现,出现的位置,最大的下拉位置
swipeRefreshLayout.setProgressViewOffset(true, 50, 200);

// 设置下拉圆圈的大小,两个值 LARGE, DEFAULT
swipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);

// 设定下拉圆圈的背景
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
        android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light);

// 设置手势下拉刷新的监听
swipeRefreshLayout.setOnRefreshListener(new OnRefreshListener() {
    // 刷新动画开始后回调到此方法
    @Override
    public void onRefresh() {
        LogUtil.i("onRefresh...");
        new Handler().postDelayed(new Runnable() {
            public void run() {
                data.add(0, "hacket add item_" + new Random(100).nextInt());
                adapter.notifyDataSetChanged();

                swipeRefreshLayout.setRefreshing(false);
            }
        }, 5000);
    }
});

WebView 下拉刷新与 SwipeRefreshLayout 事件冲突解决

问题

会发现网页无法上拉,往上滑动就会触发下拉刷新控件的 refresh 事件

事件冲突解决

关于两个控件的冲突问题,网络上有不少解决办法,有的是自定义 SwipeRefreshLayout 重写 onTouchEvent 方法;有的是重写 WebView 的 scroll 监听。

现在我们通过 SwipeRefreshLayout 的 setOnChildScrollUpCallback 来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
swipeRefreshLayout.setOnChildScrollUpCallback(new SwipeRefreshLayout.OnChildScrollUpCallback() {

    @Override

    public boolean canChildScrollUp(@NonNull SwipeRefreshLayout parent, @Nullable View child) {

        if (mWebView != null) {

            return mWebView.getScrollY() > 0;

        }

        return true;

    }

});

说明:canChildScrollUp 方法返回的 true/false 表示子视图是否可返回顶部,我们改成 webView.getScrollY() > 0 后表示 webView 到顶部时返回 false,refreshLayout 可接收到下拉动作,触发 refresh 事件。

此时上拉、下拉都没问题了。

OnChildScrollUpCallback 接口就一个 canChildScrollUp 方法,返回是否可滚动。

在上述实现过程中,我们判断 WebView 的状态:

  1. 在顶部时,返回 false,表示子视图不可滚动,refreshLayout 接收到滑动事件,引出滑动视图和调用滑动刷新方法;
  2. 不在顶部时,webView.getScrollY() > 0,返回 true,表示子视图可滚动,refreshLayout 中 canChildScrollUp() 返回 true,刷新控件不再处理滑动问题,所以没有调用滑动刷新方法。

SwipeRefreshLayout 和 RecyclerView 的冲突解决

当你是自定义 item 布局时,必须要重写 OnChildScrollUpCallback() 方法–原文说明:Override this if the child view is a custom view.

在做项目的时候,大部分的机型是没有什么问题的,但是在一款华为的手机上,就发现了一个问题:滑动冲突了。在往下滑的时候,RecyclerView 还没有到达顶部就触发了下拉刷新的动作。这里产生的具体原因未知,得去看源码才知道。

解决方法是,在 swipeRefreshLayout 类的内部,实现了 OnChildScrollUpCallback() 方法,这个方法返回值指示 swipeRefreshLayout 内部的 view 有没有可能向上滚动,返回 true 表示可以滚动,返回 false 表示不可滚动,触发刷新。如果里面的控件是自定义的,就需要重写这个方法。API 提供了 setOnChildScrollUpCallback() 方法给我们重写

1
2
3
4
5
6
7
8
9
10
swipeRefreshLayout.setOnChildScrollUpCallback(new SwipeRefreshLayout.OnChildScrollUpCallback() {
        @Override
        public boolean canChildScrollUp(SwipeRefreshLayout parent, @Nullable View child) {
            if (mRecyclerView == null) {
                return false;
            }
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
            return linearLayoutManager.findFirstCompletelyVisibleItemPosition() != 0;
        }
    });

在这里我们先获得 RecyclerView 的布局管理器 LinearLayoutManager 这里使用的是线性布局,还有网格布局 GridLayoutManager 和瀑布流 StaggeredGridLayoutManager 布局管理器,可以很方便的实现绚丽的各种界面。

然后 findFirstCompletelyVisibleItemPosition() 返回的位置是可见区域第一个全部可见的 item 的位置,这里注意:是整个 item 全部出现了才算。如果返回的位置不等于 0,结果为 true,那么 swipeRefreshLayout 里面的 RecyclerView 标志为可滚动的,RecyclerView 就可以往上滚动。当到了第一个全部 item 可见的时候,返回的 position 为 0,结果 false,那么说明 RecyclerView 已经到顶了,那么 RecyclerView 就标志位不可滚动的状态,继续下拉就会触发 swipeRefreshLayout 的刷新机制了。

Ref

本文由作者按照 CC BY 4.0 进行授权