一、前言:

很多应用都会用到ListView,当然如果是iOS就会用UITableViewController,这两个控件在不同的OS上,功能是一样的,只是有些细微的不同(iOS的UITableViewController支持静态与动态两种),不过,大多数应用都用的是动态属性,那么,这里就涉及到一个问题:刷新及加载更多内容。

目前网上流行的有两种方式:

1. 通用的方法,即将ListView, GridView和ScrollView当成ChildView,在这顶部及底部各加一个Layout,但是,一但出现了,就一直显示在顶部或底部,并不会随着ChildView的滚动而滚动,功能实用,就是有点破坏美感;

2. 各自实现,即如果需要实现ListView的下拉刷新和上拉更多,那么就得去继承ListView,并对它的HeaderView和FooterView做一些扩展,同理,GridView和ScrollView;

本篇将使用第二种方法来实现,如果通过继承ListView的方式,来实现下拉刷新,以及上拉更多,或者是点击底部加载更多的。

二、实现:

2.1 HeaderView的布局,以及代码实现

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:background="#ffffff"
  6. android:gravity="bottom">
  7.  
  8. <RelativeLayout
  9. android:id="@+id/header_content"
  10. android:layout_width="match_parent"
  11. android:layout_height="60dip">
  12.  
  13. <LinearLayout
  14. android:id="@+id/layoutTitle"
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:layout_centerInParent="true"
  18. android:gravity="center"
  19. android:orientation="vertical">
  20.  
  21. <TextView
  22. android:id="@+id/refresh_tips"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:textSize="15sp"
  26. android:text="@string/pull_down_for_refresh"/>
  27.  
  28. <LinearLayout
  29. android:layout_width="wrap_content"
  30. android:layout_height="wrap_content"
  31. android:orientation="horizontal"
  32. android:layout_marginTop="4dip">
  33.  
  34. <TextView
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:textSize="12sp"
  38. android:text="@string/label_update"/>
  39. <TextView
  40. android:id="@+id/refresh_last_time"
  41. android:layout_width="wrap_content"
  42. android:layout_height="wrap_content"
  43. android:textSize="12sp"
  44. android:text="@string/label_last_time"/>
  45.  
  46. </LinearLayout>
  47.  
  48. </LinearLayout>
  49.  
  50. <ImageView
  51. android:id="@+id/ivArrow"
  52. android:layout_height="wrap_content"
  53. android:layout_width="wrap_content"
  54. android:layout_toLeftOf="@id/layoutTitle"
  55. android:layout_centerInParent="true"
  56. android:layout_marginRight="30dip"
  57. android:contentDescription="@string/image_desc"
  58. android:src="@drawable/refresh_arrow_down"/>
  59.  
  60. <ProgressBar
  61. android:id="@+id/pbWaiting"
  62. android:visibility="gone"
  63. android:layout_height="wrap_content"
  64. android:layout_width="wrap_content"
  65. android:layout_toLeftOf="@id/layoutTitle"
  66. android:layout_centerInParent="true"
  67. android:layout_marginRight="30dip"
  68. style="?android:attr/progressBarStyleSmall"/>
  69.  
  70. </RelativeLayout>
  71.  
  72. </LinearLayout>

