Android中100行代码实现可上下拉动的自定义ListView
来源:互联网 发布:房屋平面图制作软件 编辑:程序博客网 时间:2024/05/14 04:00
转载请注明出处:http://blog.csdn.net/bettarwang/article/details/41634729
之前在网上也看到一些所谓的下拉刷新的例子,但是总感觉是把简单的事情复杂化了,动辄300多行甚至600多行的代码,其实主要就是对触摸事件作出反应嘛,根本用不着这么麻烦。下面先实现一个可上下拉动的ListView,再实现一个带有Header的可下拉刷新的ListView:
可上下拉动的ListView的源码如下:
/** * 可上下拉动的ListView * @author Bettar * */public class RefreshableListView extends ListView{private static final String TAG="RefreshableListView";private int touchSlop;private int initTopMargin;//private int initTopOfFirstChild;private boolean hasRecord=false;private float startY;private boolean isPulling=false;//private ViewGroup.LayoutParams params;private LinearLayout.LayoutParams params;public RefreshableListView(Context context, AttributeSet attrs) {super(context, attrs);//这样的话就可以将设置参数读入,从而不会与layout文件的设置产生冲突。params=new LinearLayout.LayoutParams(context, attrs);initTopMargin=params.topMargin;this.setLayoutParams(params);touchSlop=ViewConfiguration.get(context).getTouchSlop();}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:if(!hasRecord){hasRecord=true;startY=event.getY();Log.i(TAG,"ACTION_DOWN");}break;case MotionEvent.ACTION_MOVE:float distance=event.getY()-startY;if(!isPulling){if(!couldPull(distance)) { Log.i(TAG,"could not pull in ACTION_MOVE"); return false; } } isPulling=true; Log.i(TAG,"pull in ACTION_MOVE"); params.topMargin+=distance; this.setLayoutParams(params); this.setPressed(false); this.setFocusable(false); this.setFocusableInTouchMode(false); return true;case MotionEvent.ACTION_UP:Log.i(TAG,"ACTION_UP");params.topMargin=initTopMargin;this.setLayoutParams(params);hasRecord=false;this.setFocusable(true);this.setFocusableInTouchMode(true);if(isPulling){isPulling=false;//注意:拉伸后放起必须返回true,否则这个事件还会被其他的事件处理器读取,从而影响该类的外部操作,如setOnItemClickListener中的操作。return true;}isPulling=false;break;}return super.onTouchEvent(event);}private boolean couldPull(float distance){if(Math.abs(distance)<touchSlop){return false;}if(distance>0){Log.i(TAG,"getTop()"+this.getTop());if(this.getFirstVisiblePosition()==0&&this.getChildAt(0).getTop()==0){return true;}}else{if(this.getLastVisiblePosition()==this.getCount()-1){return true;}}return false;}}
要注意的一个细节是ACTION_UP时的处理,如果是拉伸后放开手指的ACTION_UP,那么要返回true而不是false,否则会影响这个自定义ListView的正常使用,因为如果返回false的话则这整个过程由于有ACTION_DOWN和ACTION_UP,会被当作一次Click,从而影响造成额外的影响。
如果要加上一定的动画,也很简单,使用补间动画或者异步任务去实现,下面的代码使用了两种实现方式:
package com.android.customview;import android.content.Context;import android.os.AsyncTask;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.animation.TranslateAnimation;import android.widget.AbsListView;import android.widget.LinearLayout;import android.widget.ListView;/** * 可上下拉动的ListView * @author Bettar * */public class RefreshableListView extends ListView{private static final String TAG="RefreshableListView";//0.5的话会感觉很粘滞,而1.0的话又感觉太滑,0.8是一个比较好的参数。private static final float RATIO=0.8f;private static final int ANIM_DURATION=1000;private int touchSlop;private int initTopMargin;private int[]initLocation=new int[2];private boolean hasRecord=false;private float startY;private boolean isPulling=false;//private ViewGroup.LayoutParams params;private LinearLayout.LayoutParams params;public RefreshableListView(Context context, AttributeSet attrs) {super(context, attrs);//params=this.getLayoutParams();//这样的话就可以将设置参数读入,从而不会与layout文件的设置产生冲突。params=new LinearLayout.LayoutParams(context, attrs);initTopMargin=params.topMargin;this.getLocationOnScreen(initLocation);//initTopOfFirstChild=this.getChildAt(0).getTop();this.setLayoutParams(params);touchSlop=ViewConfiguration.get(context).getTouchSlop();}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:if(!hasRecord){hasRecord=true;startY=event.getY();Log.i(TAG,"ACTION_DOWN");}break;case MotionEvent.ACTION_MOVE:float distance=event.getY()-startY;if(!isPulling){if(!couldPull(distance)) { Log.i(TAG,"could not pull in ACTION_MOVE"); return false; } } isPulling=true; Log.i(TAG,"pull in ACTION_MOVE"); params.topMargin=initTopMargin+(int)(distance*RATIO); this.setLayoutParams(params); this.setPressed(false); this.setFocusable(false); this.setFocusableInTouchMode(false); return true;case MotionEvent.ACTION_UP:Log.i(TAG,"ACTION_UP");if(isPulling){startTranslateAnimation();//executeTranslateAnimation();}//重设参数,注意如果是使用自定义动画,那么要将此处的reset();注释,等到异步任务执行完毕后再执行reset();否则参数会相互干扰。 reset();if(isPulling){isPulling=false;//注意:拉伸后放起必须返回true,否则这个事件还会被其他的事件处理器读取,从而影响该类的外部操作,如setOnItemClickListener中的操作。return true;}isPulling=false;break;}return super.onTouchEvent(event);}private void reset(){params.topMargin=initTopMargin;this.setLayoutParams(params);hasRecord=false;this.setFocusable(true);this.setFocusableInTouchMode(true);}private void startTranslateAnimation(){int[]location=new int[2];RefreshableListView.this.getLocationOnScreen(location);//测试发现location[0]==0而location[1]就是第一个Item上端距离顶部的距离。Log.i(TAG,"location[0]="+location[0]+" location[1]="+location[1]);TranslateAnimation anim=new TranslateAnimation(location[0],initLocation[0],location[1],initLocation[1]);anim.setDuration(ANIM_DURATION);RefreshableListView.this.startAnimation(anim);}/** *这其实就相当于自己去实现动画了。 */private void executeTranslateAnimation(){new TranslateTask(20).execute();}/** * 如果是使用它的话就要将params的参数等放到异步任务执行完之后再完成,否则会现相互干扰的情况。 * @author Bettar * */private class TranslateTask extends AsyncTask<Void,Integer,Integer>{//每次线程的睡眠时间private int deltaSleepTime;private int deltaScrollY;public TranslateTask(int deltaSleepTime){this.deltaSleepTime=deltaSleepTime;if(deltaSleepTime>0){deltaScrollY=0-(params.topMargin-initTopMargin)/(ANIM_DURATION/deltaSleepTime);}else{deltaScrollY=params.topMargin>initTopMargin?-20:20;}Log.i(TAG,"deltaScrollY="+deltaScrollY);}@Overrideprotected Integer doInBackground(Void...voidParams) {int topMargin=params.topMargin;while(true){topMargin+=deltaScrollY;Log.i(TAG,"topMargin="+topMargin);if(deltaScrollY<0){if(topMargin<0){topMargin=0;break;}}else{if(topMargin>0){topMargin=0;break;}}publishProgress(topMargin);try{Thread.sleep(deltaSleepTime);}catch(InterruptedException ex){ex.printStackTrace();}}publishProgress(0);return topMargin;}@Overrideprotected void onProgressUpdate(Integer... values) {//values[0]对应上面publisProgress中的topMarginLog.i(TAG,"values[0] i.e topMargin="+values[0]);params.topMargin=values[0];RefreshableListView.this.setLayoutParams(params);}@Overrideprotected void onPostExecute(Integer result) {//执行完异步任务之后就可以进行参数重新设置了reset();}}/** * 判断是否可以开始拉动,如果是向下拉动,则要求第一个Item完全可见;如果是向上拉,则要求最后一个Item完全可见。 * @param distance * @return */private boolean couldPull(float distance){if(Math.abs(distance)<touchSlop){return false;}if(distance>0){Log.i(TAG,"getTop()"+this.getTop());if(this.getFirstVisiblePosition()==0&&this.getChildAt(0).getTop()==0)//if(this.getFirstVisiblePosition()==0&&this.getChildAt(0).getTop()==initTopOfFirstChild){return true;}}else{if(this.getLastVisiblePosition()==this.getCount()-1){return true;}}return false;}}
相信到了这里,要做一个带下拉刷新头的ListView是极简单的,今天就先到这儿吧,下拉刷新的代码后面补上。
1 0
- Android中100行代码实现可上下拉动的自定义ListView
- 写一个上下拉动到底时有弹性的listview
- Xlistview实现上下拉动刷新
- ios7中,A界面状态栏下面有个UIScrollView,push到B界面后,回退回来UIScrollView下移和可上下拉动的解决
- ios7中,A界面状态栏下面有个UIScrollView,push到B界面后,回退回来UIScrollView下移和可上下拉动的解决
- android listview中设置背景图片后 拉动变黑
- android 自定义ScrollView可上下拉伸
- Android自定义ListView(一) - 可下拉刷新的ListView
- Android使用按钮实现ListView的上下分页
- 一种上下拉动的实用下拉框
- android通过代码控制ListView上下滚动
- android解决listview与scrollview的冲突、自定义listview的高度以及上下拉刷新
- android中实现图片的上下移动
- android 自定义ViewGroup实现可记载并呈现选择的ListView
- Android中ListView的分页--自定义ListView
- 做listview+上下拉动在header和footer
- Android自定义ListView,轻松实现上下拉刷新,一看就懂,一学就会,超简单。
- Android 自定义最简单的ScrollView,附带拉动回弹
- NOIP2014day1T2联合权值新版题解(优化的邻接表)
- Core data 使用(二)
- OC学习总结(二)
- @synthesize和@dynamic区别
- eclipse快捷键
- Android中100行代码实现可上下拉动的自定义ListView
- hibernate缓存机制
- nginx负载均衡配置
- 如何提升自己的技术/能力——兴趣篇
- NYOJ-109 数列转换 AC
- QT5.3的中文乱码问题
- eclipse 手机识别的解决方案
- 小白书训练-Artificial Intelligence?
- 自动化部署MySQL 5.6 步骤