SwipeRefreshLayout基本使用和冲突解决机制
来源:互联网 发布:二阶矩阵 编辑:程序博客网 时间:2024/06/06 04:26
一、SwipeRefreshLayout简单使用
SwipeRefreshLayout 是官方的一个可以下拉刷新的ViewGroup,其内只能填充一个子view,比如ListView、RecyclerView等。使用之前我们先来了解一下主要的方法。
(1)主要方法:
1、setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener):设置手势滑动监听器。 2、setProgressBackgroundColor(int colorRes):设置进度圈的背景色。 3、setColorSchemeResources(int… colorResIds):设置进度动画的颜色。 4、setRefreshing(Boolean refreshing):设置组件的刷洗状态。 5、setSize(int size):设置进度圈的大小,只有两个值:DEFAULT、LARGE
(2)xml布局:
注意其内只能放一个子view。
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_container" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </android.support.v4.widget.SwipeRefreshLayout></RelativeLayout>
(3)java代码:
public class MainActivity extends AppCompatActivity { TextView tv = null; SwipeRefreshLayout swipeRefreshLayout = null; ListView listView = null; String[] listData = new String[]{ "第一项","第二项","第三项","第四项","第五项","第六项","第七项","第八项","第九项","第十项","第十一项","第十二项","第十三项" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); swipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_container); listView = (ListView)findViewById(R.id.listview); listView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,listData)); //设置刷新时动画的颜色,可以设置4个 swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { // TODO Auto-generated method stub //开始刷新 new Handler().postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub //停止刷新 swipeRefreshLayout.setRefreshing(false); } }, 6000); } }); }}
(4)运行效果:
如下,listview滑动到顶部才会下拉刷新,否则由listview处理下拉事件。
二、冲突解决机制
从前面我们可以看到,似乎SwipeRefreshLayout和listview的下拉事件冲突已经解决:即如果listview在顶部,SwipeRefreshLayout处理滑动。否则,listview处理滑动。那么SwipeRefreshLayout内部是如何实现的呢?
追溯其源码,我们可以发现SwipeRefreshLayout内部包括两个控件:我们布局中定义的子view和一个mCircleView,只是这个mCircleView平时状态下mCircleView.setVisibility(View.GONE),因此不会显示。
要查看冲突解决方面的,要看onInterceptTouchEvent()方法,如下:
public boolean onInterceptTouchEvent(MotionEvent ev) { ensureTarget(); final int action = MotionEventCompat.getActionMasked(ev); if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { mReturningToStart = false; } //注意这里的canChildScrollUp()!! if (!isEnabled() || mReturningToStart || canChildScrollUp() || mRefreshing || mNestedScrollInProgress) { // Fail fast if we're not in a state where a swipe is possible return false; } switch (action) { case MotionEvent.ACTION_DOWN: setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop(), true); mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mIsBeingDragged = false; final float initialDownY = getMotionEventY(ev, mActivePointerId); if (initialDownY == -1) { return false; } mInitialDownY = initialDownY; break; case MotionEvent.ACTION_MOVE: ....... case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; break; } return mIsBeingDragged; }
我们发现一个canChildScrollUp(),这是关键所在。如果子view可以上滑,那么父view(SwipeRefreshLayout)就不处理滑动事件,自然交给子view来处理。
顺便,我们再来看一下onTouchEvent()方法,观察下mCircleView什么时候可以显示的。
@Override public boolean onTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); int pointerIndex = -1; if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { mReturningToStart = false; } if (!isEnabled() || mReturningToStart || canChildScrollUp() || mNestedScrollInProgress) { // Fail fast if we're not in a state where a swipe is possible return false; } switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mIsBeingDragged = false; break; case MotionEvent.ACTION_MOVE: { ...... if (mIsBeingDragged) { if (overscrollTop > 0) { //在这里开始显示mCircleView moveSpinner(overscrollTop); } else { return false; } } break; } case MotionEventCompat.ACTION_POINTER_DOWN: { ...... } case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; case MotionEvent.ACTION_UP: { ...... //在这里mCircleView开启动画 finishSpinner(overscrollTop); mActivePointerId = INVALID_POINTER; return false; } case MotionEvent.ACTION_CANCEL: return false; } return true; }
可以发现,滑动过程中(ACTION_MOVE),调用moveSpinner方法,该方法内部实现mCircleView的显示,如下:
private void moveSpinner(float overscrollTop) { ...... if (mCircleView.getVisibility() != View.VISIBLE) { mCircleView.setVisibility(View.VISIBLE); } ...... }
滑动结束(ACTION_UP),调用finishSpinner方法,该方法时进度圆圈开始转动。当数据加载完成,调用swipeRefreshLayout.setRefreshing(false);停止动画和进度圈显示。
此外,我们在ViewPager中嵌套SwipeRefreshLayout时常常会出现ViewPager左右滑动和SwipeRefreshLayout上下滑动的冲突(因为一次滑动中上下和左右两个方向都会产生位移),因而我们使用过程中常常重写SwipeRefreshLayout的onInterceptTouchEvent方法,对左右和上下的位移进行比较,当上下位移较大时才会处理滑动事件。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 其实是根据滑动的上下距离和左右距离作比较 解决两个方向上的滑动冲突 switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; if (xDistance > yDistance) { //不处理触摸事件 return false; } } return super.onInterceptTouchEvent(ev); }
- SwipeRefreshLayout基本使用和冲突解决机制
- Android之SwipeRefreshLayout使用和冲突解决
- 解决使用SwipeRefreshLayout ListView使用EmptyView冲突
- SwipeRefreshLayout和ConvenientBanner 事件冲突解决
- viewpager和SwipeRefreshLayout下拉刷新冲突解决
- 解决使用`SwipeRefreshLayout`下拉刷新和左右滑动事件冲突的问题
- //解决SwipeRefreshLayout和RecyclerView存在滑动冲突的问题
- Android 解决SwipeRefreshLayout与ListView和ScrollView滑动冲突
- 解决SwipeRefreshLayout和webview下拉刷新冲突问题
- 解决ScrollView和SwipeRefreshLayout滑动时的冲突
- 解决SwipeRefreshLayout和webview下拉刷新冲突问题
- SwipeRefreshLayout下拉刷新冲突解决
- SwipeRefreshLayout嵌套Listview冲突解决
- SwipeRefreshLayout下拉刷新冲突解决
- SwipeRefreshLayout+RecyclerView滑动冲突解决
- Android 解决SwipeRefreshLayout和listview的setEmptyView的冲突 listview和scrollview的冲突
- SwipeRefreshLayout基本使用
- SwipeRefreshLayout 的基本使用
- error RC2104 : undefined keyword or key name: Select 1394
- #51nod 1305 Pairwise Sum and Divide
- 修改Makefile.am,无法生效
- iOS中常见的第三方登录
- hadoop-2.7.1/tmp/dfs/namesecondary/in_use.lock (Permission denied) 错误解决
- SwipeRefreshLayout基本使用和冲突解决机制
- 一个循环队列在linux下的应用
- TortoiseSVN 修改服务器地址url
- ubuntu 左边菜单栏不见
- Codeforces Intel Code Challenge Elimination Round(Oct/01/2016)
- tortoise working copy locked 问题
- 【学习笔记javascript设计模式与开发实践(迭代器模式)----7】
- PHP notice/warning 对性能的影响
- windows server 2003 搭建环境