布局很简单,一些TextView,一个ImageView和一个ProgressBar。再来看看代码实现

  1. package com.chris.list.refresh;
  2.  
  3. import android.content.Context;
  4. import android.util.AttributeSet;
  5. import android.view.Gravity;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.animation.Animation;
  9. import android.view.animation.RotateAnimation;
  10. import android.widget.ImageView;
  11. import android.widget.LinearLayout;
  12. import android.widget.ProgressBar;
  13. import android.widget.TextView;
  14.  
  15. public class HeaderView extends LinearLayout {
  16.  
  17. public final static int STATE_NORMAL = 0;
  18. public final static int STATE_WILL_RELEASE = 1;
  19. public final static int STATE_REFRESHING = 2;
  20. private int mState = STATE_NORMAL;
  21.  
  22. private View mHeader = null;
  23. private ImageView mArrow = null;
  24. private ProgressBar mProgressBar = null;
  25. private TextView mRefreshTips = null;
  26. //private TextView mRefreshLastTime = null;
  27. private RotateAnimation mRotateUp = null;
  28. private RotateAnimation mRotateDown = null;
  29. private final static int ROTATE_DURATION = 250;
  30.  
  31. public HeaderView(Context context) {
  32. this(context, null);
  33. }
  34.  
  35. public HeaderView(Context context, AttributeSet attrs) {
  36. super(context, attrs);
  37. initHeaderView(context);
  38. }
  39.  
  40. private void initHeaderView(Context context){
  41. LinearLayout.LayoutParams lp = new LayoutParams(
  42. LayoutParams.MATCH_PARENT, 0);
  43. mHeader = LayoutInflater.from(context).inflate(R.layout.refresh_header, null);
  44. addView(mHeader, lp);
  45. setGravity(Gravity.BOTTOM);
  46.  
  47. mArrow = (ImageView) mHeader.findViewById(R.id.ivArrow);
  48. mProgressBar = (ProgressBar) mHeader.findViewById(R.id.pbWaiting);
  49. mRefreshTips = (TextView) mHeader.findViewById(R.id.refresh_tips);
  50. //mRefreshLastTime = (TextView) mHeader.findViewById(R.id.refresh_last_time);
  51.  
  52. mRotateUp = new RotateAnimation(0.0f, -180.0f,
  53. Animation.RELATIVE_TO_SELF, 0.5f,
  54. Animation.RELATIVE_TO_SELF, 0.5f);
  55. mRotateUp.setDuration(ROTATE_DURATION);
  56. mRotateUp.setFillAfter(true);
  57.  
  58. mRotateDown = new RotateAnimation(-180.0f, 0.0f,
  59. Animation.RELATIVE_TO_SELF, 0.5f,
  60. Animation.RELATIVE_TO_SELF, 0.5f);
  61. mRotateDown.setDuration(ROTATE_DURATION);
  62. mRotateDown.setFillAfter(true);
  63. }
  64.  
  65. public void setHeaderState(int state){
  66. if(mState == state){
  67. return;
  68. }
  69.  
  70. mArrow.clearAnimation();
  71. if(state == STATE_REFRESHING){
  72. mProgressBar.setVisibility(View.VISIBLE);
  73. mArrow.setVisibility(View.GONE);
  74. }else{
  75. mProgressBar.setVisibility(View.GONE);
  76. mArrow.setVisibility(View.VISIBLE);
  77. }
  78.  
  79. switch(state){
  80. case STATE_NORMAL:
  81. mArrow.startAnimation(mRotateDown);
  82. mRefreshTips.setText(R.string.pull_down_for_refresh);
  83. break;
  84.  
  85. case STATE_WILL_RELEASE:
  86. mArrow.startAnimation(mRotateUp);
  87. mRefreshTips.setText(R.string.release_for_refresh);
  88. break;
  89.  
  90. case STATE_REFRESHING:
  91. mRefreshTips.setText(R.string.refreshing);
  92. break;
  93.  
  94. default:
  95. break;
  96. }
  97.  
  98. mState = state;
  99. }
  100.  
  101. public int getCurrentState(){
  102. return mState;
  103. }
  104.  
  105. public void setHeaderHeight(int height){
  106. if(height <= 0){
  107. height = 0;
  108. }
  109. LayoutParams lp = (LayoutParams) mHeader.getLayoutParams();
  110. lp.height = height;
  111. mHeader.setLayoutParams(lp);
  112. }
  113. public int getHeaderHeight(){
  114. return mHeader.getHeight();
  115. }
  116. }

这个代码中,主要就两个函数:setHeaderState 和 setHeaderHeight。 前者是根据TouchEvent,以及当前移动的距离,来设置状态,同时,移动的距离去设置HeaderView的高度,达到一点一点的显示出来。

2.2 FooterView的布局,以及代码实现

