Android问题集(3):SwipeRefreshLayout与ListView的滑动冲突

问题

前两天刚写了一篇关于SwipeRefreshLayout的文章,讲的是在它的内部不是ListView,而是还有一个父布局,如FrameLayout时,会出现进度圈不显示的问题。今天的问题也跟那个bug类似,如果SwipeRefreshLayout的内部是ListView,没有这个bug,如果内部为FrameLayout(其它诸如RelativeLayout等也一样),在FrameLayout的内部才是ListView,那么会造成SwipeRefreshLayout和ListView的滑动冲突,先看下症状,如下图:
这里写图片描述

问题代码如下

由于我的代码是写进自己的demo集中的,有一定的抽取和优化,自己又懒得再摘出来,所以有些代码会看着很奇怪,我都加了注释,如果哪里看得很奇怪,兄弟们可以留言给我。

public class SwipeRefreshLayoutBugActivity extends BaseActivity {

    SwipeRefreshLayout mSwipeRefreshLayout;
    ListView mListView;
    BaseAdapter mBaseAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl_bug_1);
        mListView = (ListView) findViewById(R.id.lv_bug_swipe_refresh_layout);

        //这里的BaseAdapter不是android自身提供的类,而是我自己封装的一个适配器基类
        mBaseAdapter = new BaseAdapter<String>(this) {
            @Override
            public ViewHolder getHolder() {
                return new TextViewHolder(SwipeRefreshLayoutBugActivity.this);
            }
        };
        mListView.setAdapter(mBaseAdapter);

        loadData();

    }

    private void loadData(){

        //OpenAPI是我封装的一个类,专门用来获取数据
        OpenAPI.getVirtualData(10, new OpenAPI.AbstractCallback<String>(){

            @Override
            public void onSuccess(List list) {
                mBaseAdapter.setData(list);
            }
        });
    }

    @Override
    public int getLayoutResource() {
        return R.layout.activity_bug_swipe_refresh_layout;
    }

    @Override
    public String getTitleContent() {
        return "SwipeRefreshLayout与ListView的滑动冲突";
    }
}
    
    
  • 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
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/title"/>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl_bug_1"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <ListView
                android:id="@+id/lv_bug_swipe_refresh_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </FrameLayout>

    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

问题解决:方法1

对于可能出现的这种滑动冲突,SwipeRefreshLayout源码的编写者应该早有预料,所以特意在源码中流出了一个回调,OnChildScrollUpCallback,只要为SwipeRefreshLayout设置这个监听,并书写一定逻辑,就可以解决这个问题,代码如下

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl_bug_1);
        mListView = (ListView) findViewById(R.id.lv_bug_swipe_refresh_layout);
        mSwipeRefreshLayout.setOnChildScrollUpCallback(new SwipeRefreshLayout.OnChildScrollUpCallback() {
            @Override
            public boolean canChildScrollUp(SwipeRefreshLayout parent, @Nullable View child) {

                return canScrollVertically(child);
            }

            private boolean canScrollVertically(View child) {
                View childView;
                //这里的判断需要参考layout布局是怎么写的,因为上面的layout中,SwipeRefreshLayout中是FrameLayout,所以下面才会判断它是不是ViewGroup,如果你的布局在SwipeRefreshLayout中嵌套了几层之后才是ListView的话,下面的if语句需要相应更改
                if (child instanceof ViewGroup) {
                    ViewGroup viewGroup = (ViewGroup) child;
                    childView = viewGroup.getChildAt(0);
                } else {
                    //如果子View是不可滑动的控件,如TextView等,通过判断在getScrollY()方法的返回值判断
                    return child.getScrollY() > 0;
                }
                if (childView instanceof AbsListView) {
                    AbsListView view = (AbsListView) childView;
                    int count = view.getChildCount();
                    int firstVisiblePosition = view.getFirstVisiblePosition();

                    return count > 0 && (firstVisiblePosition > 0 || view.getChildAt(0).getTop() > view.getPaddingTop());
                }
                return false;
            }
        });
        …
}
    
    
  • 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
  • 33
  • 34

这里有一篇文章,是重写了SwipeRefreshLayout的一个方法,上面的具体代码,参考的就是里面的代码。

问题解决:方法2

