自定义简单实现滑动下拉刷新效果
2016-02-18 10:43
169 查看
最近在看了一遍关于触摸事件的流程,所以索性写个例子总结一下,然后就产生了这个小例子,大神请绕过~:
一个自己制作的简单的下拉刷新的小控件,主要基于LinearLayout实现的,在内部只是关联了一个listview,非常简单,下面见效果图:
主要过程在于触摸事件的把控以及scroller的运用,还是比较简单的,首先看看布局:
上面主要使用了marginTop=负数来进行隐藏刷新头部的作用。
看自定义LinearLayout的代码:
首先我们将ThisView继承自LinearLayout,重写他的构造函数,并且在构造函数中出事后一些参数:
mTouchSlop:主要运用于判断是否滑动的最小距离。
maxSmoothDistance:主要用来定义用户下拉的最大距离,我们需要把最大距离即MAX_DISTANCE转换成像素使用,写了一个基本的dp和px的转换类DensityUtil。
mScroller:主要运用于各种情况下的下拉刷新的自主滑动操作。
重写onLayout函数进行ThisView中子View的获取:
这里说明一下为什么在onLayout中获取子View,而不是其他方法中,我们知道View的绘制流程是通过onMeasure->onLayout->onDraw进行依次调用的,onLayout用来进行子View布局的确定,从而确定父容器的本身布局,所以我们在onLayout完成后在拿到子View对象的引用是最保险的。
其次,很多时候我们会遇到要使用getWidth()或者getHeight()进行空间或者布局的宽高获取,一般而言,我们极大可能会遇到获取的宽高=0的情况,可能的原因就是布局还没有初始化完成的时候我们却想要获取宽高,所以导致的这个问题,所以我们在layout完成后进行布局高宽的获取,即:
refreshTextView.getHeight();
接下来就是重头戏了
触摸事件的判断:
先看我们的onInterceptTouchEvent():
关于触摸事件的原理,在这里就不过多的去详述了,不理解的请自行查阅。
看上面的代码,我们定义了几个变量来记录触摸时候的坐标:
我们在ACTION_DOWN的时候返回了false,让父容器下发事件,Listview能够接受到事件进行滑动
当我们在ACTION_MOVE的时候,我们要进行判断是父容器拦截事件还是向下继续传递事件,我们常规来想就是当listview到达头部的时候,我们进行控件的下拉操作,即:
且,我们是需要进行下拉操作,并且父容器现在并没有进行刷新操作:
ACTION_UP的时候我们还是不拦截事件。
当父容器拦截了事件后,就会执行onTouchEvent:
请注意看ACTION_DOWN的注释,因为我们父容器并没有拦截此事件,所以我们在onTouchEvent中是不会执行ACTION_DOWN操作的。
ACTION_DOWN中我们要判断是否是竖直方向的下拉,如果是,则进行scrollTo操作:
在ACTION_UP的时候我们要进行scroller的使用,判断是否完成刷新,或者达不到刷新的要求就自动滑动回原来的位置,smoothScrollTo方法:
主要代码展示如上所示,如果想要这个demo的同学,可以去我的gitHub中查看下载:
https://github.com/JerryChan123/android-learning
一个自己制作的简单的下拉刷新的小控件,主要基于LinearLayout实现的,在内部只是关联了一个listview,非常简单,下面见效果图:
主要过程在于触摸事件的把控以及scroller的运用,还是比较简单的,首先看看布局:
<com.my.ownpulltorefreshdemo.ThisView android:layout_width="match_parent" android:orientation="vertical" android:id="@+id/father" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="80dp" android:layout_above="@+id/list" android:text="anny_lin的刷新小demo" android:layout_gravity="center" android:gravity="center" android:textColor="#ffffff" //这句话是将刷新头部隐藏在上方的关键 android:layout_marginTop="-80dp" android:textSize="20sp" android:background="#5537c1"/> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/list" ></ListView> </com.my.ownpulltorefreshdemo.ThisView>
上面主要使用了marginTop=负数来进行隐藏刷新头部的作用。
看自定义LinearLayout的代码:
首先我们将ThisView继承自LinearLayout,重写他的构造函数,并且在构造函数中出事后一些参数:
private void init(Context context) { //滑动的最小距离 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mScroller=new Scroller(context); maxSmoothDistance= DensityUtil.dip2px(context,MAX_DISTANCE); Log.e("maxSmoothDistance==",maxSmoothDistance+""); }
mTouchSlop:主要运用于判断是否滑动的最小距离。
maxSmoothDistance:主要用来定义用户下拉的最大距离,我们需要把最大距离即MAX_DISTANCE转换成像素使用,写了一个基本的dp和px的转换类DensityUtil。
mScroller:主要运用于各种情况下的下拉刷新的自主滑动操作。
重写onLayout函数进行ThisView中子View的获取:
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); int count = this.getChildCount(); for (int i = 0; i < count; i++) { if (getChildAt(i) instanceof ListView) { listView = (ListView) getChildAt(i); } if (getChildAt(i)instanceof TextView) { refreshTextView= (TextView) getChildAt(i); putRefreshHeight=refreshTextView.getHeight(); Log.e("putRefreshHeight","==="+putRefreshHeight); } } if (listView == null) { try { throw new Exception("please checked out the Linearayout surely has a Listvew!"); } catch (Exception e) { e.printStackTrace(); } } }
这里说明一下为什么在onLayout中获取子View,而不是其他方法中,我们知道View的绘制流程是通过onMeasure->onLayout->onDraw进行依次调用的,onLayout用来进行子View布局的确定,从而确定父容器的本身布局,所以我们在onLayout完成后在拿到子View对象的引用是最保险的。
其次,很多时候我们会遇到要使用getWidth()或者getHeight()进行空间或者布局的宽高获取,一般而言,我们极大可能会遇到获取的宽高=0的情况,可能的原因就是布局还没有初始化完成的时候我们却想要获取宽高,所以导致的这个问题,所以我们在layout完成后进行布局高宽的获取,即:
refreshTextView.getHeight();
接下来就是重头戏了
触摸事件的判断:
先看我们的onInterceptTouchEvent():
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercept=false; int action = ev.getAction(); if (action == MotionEvent.ACTION_DOWN) { oldInterceptX = (int) ev.getX(); oldInterceptY = (int) ev.getY(); intercept=false; if (!mScroller.isFinished()){ mScroller.abortAnimation(); intercept=true; } } else if (action == MotionEvent.ACTION_MOVE) { interceptX = (int) ev.getX(); interceptY = (int) ev.getY(); int distance = interceptY - oldInterceptY; //向下滑动,listview到头顶了,滑动最小距离超过mTouchSlop, // 且当前正在刷新,则进行下拉刷新的实现 if (interceptY >oldInterceptY && Math.abs(distance) > mTouchSlop && isOnTop() &&!isRefresh) { // Log.e("canReshresh",true+""); intercept=true; }else intercept=false; } else if (action == MotionEvent.ACTION_UP) { oldInterceptX = (int) ev.getX(); oldInterceptY = (int) ev.getY(); intercept=false; } return intercept; }
关于触摸事件的原理,在这里就不过多的去详述了,不理解的请自行查阅。
看上面的代码,我们定义了几个变量来记录触摸时候的坐标:
//正在触摸的坐标 private int interceptX, interceptY; //上一次触摸是的坐标 private int oldInterceptX, oldInterceptY;
我们在ACTION_DOWN的时候返回了false,让父容器下发事件,Listview能够接受到事件进行滑动
当我们在ACTION_MOVE的时候,我们要进行判断是父容器拦截事件还是向下继续传递事件,我们常规来想就是当listview到达头部的时候,我们进行控件的下拉操作,即:
private boolean isOnTop() { View child_one = listView.getChildAt(0); //通过第一个child判断是否到达顶端,并且进行下拉操作 if (child_one.getY() - listView.getPaddingTop() == 0) { // Log.e("isOnTop===", true + ""); return true; } return false; }
且,我们是需要进行下拉操作,并且父容器现在并没有进行刷新操作:
interceptX = (int) ev.getX(); interceptY = (int) ev.getY(); int distance = interceptY - oldInterceptY; //向下滑动,listview到头顶了,滑动最小距离超过mTouchSlop, // 且当前不在刷新,则进行下拉刷新的实现 if (interceptY >oldInterceptY && Math.abs(distance) > mTouchSlop && isOnTop() &&!isRefresh) { // Log.e("canReshresh",true+""); intercept=true; }else intercept=false;
ACTION_UP的时候我们还是不拦截事件。
当父容器拦截了事件后,就会执行onTouchEvent:
int action = event.getAction(); lastX = oldInterceptX; lastY = oldInterceptY; // Log.e("lastY",lastY+""); switch (action) { case MotionEvent.ACTION_DOWN: //由于onInterceptTouchEvent在ACTION_DOWN的时候返回的是false,所以事件往下发放,这里 //如果写: // lastX = (int) event.getX(); // lastY = (int) event.getY(); //是拦截不到的 if (!mScroller.isFinished()){ mScroller.abortAnimation(); } break; case MotionEvent.ACTION_MOVE: currentX = (int) event.getX(); currentY = (int) event.getY(); int moveX = Math.abs(currentX - lastX); int moveY = Math.abs(currentY - lastY); // Log.e("currentY",currentY+""); // Log.e("lastY",lastY+""); // &&Math.abs(getScrollY())<maxSmoothDistance if (moveY > moveX) { scrollTo(0, -(currentY-lastY)); } break; case MotionEvent.ACTION_UP: lastX = (int) event.getX(); lastY = (int) event.getY(); currentX = (int) event.getX(); currentY = (int) event.getY(); smoothScrollTo(); break; } return true;
请注意看ACTION_DOWN的注释,因为我们父容器并没有拦截此事件,所以我们在onTouchEvent中是不会执行ACTION_DOWN操作的。
ACTION_DOWN中我们要判断是否是竖直方向的下拉,如果是,则进行scrollTo操作:
currentX = (int) event.getX(); currentY = (int) event.getY(); int moveX = Math.abs(currentX - lastX); int moveY = Math.abs(currentY - lastY); // Log.e("currentY",currentY+""); // Log.e("lastY",lastY+""); // &&Math.abs(getScrollY())<maxSmoothDistance if (moveY > moveX) { scrollTo(0, -(currentY-lastY)); }
在ACTION_UP的时候我们要进行scroller的使用,判断是否完成刷新,或者达不到刷新的要求就自动滑动回原来的位置,smoothScrollTo方法:
//移动要移动到得位置坐标destX,destY public void smoothScrollTo(){ int scrollY=getScrollY(); Log.e("scrolly-----------",""+getScrollY()); if (Math.abs(scrollY)>0){ //当下拉刷新布局全部显示时候,进行刷新操作,否则,不进行刷新操作 if (Math.abs(scrollY)>putRefreshHeight){ mScroller.startScroll(0,scrollY,0,-scrollY-putRefreshHeight,800); isRefresh=true; invalidate(); }else { mScroller.startScroll(0,scrollY,0,-scrollY,800); invalidate(); isRefresh=false; } }
主要代码展示如上所示,如果想要这个demo的同学,可以去我的gitHub中查看下载:
https://github.com/JerryChan123/android-learning
相关文章推荐
- java文本处理常用代码总结(二)
- 不相交集python实现
- 微信无法跳转appstore总结--应用宝微下载申请
- Android Studio默认产生Fragment
- SpringMVC -- 注解的奥秘
- DIV+CSS 命名规范
- 自定义SeekBar
- Linux下MySQL数据库常用基本操作 一
- 使用UITextField 键盘遮盖输入框
- JMS入门_StepByStep
- 过程与它们所产生的计算——《计算机程序的构造和解释》
- HDU1711(KMP入门题)
- SQLSERVER聚集索引与非聚集索引的再次研究(上)
- Codevs_P1237 餐巾问题(线性规划与网络流24题+最小费用最大流)
- 适配器模式
- 不使用库函数实现字符串的复制、连接、比较、反转、计算长度及易混淆点等
- Android通过Movie展示Gif格式图片
- 01背包动态规划
- zabbix使用自带模板监控mysql
- 命令性解释性编译性语言