看了HeaderView的布局与实现后,FooterView的布局与实现也差不多,咱们一起来看看吧

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:gravity="top" >
  6.  
  7. <RelativeLayout
  8. android:id="@+id/footer_content"
  9. android:layout_width="match_parent"
  10. android:layout_height="60dip" >
  11.  
  12. <TextView
  13. android:id="@+id/loader_tips"
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:layout_centerInParent="true"
  17. android:text="@string/pull_up_for_more"
  18. android:textSize="15sp" />
  19.  
  20. <ImageView
  21. android:id="@+id/ivLoaderArrow"
  22. android:layout_width="wrap_content"
  23. android:layout_height="wrap_content"
  24. android:layout_centerInParent="true"
  25. android:layout_marginRight="30dip"
  26. android:layout_toLeftOf="@id/loader_tips"
  27. android:contentDescription="@string/image_desc"
  28. android:src="@drawable/refresh_arrow_up" />
  29.  
  30. <ProgressBar
  31. android:id="@+id/pbLoaderWaiting"
  32. style="?android:attr/progressBarStyleSmall"
  33. android:layout_width="wrap_content"
  34. android:layout_height="wrap_content"
  35. android:layout_centerInParent="true"
  36. android:layout_marginRight="30dip"
  37. android:layout_toLeftOf="@id/loader_tips"
  38. android:visibility="gone" />
  39. </RelativeLayout>
  40.  
  41. </LinearLayout>

哇,这个布局比HeaderView布局还要简单!?这个布局涵盖了两部分,不过,在布局中无法体现出来,但在代码实现中体现出来了:

1. 上拉更多,这个布局全部显示;

2. 如果是滑到底部点击加载,就不会有ImageView;

还是来看看代码实现吧

  1. package com.chris.list.refresh;
  2.  
  3. import android.content.Context;
  4. import android.util.AttributeSet;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.animation.Animation;
  8. import android.view.animation.RotateAnimation;
  9. import android.widget.ImageView;
  10. import android.widget.LinearLayout;
  11. import android.widget.ProgressBar;
  12. import android.widget.TextView;
  13.  
  14. public class FooterView extends LinearLayout {
  15.  
  16. public final static int FOOTER_OPTIONS_PULL = 0;
  17. public final static int FOOTER_OPTIONS_CLICK = 1;
  18. private static int sFooterOps = FOOTER_OPTIONS_PULL;
  19.  
  20. public final static int STATE_NORMAL = 0;
  21. public final static int STATE_WILL_RELEASE = 1;
  22. public final static int STATE_LOADING = 2;
  23. private int mState = STATE_NORMAL;
  24.  
  25. private View mFooter = null;
  26. private ImageView mArrow = null;
  27. private ProgressBar mProgressBar = null;
  28. private TextView mLoaderTips = null;
  29.  
  30. private RotateAnimation mRotateUp = null;
  31. private RotateAnimation mRotateDown = null;
  32. private final static int ROTATE_DURATION = 250;
  33.  
  34. public FooterView(Context context) {
  35. this(context, null);
  36. }
  37.  
  38. public FooterView(Context context, AttributeSet attrs) {
  39. super(context, attrs);
  40. initFooterView(context);
  41. }
  42.  
  43. private void initFooterView(Context context){
  44. LinearLayout.LayoutParams lp = new LayoutParams(
  45. LayoutParams.MATCH_PARENT, 0);
  46. mFooter = LayoutInflater.from(context).inflate(R.layout.loader_footer, null);
  47. addView(mFooter, lp);
  48.  
  49. mArrow = (ImageView) mFooter.findViewById(R.id.ivLoaderArrow);
  50. mProgressBar = (ProgressBar) mFooter.findViewById(R.id.pbLoaderWaiting);
  51. mLoaderTips = (TextView) mFooter.findViewById(R.id.loader_tips);
  52.  
  53. mRotateDown = new RotateAnimation(0.0f, 180.0f,
  54. Animation.RELATIVE_TO_SELF, 0.5f,
  55. Animation.RELATIVE_TO_SELF, 0.5f);
  56. mRotateDown.setDuration(ROTATE_DURATION);
  57. mRotateDown.setFillAfter(true);
  58.  
  59. mRotateUp = new RotateAnimation(180.0f, 0.0f,
  60. Animation.RELATIVE_TO_SELF, 0.5f,
  61. Animation.RELATIVE_TO_SELF, 0.5f);
  62. mRotateUp.setDuration(ROTATE_DURATION);
  63. mRotateUp.setFillAfter(true);
  64.  
  65. setFooterViewOptions(FOOTER_OPTIONS_CLICK);
  66. }
  67.  
  68. public void setFooterViewOptions(int options){
  69. sFooterOps = options;
  70.  
  71. switch(sFooterOps){
  72. case FOOTER_OPTIONS_PULL:
  73. hide();
  74. break;
  75.  
  76. case FOOTER_OPTIONS_CLICK:
  77. show();
  78. break;
  79.  
  80. default:
  81. break;
  82. }
  83. }
  84.  
  85. public int getFooterViewOptions(){
  86. return sFooterOps;
  87. }
  88.  
  89. public void setFooterState(int state){
  90. if(mState == state){
  91. return;
  92. }
  93.  
  94. mArrow.clearAnimation();
  95. if(state == STATE_LOADING){
  96. mProgressBar.setVisibility(View.VISIBLE);
  97. mArrow.setVisibility(View.GONE);
  98. }else{
  99. mProgressBar.setVisibility(View.GONE);
  100. mArrow.setVisibility(View.VISIBLE);
  101. }
  102.  
  103. switch(state){
  104. case STATE_NORMAL:
  105. mArrow.startAnimation(mRotateUp);
  106. mLoaderTips.setText(R.string.pull_up_for_more);
  107. break;
  108.  
  109. case STATE_WILL_RELEASE:
  110. mArrow.startAnimation(mRotateDown);
  111. mLoaderTips.setText(R.string.release_for_more);
  112. break;
  113.  
  114. case STATE_LOADING:
  115. mLoaderTips.setText(R.string.loading);
  116. break;
  117.  
  118. default:
  119. break;
  120. }
  121. mState = state;
  122. }
  123.  
  124. public int getCurrentState(){
  125. return mState;
  126. }
  127.  
  128. public void setFooterHeight(int height){
  129. if(height <= 0){
  130. height = 0;
  131. }
  132.  
  133. LayoutParams lp = (LayoutParams) mFooter.getLayoutParams();
  134. lp.height = height;
  135. mFooter.setLayoutParams(lp);
  136. }
  137.  
  138. public int getFooterHeight(){
  139. return mFooter.getHeight();
  140. }
  141.  
  142. public void hide(){
  143. mArrow.clearAnimation();
  144. mArrow.setVisibility(View.VISIBLE);
  145. mLoaderTips.setText(R.string.pull_up_for_more);
  146. setFooterHeight(0);
  147. }
  148.  
  149. public void show(){
  150. mArrow.clearAnimation();
  151. mArrow.setVisibility(View.GONE);
  152. mLoaderTips.setText(R.string.click_for_more);
  153.  
  154. LayoutParams lp = (LayoutParams) mFooter.getLayoutParams();
  155. lp.height = LayoutParams.WRAP_CONTENT;
  156. mFooter.setLayoutParams(lp);
  157. }
  158. }