解决方法1是从SwipeRefreshLayout的角度来解决这个问题,我们还可以从ListView的角度来解决。可以为ListView设置一个滑动监听,监听类如下:

public class SwipeListViewOnScrollListener implements AbsListView.OnScrollListener {

    private SwipeRefreshLayout mSwipeView;

    public SwipeListViewOnScrollListener(SwipeRefreshLayout swipeView) {
        mSwipeView = swipeView;
    }
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        View firstView = view.getChildAt(firstVisibleItem);

        // 当firstVisibleItem是第0位。如果firstView==null说明列表为空,需要刷新;或者top==0说明已经到达列表顶部, 也需要刷新
        if (firstVisibleItem == 0 && (firstView == null || firstView.getTop() == view.getPaddingTop())) {
            mSwipeView.setEnabled(true);
        } else {
            //不加这个if判断会引起其它问题,大家自行尝试
            if(mSwipeView.isRefreshing()){
                mSwipeView.setRefreshing(false);
            }
            mSwipeView.setEnabled(false);
        }
    }
}
    
    
  • 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

这个类是我从网上找到,然后自己加了一个if判断,由于时间太长,忘了是从哪里看到的,所以就不备注来源了。

在activity中设置该监听对象即可,如下:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl_bug_1);
        mListView = (ListView) findViewById(R.id.lv_bug_swipe_refresh_layout);
        mListView.setOnScrollListener(new SwipeListViewOnScrollListener(mSwipeRefreshLayout));
        …
}
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

成功了,最后我们来张正常的运行图
这里写图片描述

总结

2种方法,使用的时候全凭个人喜好。在解决方法2的实现过程中,我还发现了另一个问题,如果监听类的onScroll()方法中,如果没有加如下代码

if(mSwipeView.isRefreshing()){
    mSwipeView.setRefreshing(false);
}
    
    
  • 1
  • 2
  • 3

也就是,没有关闭进度圈,那么再次拉动时,进度圈不会出现。这个问题需要研究一下SwipeRefreshLayout的源码,看一下setEnabled()方法到底做了什么,留着以后再来看吧。

转自:https://blog.csdn.net/u013673799/article/details/71190065

问题

前两天刚写了一篇关于SwipeRefreshLayout的文章,讲的是在它的内部不是ListView,而是还有一个父布局,如FrameLayout时,会出现进度圈不显示的问题。今天的问题也跟那个bug类似,如果SwipeRefreshLayout的内部是ListView,没有这个bug,如果内部为FrameLayout(其它诸如RelativeLayout等也一样),在FrameLayout的内部才是ListView,那么会造成SwipeRefreshLayout和ListView的滑动冲突,先看下症状,如下图:
这里写图片描述

问题代码如下

由于我的代码是写进自己的demo集中的,有一定的抽取和优化,自己又懒得再摘出来,所以有些代码会看着很奇怪,我都加了注释,如果哪里看得很奇怪,兄弟们可以留言给我。

public class SwipeRefreshLayoutBugActivity extends BaseActivity {

    SwipeRefreshLayout mSwipeRefreshLayout;
    ListView mListView;
    BaseAdapter mBaseAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl_bug_1);
        mListView = (ListView) findViewById(R.id.lv_bug_swipe_refresh_layout);

        //这里的BaseAdapter不是android自身提供的类,而是我自己封装的一个适配器基类
        mBaseAdapter = new BaseAdapter<String>(this) {
            @Override
            public ViewHolder getHolder() {
                return new TextViewHolder(SwipeRefreshLayoutBugActivity.this);
            }
        };
        mListView.setAdapter(mBaseAdapter);

        loadData();

    }

    private void loadData(){

        //OpenAPI是我封装的一个类,专门用来获取数据
        OpenAPI.getVirtualData(10, new OpenAPI.AbstractCallback<String>(){

            @Override
            public void onSuccess(List list) {
                mBaseAdapter.setData(list);
            }
        });
    }

    @Override
    public int getLayoutResource() {
        return R.layout.activity_bug_swipe_refresh_layout;
    }

    @Override
    public String getTitleContent() {
        return "SwipeRefreshLayout与ListView的滑动冲突";
    }
}
  
  
  • 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
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/title"/>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl_bug_1"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <ListView
                android:id="@+id/lv_bug_swipe_refresh_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </FrameLayout>

    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

