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的滑动冲突的主要内容,如果未能解决你的问题,请参考以下文章