代码中,有个Options函数,用来提供设置:上拉或点击。同样,也有设置状态,和设计高度。

2.3 扩展ListView的实现

  1. package com.chris.list.refresh;
  2.  
  3. import android.content.Context;
  4. import android.util.AttributeSet;
  5. import android.util.Log;
  6. import android.view.MotionEvent;
  7. import android.view.View;
  8. import android.view.ViewTreeObserver.OnGlobalLayoutListener;
  9. import android.view.animation.DecelerateInterpolator;
  10. import android.widget.AbsListView;
  11. import android.widget.ListAdapter;
  12. import android.widget.ListView;
  13. import android.widget.RelativeLayout;
  14. import android.widget.Scroller;
  15. import android.widget.AbsListView.OnScrollListener;
  16.  
  17. public class ListViewExt extends ListView implements OnScrollListener {
  18.  
  19. private final static String TAG = "ChrisLV";
  20.  
  21. private HeaderView mHeaderView = null;
  22. private RelativeLayout mHeaderContent = null;
  23. private int iHeaderHeight = 0;
  24.  
  25. private FooterView mFooterView = null;
  26. private RelativeLayout mFooterContent = null;
  27. private int iFooterHeight = 0;
  28.  
  29. private final static int SCROLL_HEADER = 0;
  30. private final static int SCROLL_FOOTER = 1;
  31. private int iScrollWhich = SCROLL_HEADER;
  32.  
  33. private Scroller mScroller = null;
  34. private final static float OFFSET_Y = 0.7f;
  35. private float iLastY = 0;
  36. private int mTotalNumber = 0;
  37.  
  38. public ListViewExt(Context context) {
  39. this(context, null, 0);
  40. }
  41. public ListViewExt(Context context, AttributeSet attrs) {
  42. this(context, attrs, 0);
  43. }
  44. public ListViewExt(Context context, AttributeSet attrs, int defStyle) {
  45. super(context, attrs, defStyle);
  46. initView(context);
  47. }
  48.  
  49. private void initView(Context context){
  50. /*
  51. * mScroller用来回弹下拉刷新/上拉更多
  52. * 配合computerScroll来使用
  53. */
  54. mScroller = new Scroller(context, new DecelerateInterpolator());
  55. super.setOnScrollListener(this);
  56.  
  57. initHeaderView(context);
  58. initFooterView(context);
  59. }
  60.  
  61. @Override
  62. public void setAdapter(ListAdapter adapter) {
  63. if(getFooterViewsCount() == 0){
  64. addFooterView(mFooterView);
  65. }
  66. super.setAdapter(adapter);
  67. }
  68.  
  69. @Override
  70. public boolean onTouchEvent(MotionEvent ev) {
  71.  
  72. switch(ev.getAction()){
  73. case MotionEvent.ACTION_DOWN:
  74. iLastY = ev.getY();
  75. break;
  76.  
  77. case MotionEvent.ACTION_MOVE:
  78. float deltaY = ev.getY() - iLastY;
  79. iLastY = ev.getY();
  80. if(canHeaderPull() && getFirstVisiblePosition() == 0 &&
  81. (deltaY > 0 || mHeaderView.getHeaderHeight() > 0)){
  82. updateHeaderState(deltaY * OFFSET_Y);
  83. }else if(canFooterPull() && getLastVisiblePosition() == mTotalNumber - 1
  84. && (deltaY < 0 || mFooterView.getFooterHeight() > 0)){
  85. updateFooterState(-deltaY * OFFSET_Y);
  86. }
  87. break;
  88.  
  89. case MotionEvent.ACTION_UP:
  90. if(getFirstVisiblePosition() == 0){
  91. if(mHeaderView.getHeaderHeight() > iHeaderHeight){
  92. mHeaderView.setHeaderState(HeaderView.STATE_REFRESHING);
  93. if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
  94. mFooterView.hide();
  95. }
  96. }
  97. resetHeader();
  98. }else if(getLastVisiblePosition() == mTotalNumber - 1){
  99. if(mFooterView.getFooterHeight() > iFooterHeight){
  100. mFooterView.setFooterState(FooterView.STATE_LOADING);
  101. }
  102. resetFooter();
  103. }
  104. break;
  105.  
  106. default:
  107. break;
  108. }
  109. return super.onTouchEvent(ev);
  110. }
  111.  
  112. @Override
  113. public void computeScroll() {
  114. if(mScroller.computeScrollOffset()){
  115. if(iScrollWhich == SCROLL_HEADER){
  116. mHeaderView.setHeaderHeight(mScroller.getCurrY());
  117. }else if(iScrollWhich == SCROLL_FOOTER){
  118. mFooterView.setFooterHeight(mScroller.getCurrY());
  119. }
  120. }
  121. super.computeScroll();
  122. }
  123.  
  124. /*
  125. * 获取ListView有多少个item:
  126. * 1. 在init中,需要设置super.setOnScrollListener;
  127. * 2. 重载以下两个函数;
  128. * 3. 在onScroll中取得totalItemCount即可;
  129. */
  130. @Override
  131. public void onScroll(AbsListView view, int firstVisibleItem,
  132. int visibleItemCount, int totalItemCount) {
  133. mTotalNumber = totalItemCount;
  134. }
  135. @Override
  136. public void onScrollStateChanged(AbsListView view, int scrollState) {
  137. }
  138. /////////////////////////////////////////////////////////////////////////////
  139. private boolean canHeaderPull(){
  140. if(mFooterView.getCurrentState() == FooterView.STATE_NORMAL){
  141. return true;
  142. }
  143. return false;
  144. }
  145.  
  146. private boolean canFooterPull(){
  147. if(mHeaderView.getCurrentState() == HeaderView.STATE_NORMAL){
  148. return true;
  149. }
  150. return false;
  151. }
  152. ///////////////////////////////////// Header ////////////////////////////////
  153. public void stopRefresh(){
  154. if(mHeaderView.getCurrentState() == HeaderView.STATE_REFRESHING){
  155. mHeaderView.setHeaderState(HeaderView.STATE_NORMAL);
  156. resetHeader();
  157. if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
  158. mFooterView.show();
  159. }
  160. }
  161. }
  162.  
  163. private void initHeaderView(Context context){
  164. mHeaderView = new HeaderView(context);
  165. mHeaderContent = (RelativeLayout) mHeaderView.findViewById(R.id.header_content);
  166. addHeaderView(mHeaderView);
  167. mHeaderView.getViewTreeObserver()
  168. .addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
  169. @Override
  170. public void onGlobalLayout() {
  171. iHeaderHeight = mHeaderContent.getHeight();
  172. Log.d(TAG, "iHeaderHeight = " + iHeaderHeight);
  173. getViewTreeObserver().removeGlobalOnLayoutListener(this);
  174. }
  175. });
  176. }
  177.  
  178. private void updateHeaderState(float delta){
  179. mHeaderView.setHeaderHeight((int)(delta + mHeaderView.getHeaderHeight()));
  180. if(mHeaderView.getCurrentState() != HeaderView.STATE_REFRESHING){
  181. if(mHeaderView.getHeaderHeight() > iHeaderHeight){
  182. mHeaderView.setHeaderState(HeaderView.STATE_WILL_RELEASE);
  183. }else{
  184. mHeaderView.setHeaderState(HeaderView.STATE_NORMAL);
  185. }
  186. }
  187. setSelection(0);
  188. }
  189.  
  190. private void resetHeader(){
  191. int height = mHeaderView.getHeaderHeight();
  192. if(height == 0){
  193. return;
  194. }
  195.  
  196. int finalHeight = 0;
  197. if(height > iHeaderHeight){
  198. /*
  199. * 如果超过HeaderView高度,则回滚到HeaderView高度即可
  200. */
  201. finalHeight = iHeaderHeight;
  202. }else if(mHeaderView.getCurrentState() == HeaderView.STATE_REFRESHING){
  203. /*
  204. * 如果HeaderView未完全显示
  205. * 1. 处于正在刷新中,则不管;
  206. * 2. 回滚HeaderView当前可视高度
  207. */
  208. return;
  209. }
  210.  
  211. iScrollWhich = SCROLL_HEADER;
  212. mScroller.startScroll(0, height, 0, finalHeight - height, 250);
  213. invalidate();
  214. }
  215. /////////////////////////////////////////////////////////////////////////////
  216. ///////////////////////////////////// Footer ////////////////////////////////
  217. public void setFooterMode(int options){
  218. mFooterView.setFooterViewOptions(options);
  219. }
  220.  
  221. public void stopLoad(){
  222. if(mFooterView.getCurrentState() == FooterView.STATE_LOADING){
  223. mFooterView.setFooterState(FooterView.STATE_NORMAL);
  224. resetFooter();
  225. }
  226. }
  227.  
  228. private void initFooterView(Context context){
  229. mFooterView = new FooterView(context);
  230. mFooterContent = (RelativeLayout) mFooterView.findViewById(R.id.footer_content);
  231. mFooterContent.setOnClickListener(new OnClickListener(){
  232. @Override
  233. public void onClick(View v) {
  234. if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK
  235. && mFooterView.getCurrentState() == FooterView.STATE_NORMAL){
  236. mFooterView.setFooterState(FooterView.STATE_LOADING);
  237. }
  238. }
  239. });
  240. mFooterView.getViewTreeObserver()
  241. .addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
  242. @Override
  243. public void onGlobalLayout() {
  244. iFooterHeight = mFooterContent.getHeight();
  245. Log.d(TAG, "iFooterHeight = " + iFooterHeight);
  246. getViewTreeObserver().removeGlobalOnLayoutListener(this);
  247. }
  248. });
  249. }
  250.  
  251. private void updateFooterState(float delta){
  252. if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
  253. return;
  254. }
  255.  
  256. mFooterView.setFooterHeight((int)(delta + mFooterView.getFooterHeight()));
  257. if(mFooterView.getCurrentState() != FooterView.STATE_LOADING){
  258. if(mFooterView.getFooterHeight() > iFooterHeight){
  259. mFooterView.setFooterState(FooterView.STATE_WILL_RELEASE);
  260. }else{
  261. mFooterView.setFooterState(FooterView.STATE_NORMAL);
  262. }
  263. }
  264. }
  265.  
  266. private void resetFooter(){
  267. int height = mFooterView.getFooterHeight();
  268. if(height == 0){
  269. return;
  270. }
  271.  
  272. if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
  273. return;
  274. }
  275.  
  276. int finalHeight = 0;
  277. if(height > iFooterHeight){
  278. finalHeight = iFooterHeight;
  279. }else if(mFooterView.getCurrentState() == FooterView.STATE_LOADING){
  280. return;
  281. }
  282.  
  283. iScrollWhich = SCROLL_FOOTER;
  284. mScroller.startScroll(0, height, 0, finalHeight - height, 250);
  285. invalidate();
  286. }
  287. /////////////////////////////////////////////////////////////////////////////
  288. }