问题解决:方法1

对于可能出现的这种滑动冲突,SwipeRefreshLayout源码的编写者应该早有预料,所以特意在源码中流出了一个回调,OnChildScrollUpCallback,只要为SwipeRefreshLayout设置这个监听,并书写一定逻辑,就可以解决这个问题,代码如下

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl_bug_1);
        mListView = (ListView) findViewById(R.id.lv_bug_swipe_refresh_layout);
        mSwipeRefreshLayout.setOnChildScrollUpCallback(new SwipeRefreshLayout.OnChildScrollUpCallback() {
            @Override
            public boolean canChildScrollUp(SwipeRefreshLayout parent, @Nullable View child) {

                return canScrollVertically(child);
            }

            private boolean canScrollVertically(View child) {
                View childView;
                //这里的判断需要参考layout布局是怎么写的,因为上面的layout中,SwipeRefreshLayout中是FrameLayout,所以下面才会判断它是不是ViewGroup,如果你的布局在SwipeRefreshLayout中嵌套了几层之后才是ListView的话,下面的if语句需要相应更改
                if (child instanceof ViewGroup) {
                    ViewGroup viewGroup = (ViewGroup) child;
                    childView = viewGroup.getChildAt(0);
                } else {
                    //如果子View是不可滑动的控件,如TextView等,通过判断在getScrollY()方法的返回值判断
                    return child.getScrollY() > 0;
                }
                if (childView instanceof AbsListView) {
                    AbsListView view = (AbsListView) childView;
                    int count = view.getChildCount();
                    int firstVisiblePosition = view.getFirstVisiblePosition();

                    return count > 0 && (firstVisiblePosition > 0 || view.getChildAt(0).getTop() > view.getPaddingTop());
                }
                return false;
            }
        });
        …
}
  
  
  • 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
  • 33
  • 34

这里有一篇文章,是重写了SwipeRefreshLayout的一个方法,上面的具体代码,参考的就是里面的代码。

问题解决:方法2

解决方法1是从SwipeRefreshLayout的角度来解决这个问题,我们还可以从ListView的角度来解决。可以为ListView设置一个滑动监听,监听类如下:

public class SwipeListViewOnScrollListener implements AbsListView.OnScrollListener {

    private SwipeRefreshLayout mSwipeView;

    public SwipeListViewOnScrollListener(SwipeRefreshLayout swipeView) {
        mSwipeView = swipeView;
    }
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        View firstView = view.getChildAt(firstVisibleItem);

        // 当firstVisibleItem是第0位。如果firstView==null说明列表为空,需要刷新;或者top==0说明已经到达列表顶部, 也需要刷新
        if (firstVisibleItem == 0 && (firstView == null || firstView.getTop() == view.getPaddingTop())) {
            mSwipeView.setEnabled(true);
        } else {
            //不加这个if判断会引起其它问题,大家自行尝试
            if(mSwipeView.isRefreshing()){
                mSwipeView.setRefreshing(false);
            }
            mSwipeView.setEnabled(false);
        }
    }
}
  
  
  • 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

这个类是我从网上找到,然后自己加了一个if判断,由于时间太长,忘了是从哪里看到的,所以就不备注来源了。

在activity中设置该监听对象即可,如下:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl_bug_1);
        mListView = (ListView) findViewById(R.id.lv_bug_swipe_refresh_layout);
        mListView.setOnScrollListener(new SwipeListViewOnScrollListener(mSwipeRefreshLayout));
        …
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

成功了,最后我们来张正常的运行图
这里写图片描述

总结

2种方法,使用的时候全凭个人喜好。在解决方法2的实现过程中,我还发现了另一个问题,如果监听类的onScroll()方法中,如果没有加如下代码

if(mSwipeView.isRefreshing()){
    mSwipeView.setRefreshing(false);
}
  
  
  • 1
  • 2
  • 3

也就是,没有关闭进度圈,那么再次拉动时,进度圈不会出现。这个问题需要研究一下SwipeRefreshLayout的源码,看一下setEnabled()方法到底做了什么,留着以后再来看吧。

猜你喜欢

转载自blog.csdn.net/u013651026/article/details/80447467