前言 原 来的Android SDK中并没有下拉刷新组件,但是这个组件确实绝大多数APP必备的一个部件。好在google在v4包中出了一个 SwipeRefreshLayout,但是这个组件只支持下拉刷新,不支持上拉加载更多的操作。因此,我们就来简单的扩展一下这个组件以实现上拉下载的 目的。 基本原理 上 拉加载或者说滚动到底部时自动加载,都是通过判断是否滚动到了ListView或者其他View的底部,然后触发相应的操作,这里我们以ListView 来说明。因此我们需要在监听ListView的滚动事件,当ListView滚动到底部时自动触发加载操作;但是当用户支持手指滑动屏幕,没有滚动时,我 们也需要让它加载,因此这种情形就是上拉加载更多。所以我们需要在触摸事件里面进行判断,如果到了底部,且用户是上拉操作,那么执行加载更多。更多关于下 拉刷新、上拉加载请参考[打造通用的Android下拉刷新组件(适用于ListView、GridView等各类View)](http://blog.csdn.net/bboyfeiyu/article/details/39718861)。 时间有限,直接上代码吧。 实现代码 **[java]** [view plain](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[copy](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[](https://code.csdn.net/snippets/503362)[](https://code.csdn.net/snippets/503362/fork) <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> </div> </div> - <span class="comment">/**</span> - <span class="comment"> * 继承自SwipeRefreshLayout,从而实现滑动到底部时上拉加载更多的功能.</span> - <span class="comment"> * </span> - <span class="comment"> * @author mrsimple</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">class</span> RefreshLayout <span class="keyword">extends</span> SwipeRefreshLayout <span class="keyword">implements</span> OnScrollListener { - - <span class="comment">/**</span> - <span class="comment"> * 滑动到最下面时的上拉操作</span> - <span class="comment"> */</span> - - <span class="keyword">private</span> <span class="keyword">int</span> mTouchSlop; - <span class="comment">/**</span> - <span class="comment"> * listview实例</span> - <span class="comment"> */</span> - <span class="keyword">private</span> ListView mListView; - - <span class="comment">/**</span> - <span class="comment"> * 上拉监听器, 到了最底部的上拉加载操作</span> - <span class="comment"> */</span> - <span class="keyword">private</span> OnLoadListener mOnLoadListener; - - <span class="comment">/**</span> - <span class="comment"> * ListView的加载中footer</span> - <span class="comment"> */</span> - <span class="keyword">private</span> View mListViewFooter; - - <span class="comment">/**</span> - <span class="comment"> * 按下时的y坐标</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">int</span> mYDown; - <span class="comment">/**</span> - <span class="comment"> * 抬起时的y坐标, 与mYDown一起用于滑动到底部时判断是上拉还是下拉</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">int</span> mLastY; - <span class="comment">/**</span> - <span class="comment"> * 是否在加载中 ( 上拉加载更多 )</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> isLoading = <span class="keyword">false</span>; - - <span class="comment">/**</span> - <span class="comment"> * @param context</span> - <span class="comment"> */</span> - <span class="keyword">public</span> RefreshLayout(Context context) { - <span class="keyword">this</span>(context, <span class="keyword">null</span>); - } - - <span class="keyword">public</span> RefreshLayout(Context context, AttributeSet attrs) { - <span class="keyword">super</span>(context, attrs); - - mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - - mListViewFooter = LayoutInflater.from(context).inflate(R.layout.listview_footer, <span class="keyword">null</span>, - <span class="keyword">false</span>); - } - - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onLayout(<span class="keyword">boolean</span> changed, <span class="keyword">int</span> left, <span class="keyword">int</span> top, <span class="keyword">int</span> right, <span class="keyword">int</span> bottom) { - <span class="keyword">super</span>.onLayout(changed, left, top, right, bottom); - - <span class="comment">// 初始化ListView对象</span> - <span class="keyword">if</span> (mListView == <span class="keyword">null</span>) { - getListView(); - } - } - - <span class="comment">/**</span> - <span class="comment"> * 获取ListView对象</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">void</span> getListView() { - <span class="keyword">int</span> childs = getChildCount(); - <span class="keyword">if</span> (childs > <span class="number"></span>) { - View childView = getChildAt(<span class="number"></span>); - <span class="keyword">if</span> (childView <span class="keyword">instanceof</span> ListView) { - mListView = (ListView) childView; - <span class="comment">// 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载</span> - mListView.setOnScrollListener(<span class="keyword">this</span>); - Log.d(VIEW_LOG_TAG, <span class="string">“### 找到listview”</span>); - } - } - } - - <span class="comment">/*</span> - <span class="comment"> * (non-Javadoc)</span> - <span class="comment"> * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)</span> - <span class="comment"> */</span> - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> dispatchTouchEvent(MotionEvent event) { - <span class="keyword">final</span> <span class="keyword">int</span> action = event.getAction(); - - <span class="keyword">switch</span> (action) { - <span class="keyword">case</span> MotionEvent.ACTION_DOWN: - <span class="comment">// 按下</span> - mYDown = (<span class="keyword">int</span>) event.getRawY(); - <span class="keyword">break</span>; - - <span class="keyword">case</span> MotionEvent.ACTION_MOVE: - <span class="comment">// 移动</span> - mLastY = (<span class="keyword">int</span>) event.getRawY(); - <span class="keyword">break</span>; - - <span class="keyword">case</span> MotionEvent.ACTION_UP: - <span class="comment">// 抬起</span> - <span class="keyword">if</span> (canLoad()) { - loadData(); - } - <span class="keyword">break</span>; - <span class="keyword">default</span>: - <span class="keyword">break</span>; - } - - <span class="keyword">return</span> <span class="keyword">super</span>.dispatchTouchEvent(event); - } - - <span class="comment">/**</span> - <span class="comment"> * 是否可以加载更多, 条件是到了最底部, listview不在加载中, 且为上拉操作.</span> - <span class="comment"> * </span> - <span class="comment"> * @return</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> canLoad() { - <span class="keyword">return</span> isBottom() && !isLoading && isPullUp(); - } - - <span class="comment">/**</span> - <span class="comment"> * 判断是否到了最底部</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> isBottom() { - - <span class="keyword">if</span> (mListView != <span class="keyword">null</span> && mListView.getAdapter() != <span class="keyword">null</span>) { - <span class="keyword">return</span> mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() – <span class="number">1</span>); - } - <span class="keyword">return</span> <span class="keyword">false</span>; - } - - <span class="comment">/**</span> - <span class="comment"> * 是否是上拉操作</span> - <span class="comment"> * </span> - <span class="comment"> * @return</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">boolean</span> isPullUp() { - <span class="keyword">return</span> (mYDown – mLastY) >= mTouchSlop; - } - - <span class="comment">/**</span> - <span class="comment"> * 如果到了最底部,而且是上拉操作.那么执行onLoad方法</span> - <span class="comment"> */</span> - <span class="keyword">private</span> <span class="keyword">void</span> loadData() { - <span class="keyword">if</span> (mOnLoadListener != <span class="keyword">null</span>) { - <span class="comment">// 设置状态</span> - setLoading(<span class="keyword">true</span>); - <span class="comment">//</span> - mOnLoadListener.onLoad(); - } - } - - <span class="comment">/**</span> - <span class="comment"> * @param loading</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">void</span> setLoading(<span class="keyword">boolean</span> loading) { - isLoading = loading; - <span class="keyword">if</span> (isLoading) { - mListView.addFooterView(mListViewFooter); - } <span class="keyword">else</span> { - mListView.removeFooterView(mListViewFooter); - mYDown = <span class="number"></span>; - mLastY = <span class="number"></span>; - } - } - - <span class="comment">/**</span> - <span class="comment"> * @param loadListener</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">void</span> setOnLoadListener(OnLoadListener loadListener) { - mOnLoadListener = loadListener; - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onScrollStateChanged(AbsListView view, <span class="keyword">int</span> scrollState) { - - } - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onScroll(AbsListView view, <span class="keyword">int</span> firstVisibleItem, <span class="keyword">int</span> visibleItemCount, - <span class="keyword">int</span> totalItemCount) { - <span class="comment">// 滚动时到了最底部也可以加载更多</span> - <span class="keyword">if</span> (canLoad()) { - loadData(); - } - } - - <span class="comment">/**</span> - <span class="comment"> * 加载更多的监听器</span> - <span class="comment"> * </span> - <span class="comment"> * @author mrsimple</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">interface</span> OnLoadListener { - <span class="keyword">public</span> <span class="keyword">void</span> onLoad(); - } - } listview_footer.xml: **[html]** [view plain](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[copy](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[](https://code.csdn.net/snippets/503362)[](https://code.csdn.net/snippets/503362/fork) <div> <embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_2"> </embed> </div> </div> </div> - <span class="tag"><?</span><span class="tag-name">xml</span> <span class="attribute">version</span>=<span class="attribute-value">“1.0”</span> <span class="attribute">encoding</span>=<span class="attribute-value">“utf-8”</span><span class="tag">?></span> - <span class="tag"><</span><span class="tag-name">RelativeLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">“http://schemas.android.com/apk/res/android”</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">“fill_parent”</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span> - <span class="attribute">android:background</span>=<span class="attribute-value">“@color/umeng_comm_comments_bg”</span> - <span class="attribute">android:gravity</span>=<span class="attribute-value">“center”</span> - <span class="attribute">android:paddingBottom</span>=<span class="attribute-value">“8dip”</span> - <span class="attribute">android:paddingTop</span>=<span class="attribute-value">“5dip”</span> <span class="tag">></span> - - <span class="tag"><</span><span class="tag-name">ProgressBar</span> - <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/pull_to_refresh_load_progress”</span> - <span class="attribute">style</span>=<span class="attribute-value">“@android:style/Widget.ProgressBar.Small.Inverse”</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">“wrap_content”</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span> - <span class="attribute">android:layout_centerVertical</span>=<span class="attribute-value">“true”</span> - <span class="attribute">android:layout_centerHorizontal</span>=<span class="attribute-value">“true”</span> - <span class="attribute">android:paddingRight</span>=<span class="attribute-value">“100dp”</span> - <span class="attribute">android:indeterminate</span>=<span class="attribute-value">“true”</span> <span class="tag">/></span> - - <span class="tag"><</span><span class="tag-name">TextView</span> - <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/pull_to_refresh_loadmore_text”</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">“fill_parent”</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span> - <span class="attribute">android:layout_gravity</span>=<span class="attribute-value">“center”</span> - <span class="attribute">android:gravity</span>=<span class="attribute-value">“center”</span> - <span class="attribute">android:paddingTop</span>=<span class="attribute-value">“5dip”</span> - <span class="attribute">android:text</span>=<span class="attribute-value">“@string/load”</span> - <span class="attribute">android:textAppearance</span>=<span class="attribute-value">“?android:attr/textAppearanceMedium”</span> - <span class="attribute">android:textColor</span>=<span class="attribute-value">“@android:color/darker_gray”</span> - <span class="attribute">android:textSize</span>=<span class="attribute-value">“14sp”</span> - <span class="attribute">android:textStyle</span>=<span class="attribute-value">“bold”</span> <span class="tag">/></span> - - <span class="tag"></</span><span class="tag-name">RelativeLayout</span><span class="tag">></span> 使用示例 refresh.xml布局文件: **[html]** [view plain](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[copy](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[](https://code.csdn.net/snippets/503362)[](https://code.csdn.net/snippets/503362/fork) <div> <embed id="ZeroClipboardMovie_3" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_3"> </embed> </div> </div> </div> - <span class="tag"><?</span><span class="tag-name">xml</span> <span class="attribute">version</span>=<span class="attribute-value">“1.0”</span> <span class="attribute">encoding</span>=<span class="attribute-value">“utf-8”</span><span class="tag">?></span> - <span class="tag"><</span><span class="tag-name">myview.RefreshLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">“http://schemas.android.com/apk/res/android”</span> - <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/swipe_layout”</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">“match_parent”</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">“match_parent”</span> <span class="tag">></span> - - <span class="tag"><</span><span class="tag-name">ListView</span> - <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/listview”</span> - <span class="attribute">android:layout_width</span>=<span class="attribute-value">“match_parent”</span> - <span class="attribute">android:layout_height</span>=<span class="attribute-value">“match_parent”</span> <span class="tag">></span> - <span class="tag"></</span><span class="tag-name">ListView</span><span class="tag">></span> - - <span class="tag"></</span><span class="tag-name">myview.RefreshLayout</span><span class="tag">></span> activity中的使用 : **[java]** [view plain](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[copy](http://blog.csdn.net/bboyfeiyu/article/details/39935329#)[](https://code.csdn.net/snippets/503362)[](https://code.csdn.net/snippets/503362/fork) <div> <embed id="ZeroClipboardMovie_4" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_4"> </embed> </div> </div> </div> - <span class="comment">/**</span> - <span class="comment"> * @author mrsimple</span> - <span class="comment"> */</span> - <span class="keyword">public</span> <span class="keyword">class</span> MainActivity <span class="keyword">extends</span> Activity { - - <span class="annotation">@Override</span> - <span class="keyword">protected</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) { - <span class="keyword">super</span>.onCreate(savedInstanceState); - - setContentView(R.layout.refresh); - - <span class="comment">// 模拟一些数据</span> - <span class="keyword">final</span> List<String> datas = <span class="keyword">new</span> ArrayList<String>(); - <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number"></span>; i < <span class="number">20</span>; i++) { - datas.add(<span class="string">“item – “</span> + i); - } - - <span class="comment">// 构造适配器</span> - <span class="keyword">final</span> BaseAdapter adapter = <span class="keyword">new</span> ArrayAdapter<String>(<span class="keyword">this</span>, - android.R.layout.simple_list_item_1, - datas); - <span class="comment">// 获取listview实例</span> - ListView listView = (ListView) findViewById(R.id.listview); - listView.setAdapter(adapter); - - <span class="comment">// 获取RefreshLayout实例</span> - <span class="keyword">final</span> RefreshLayout myRefreshListView = (RefreshLayout) - findViewById(R.id.swipe_layout); - - <span class="comment">// 设置下拉刷新时的颜色值,颜色值需要定义在xml中</span> - myRefreshListView - .setColorScheme(R.color.umeng_comm_text_topic_light_color, - R.color.umeng_comm_yellow, R.color.umeng_comm_green, - R.color.umeng_comm_linked_text); - <span class="comment">// 设置下拉刷新监听器</span> - myRefreshListView.setOnRefreshListener(<span class="keyword">new</span> OnRefreshListener() { - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onRefresh() { - - Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">“refresh”</span>, Toast.LENGTH_SHORT).show(); - - myRefreshListView.postDelayed(<span class="keyword">new</span> Runnable() { - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> run() { - <span class="comment">// 更新数据</span> - datas.add(<span class="keyword">new</span> Date().toGMTString()); - adapter.notifyDataSetChanged(); - <span class="comment">// 更新完后调用该方法结束刷新</span> - myRefreshListView.setRefreshing(<span class="keyword">false</span>); - } - }, <span class="number">1000</span>); - } - }); - - <span class="comment">// 加载监听器</span> - myRefreshListView.setOnLoadListener(<span class="keyword">new</span> OnLoadListener() { - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onLoad() { - - Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">“load”</span>, Toast.LENGTH_SHORT).show(); - - myRefreshListView.postDelayed(<span class="keyword">new</span> Runnable() { - - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> run() { - datas.add(<span class="keyword">new</span> Date().toGMTString()); - adapter.notifyDataSetChanged(); - <span class="comment">// 加载完后调用该方法</span> - myRefreshListView.setLoading(<span class="keyword">false</span>); - } - }, <span class="number">1500</span>); - - } - }); - } - - } 效果图  github链接 : [下拉刷新库](https://github.com/bboyfeiyu/android_my_pull_refresh_view) 。 示例在android_my_pull_demo工程中,进入demo后点击最后一个按钮查看效果即可。注意,在刷新和加载时,需要有一定的延时才能看到效果,这里我们用postDelay来模拟网络请求等延时操作,否则将看不到加载效果。