代码结构比较清楚,相关的都集中在一起,大致流程是:

1. down时,记住坐标;

2. move时,判断当前可见是否是第一个或是最后一个,如果是,则将移动的距离去设置HeaderView或FooterView的高度,达到一点一点的显示出来;

3. up时,判断HeaderView或FooterView是否滚动的距离超过它们的高度,如果是,则表示是刷新或加载,且回弹到移动的距离-高度;

4. 代码还提供了冲突设置,即如果当前正在刷新中,则不允许滚动到底部上拉更多,或者显示“点击加载更多”,同样,如果是底部正在加载,则不允许滚动到顶多,下拉刷新;

2.4 使用举例

首页布局

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. tools:context=".MainActivity" >
  6.  
  7. <com.chris.list.refresh.ListViewExt
  8. android:id="@+id/listview"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent"
  11. android:divider="#000000"
  12. android:dividerHeight="0.5dip"/>
  13.  
  14. </RelativeLayout>

首页Activity代码实现,和一般的使用ListView方法一样

  1. package com.chris.list.refresh;
  2.  
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.View;
  6. import android.widget.AdapterView;
  7. import android.widget.AdapterView.OnItemClickListener;
  8. import android.widget.ArrayAdapter;
  9. import android.app.Activity;
  10.  
  11. public class MainActivity extends Activity {
  12.  
  13. private final static String TAG = "ChrisLV";
  14. private ListViewExt mListView = null;
  15. private String[] mList = {
  16. "abcd1", "abcd2", "abcd3", "abcd4", "abcd5", "abcd6",
  17. "abcd7", "abcd8", "abcd9", "abcd10", "abcd11", "abcd12",
  18. "abcd13", "abcd14", "abcd15", "abcd16", "abcd17", "abcd18",
  19. "abcd19", "abcd20", "abcd21", "abcd22", "abcd23", "abcd24"
  20. };
  21. @Override
  22. protected void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.activity_main);
  25.  
  26. mListView = (ListViewExt) findViewById(R.id.listview);
  27. mListView.setAdapter(new ArrayAdapter<String>(this,
  28. android.R.layout.simple_list_item_1,
  29. mList));
  30.  
  31. mListView.setOnItemClickListener(new OnItemClickListener(){
  32. @Override
  33. public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
  34. long arg3) {
  35. Log.d(TAG, "arg2 = " + arg2);
  36. if(arg2 > 0){
  37. mListView.stopRefresh();
  38. mListView.stopLoad();
  39. }
  40.  
  41. mListView.setFooterMode(arg2 % 2);
  42. }
  43. });
  44. }
  45.  
  46. }

