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

Posted 王梵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android问题集: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的滑动冲突";
    
<?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

对于可能出现的这种滑动冲突,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;
            
        );
        …

这里有一篇文章,是重写了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);
        
    

这个类是我从网上找到,然后自己加了一个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));
        …

成功了,最后我们来张正常的运行图

总结

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

if(mSwipeView.isRefreshing())
    mSwipeView.setRefreshing(false);

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

以上是关于Android问题集:SwipeRefreshLayout与ListView的滑动冲突的主要内容,如果未能解决你的问题,请参考以下文章

我将如何下载 *** android 数据集?

android问题笔记集

结果集到 Android 光标

加载程序集 Xamarin.Android.Support.v4 时出现异常

android中的图例和字段集

Android MediaCodec 的程序集容器