在onItemClick中,只是做了简单的将HeaderView或FooterView停止,并设置FooterView的加载模式:是上拉更多,还是点击加载更多。

三、小结

本篇文章,大致就这么多,虽然,为了UI体验友好,花了很多精力,但是一通百通,其它的也不外乎是这些,所以大家学习后,希望能举一反三,同时,咱们也交流交流。

源码下载地址: http://download.csdn.net/detail/qingye_love/5597623

ListView下拉刷新及上拉更多两种状态的更多相关文章

  1. ListView下拉刷新、上拉载入更多之封装改进

    在Android中ListView下拉刷新.上拉载入更多示例一文中,Maxwin兄给出的控件比较强大,前面有详细介绍,但是有个不足就是,里面使用了一些资源文件,包括图片,String,layout,这 ...

  2. google官方的下拉刷新+自定义上拉加载更多

    转载请标注转载:http://blog.csdn.net/oqihaogongyuan/article/details/50949118 google官方的下拉刷新+自定义上拉加载更多 现在很多app ...

  3. Android打造(ListView、GridView等)通用的下拉刷新、上拉自动加载的组件

    原文 http://blog.csdn.net/bboyfeiyu/article/details/39253051       前言 下 拉刷新组件在开发中使用率是非常高的,基本上联网的APP都会采 ...

  4. Android XListView下拉刷新、上拉载入更多

    source code: https://github.com/Maxwin-z/XListView-Android 提供了两个接口: a) IXListViewListener:  触发下拉刷新.上 ...

  5. 【Android - 自定义View】之自定义可下拉刷新或上拉加载的ListView

    首先来介绍一下这个自定义View: (1)这个自定义View的名称叫做 RefreshableListView ,继承自ListView类: (2)在这个自定义View中,用户可以设置是否支持下拉刷新 ...

  6. 微信小程序开发之 下拉刷新,上拉加载更多

    本文记载了如何在微信小程序里面实现下拉刷新,上拉加载更多 先开看一下界面 大致如此的界面吧. 这个Demo使用了微信的几个Api和事件,我先列出来. 1.wx.request (获取远程服务器的数据, ...

  7. iOS开发 XML解析和下拉刷新,上拉加载更多

    iOS开发 XML解析和下拉刷新,上拉加载更多 1.XML格式 <?xml version="1.0" encoding="utf-8" ?> 表示 ...

  8. 【Web】移动端下拉刷新、上拉加载更多插件

    移动网站中常常有的功能:列表的下拉刷新.上拉加载更多 本例介绍一种简单使用的移动端下拉刷新.上拉加载更多插件,下载及参考地址:https://github.com/ximan/dropload 插件依 ...

  9. vue2.0 移动端,下拉刷新,上拉加载更多插件,修改版

    在[实现丰盛]的插件基础修改[vue2.0 移动端,下拉刷新,上拉加载更多 插件], 1.修改加载到尾页面,返回顶部刷新数据,无法继续加重下一页 2.修改加载完成文字提示 原文链接:http://ww ...

  10. vue+better-scroll 下拉刷新,上拉加载更多

    better-scroll 来做下拉刷新和 上拉加载 特别方便.  安装好vue脚手架和better-scroll 之后 直接复制粘贴就可以看到效果了 <template> <div ...

随机推荐

  1. visual studio 2013 使用IIS Express附加调试MVC5

    1.如何找到调试的站点的进程[由于图片无法上传,就不上传图片了] 2.vs运行的时候,在状态栏会存在一个IIS Express 进程,点击显示所有的应用程序,找到想要调试的程序的PID; 3.附加调试 ...

  2. cocos2d-x中的CCScrollView滑动体验不佳

    在最近的项目中,使用了Cocos2d-x (2.2.0版本)提供的CCScrollView来拖动一个比较大的画面,但是发现滑动体验非常不佳, 手指离开屏幕后,滑动没有惯性,一个不算太大的画面,要滑动好 ...

  3. 手动添加删除windows服务

    1.使用sc命令创建服务 命令格式如: sc create [service name] [binPath= ] <option1> <option2>... 比如: sc c ...

  4. ThinkPHP第十七天(隐藏index.php和简短路径配置)

    1.路由设置,让路径中不显示index.php方法: 第一步:在apache中的httpd.conf中查找: LoadModule rewrite_module modules/mod_rewrite ...

  5. 0520 python

    配置python环境变量我的电脑->右键->属性->高级系统设置->环境变量->(1)用户变量->新建 Path=C:\Python27(2)系统变量->编辑 ...

  6. Git使用方法记录(一)

    记录下git的基本使用方法,这里是以ubuntu14.04为例. 1,使用前的初始设置 git config –global user.name “FirstName LastName” git co ...

  7. LATEX使用之字体颜色深浅不一

    今天用Ctex写论文,发现出来的pdf在屏幕上会出现字体颜色深浅不一的现象. google一下之后,在饮水思源bbs上找到了解决方法,用latex+dvitopdf来编译就不会有这个现象了. 另外,对 ...

  8. selenium 学习笔记 ---新手学习记录(8) 问题总结(java)

    1.获取执行js代码后的返回值 //获取滚动距离 String jl="return $('#chapterul li').height();"; Long jlhq=(Long) ...

  9. ORA-20000: ORU-10027: buffer overflow, limit of 10000 bytes

        要用dbms_output.put_line来输出语句,遇到以下错误: ERROR 位于第 1 行: ORA-20000: ORU-10027: buffer overflow, limit ...

  10. curl 返回响应头

    demo:/root# curl -i baidu.com HTTP/1.1 200 OK Date: Wed, 27 Jul 2016 08:50:03 GMT Content-Type: text ...