【转载】Android中ListView下拉刷新的实现

时间:2021-03-02 15:24:50

在网上看到一个下拉刷新的例子,很的很棒,转载和更多的人分享学习

原文:http://blog.csdn.net/loongggdroid/article/details/9385535

【转载】Android中ListView下拉刷新的实现

ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理一下,供大家参考。那我就不解释,直接上代码了。

这里需要自己重写一下ListView,重写代码如下:

  1. package net.loonggg.listview;
  2. import java.util.Date;
  3. import android.content.Context;
  4. import android.util.AttributeSet;
  5. import android.view.LayoutInflater;
  6. import android.view.MotionEvent;
  7. import android.view.View;
  8. import android.view.ViewGroup;
  9. import android.view.animation.LinearInterpolator;
  10. import android.view.animation.RotateAnimation;
  11. import android.widget.AbsListView;
  12. import android.widget.AbsListView.OnScrollListener;
  13. import android.widget.ImageView;
  14. import android.widget.LinearLayout;
  15. import android.widget.ListView;
  16. import android.widget.ProgressBar;
  17. import android.widget.TextView;
  18. public class MyListView extends ListView implements OnScrollListener {
  19. private final static int RELEASE_To_REFRESH = 0;// 下拉过程的状态值
  20. private final static int PULL_To_REFRESH = 1; // 从下拉返回到不刷新的状态值
  21. private final static int REFRESHING = 2;// 正在刷新的状态值
  22. private final static int DONE = 3;
  23. private final static int LOADING = 4;
  24. // 实际的padding的距离与界面上偏移距离的比例
  25. private final static int RATIO = 3;
  26. private LayoutInflater inflater;
  27. // ListView头部下拉刷新的布局
  28. private LinearLayout headerView;
  29. private TextView lvHeaderTipsTv;
  30. private TextView lvHeaderLastUpdatedTv;
  31. private ImageView lvHeaderArrowIv;
  32. private ProgressBar lvHeaderProgressBar;
  33. // 定义头部下拉刷新的布局的高度
  34. private int headerContentHeight;
  35. private RotateAnimation animation;
  36. private RotateAnimation reverseAnimation;
  37. private int startY;
  38. private int state;
  39. private boolean isBack;
  40. // 用于保证startY的值在一个完整的touch事件中只被记录一次
  41. private boolean isRecored;
  42. private OnRefreshListener refreshListener;
  43. private boolean isRefreshable;
  44. public MyListView(Context context) {
  45. super(context);
  46. init(context);
  47. }
  48. public MyListView(Context context, AttributeSet attrs) {
  49. super(context, attrs);
  50. init(context);
  51. }
  52. private void init(Context context) {
  53. setCacheColorHint(context.getResources().getColor(R.color.transparent));
  54. inflater = LayoutInflater.from(context);
  55. headerView = (LinearLayout) inflater.inflate(R.layout.lv_header, null);
  56. lvHeaderTipsTv = (TextView) headerView
  57. .findViewById(R.id.lvHeaderTipsTv);
  58. lvHeaderLastUpdatedTv = (TextView) headerView
  59. .findViewById(R.id.lvHeaderLastUpdatedTv);
  60. lvHeaderArrowIv = (ImageView) headerView
  61. .findViewById(R.id.lvHeaderArrowIv);
  62. // 设置下拉刷新图标的最小高度和宽度
  63. lvHeaderArrowIv.setMinimumWidth(70);
  64. lvHeaderArrowIv.setMinimumHeight(50);
  65. lvHeaderProgressBar = (ProgressBar) headerView
  66. .findViewById(R.id.lvHeaderProgressBar);
  67. measureView(headerView);
  68. headerContentHeight = headerView.getMeasuredHeight();
  69. // 设置内边距,正好距离顶部为一个负的整个布局的高度,正好把头部隐藏
  70. headerView.setPadding(0, -1 * headerContentHeight, 0, 0);
  71. // 重绘一下
  72. headerView.invalidate();
  73. // 将下拉刷新的布局加入ListView的顶部
  74. addHeaderView(headerView, null, false);
  75. // 设置滚动监听事件
  76. setOnScrollListener(this);
  77. // 设置旋转动画事件
  78. animation = new RotateAnimation(0, -180,
  79. RotateAnimation.RELATIVE_TO_SELF, 0.5f,
  80. RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  81. animation.setInterpolator(new LinearInterpolator());
  82. animation.setDuration(250);
  83. animation.setFillAfter(true);
  84. reverseAnimation = new RotateAnimation(-180, 0,
  85. RotateAnimation.RELATIVE_TO_SELF, 0.5f,
  86. RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  87. reverseAnimation.setInterpolator(new LinearInterpolator());
  88. reverseAnimation.setDuration(200);
  89. reverseAnimation.setFillAfter(true);
  90. // 一开始的状态就是下拉刷新完的状态,所以为DONE
  91. state = DONE;
  92. // 是否正在刷新
  93. isRefreshable = false;
  94. }
  95. @Override
  96. public void onScrollStateChanged(AbsListView view, int scrollState) {
  97. }
  98. @Override
  99. public void onScroll(AbsListView view, int firstVisibleItem,
  100. int visibleItemCount, int totalItemCount) {
  101. if (firstVisibleItem == 0) {
  102. isRefreshable = true;
  103. } else {
  104. isRefreshable = false;
  105. }
  106. }
  107. @Override
  108. public boolean onTouchEvent(MotionEvent ev) {
  109. if (isRefreshable) {
  110. switch (ev.getAction()) {
  111. case MotionEvent.ACTION_DOWN:
  112. if (!isRecored) {
  113. isRecored = true;
  114. startY = (int) ev.getY();// 手指按下时记录当前位置
  115. }
  116. break;
  117. case MotionEvent.ACTION_UP:
  118. if (state != REFRESHING && state != LOADING) {
  119. if (state == PULL_To_REFRESH) {
  120. state = DONE;
  121. changeHeaderViewByState();
  122. }
  123. if (state == RELEASE_To_REFRESH) {
  124. state = REFRESHING;
  125. changeHeaderViewByState();
  126. onLvRefresh();
  127. }
  128. }
  129. isRecored = false;
  130. isBack = false;
  131. break;
  132. case MotionEvent.ACTION_MOVE:
  133. int tempY = (int) ev.getY();
  134. if (!isRecored) {
  135. isRecored = true;
  136. startY = tempY;
  137. }
  138. if (state != REFRESHING && isRecored && state != LOADING) {
  139. // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
  140. // 可以松手去刷新了
  141. if (state == RELEASE_To_REFRESH) {
  142. setSelection(0);
  143. // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
  144. if (((tempY - startY) / RATIO < headerContentHeight)// 由松开刷新状态转变到下拉刷新状态
  145. && (tempY - startY) > 0) {
  146. state = PULL_To_REFRESH;
  147. changeHeaderViewByState();
  148. }
  149. // 一下子推到顶了
  150. else if (tempY - startY <= 0) {// 由松开刷新状态转变到done状态
  151. state = DONE;
  152. changeHeaderViewByState();
  153. }
  154. }
  155. // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
  156. if (state == PULL_To_REFRESH) {
  157. setSelection(0);
  158. // 下拉到可以进入RELEASE_TO_REFRESH的状态
  159. if ((tempY - startY) / RATIO >= headerContentHeight) {// 由done或者下拉刷新状态转变到松开刷新
  160. state = RELEASE_To_REFRESH;
  161. isBack = true;
  162. changeHeaderViewByState();
  163. }
  164. // 上推到顶了
  165. else if (tempY - startY <= 0) {// 由DOne或者下拉刷新状态转变到done状态
  166. state = DONE;
  167. changeHeaderViewByState();
  168. }
  169. }
  170. // done状态下
  171. if (state == DONE) {
  172. if (tempY - startY > 0) {
  173. state = PULL_To_REFRESH;
  174. changeHeaderViewByState();
  175. }
  176. }
  177. // 更新headView的size
  178. if (state == PULL_To_REFRESH) {
  179. headerView.setPadding(0, -1 * headerContentHeight
  180. + (tempY - startY) / RATIO, 0, 0);
  181. }
  182. // 更新headView的paddingTop
  183. if (state == RELEASE_To_REFRESH) {
  184. headerView.setPadding(0, (tempY - startY) / RATIO
  185. - headerContentHeight, 0, 0);
  186. }
  187. }
  188. break;
  189. default:
  190. break;
  191. }
  192. }
  193. return super.onTouchEvent(ev);
  194. }
  195. // 当状态改变时候,调用该方法,以更新界面
  196. private void changeHeaderViewByState() {
  197. switch (state) {
  198. case RELEASE_To_REFRESH:
  199. lvHeaderArrowIv.setVisibility(View.VISIBLE);
  200. lvHeaderProgressBar.setVisibility(View.GONE);
  201. lvHeaderTipsTv.setVisibility(View.VISIBLE);
  202. lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
  203. lvHeaderArrowIv.clearAnimation();// 清除动画
  204. lvHeaderArrowIv.startAnimation(animation);// 开始动画效果
  205. lvHeaderTipsTv.setText("松开刷新");
  206. break;
  207. case PULL_To_REFRESH:
  208. lvHeaderProgressBar.setVisibility(View.GONE);
  209. lvHeaderTipsTv.setVisibility(View.VISIBLE);
  210. lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
  211. lvHeaderArrowIv.clearAnimation();
  212. lvHeaderArrowIv.setVisibility(View.VISIBLE);
  213. // 是由RELEASE_To_REFRESH状态转变来的
  214. if (isBack) {
  215. isBack = false;
  216. lvHeaderArrowIv.clearAnimation();
  217. lvHeaderArrowIv.startAnimation(reverseAnimation);
  218. lvHeaderTipsTv.setText("下拉刷新");
  219. } else {
  220. lvHeaderTipsTv.setText("下拉刷新");
  221. }
  222. break;
  223. case REFRESHING:
  224. headerView.setPadding(0, 0, 0, 0);
  225. lvHeaderProgressBar.setVisibility(View.VISIBLE);
  226. lvHeaderArrowIv.clearAnimation();
  227. lvHeaderArrowIv.setVisibility(View.GONE);
  228. lvHeaderTipsTv.setText("正在刷新...");
  229. lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
  230. break;
  231. case DONE:
  232. headerView.setPadding(0, -1 * headerContentHeight, 0, 0);
  233. lvHeaderProgressBar.setVisibility(View.GONE);
  234. lvHeaderArrowIv.clearAnimation();
  235. lvHeaderArrowIv.setImageResource(R.drawable.arrow);
  236. lvHeaderTipsTv.setText("下拉刷新");
  237. lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
  238. break;
  239. }
  240. }
  241. // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height
  242. private void measureView(View child) {
  243. ViewGroup.LayoutParams params = child.getLayoutParams();
  244. if (params == null) {
  245. params = new ViewGroup.LayoutParams(
  246. ViewGroup.LayoutParams.FILL_PARENT,
  247. ViewGroup.LayoutParams.WRAP_CONTENT);
  248. }
  249. int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0,
  250. params.width);
  251. int lpHeight = params.height;
  252. int childHeightSpec;
  253. if (lpHeight > 0) {
  254. childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
  255. MeasureSpec.EXACTLY);
  256. } else {
  257. childHeightSpec = MeasureSpec.makeMeasureSpec(0,
  258. MeasureSpec.UNSPECIFIED);
  259. }
  260. child.measure(childWidthSpec, childHeightSpec);
  261. }
  262. public void setonRefreshListener(OnRefreshListener refreshListener) {
  263. this.refreshListener = refreshListener;
  264. isRefreshable = true;
  265. }
  266. public interface OnRefreshListener {
  267. public void onRefresh();
  268. }
  269. public void onRefreshComplete() {
  270. state = DONE;
  271. lvHeaderLastUpdatedTv.setText("最近更新:" + new Date().toLocaleString());
  272. changeHeaderViewByState();
  273. }
  274. private void onLvRefresh() {
  275. if (refreshListener != null) {
  276. refreshListener.onRefresh();
  277. }
  278. }
  279. public void setAdapter(LvAdapter adapter) {
  280. lvHeaderLastUpdatedTv.setText("最近更新:" + new Date().toLocaleString());
  281. super.setAdapter(adapter);
  282. }
  283. }

重写完ListView之后,在布局文件中是这么使用的,头部下拉刷新的布局文件lv_header.xml代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!-- ListView的头部 -->
  3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="fill_parent"
  5. android:layout_height="wrap_content"
  6. android:background="#000000" >
  7. <!-- 内容 -->
  8. <RelativeLayout
  9. android:id="@+id/head_contentLayout"
  10. android:layout_width="fill_parent"
  11. android:layout_height="wrap_content"
  12. android:paddingLeft="30dp" >
  13. <!-- 箭头图像、进度条 -->
  14. <FrameLayout
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:layout_alignParentLeft="true"
  18. android:layout_centerVertical="true" >
  19. <!-- 箭头 -->
  20. <ImageView
  21. android:id="@+id/lvHeaderArrowIv"
  22. android:layout_width="wrap_content"
  23. android:layout_height="wrap_content"
  24. android:layout_gravity="center"
  25. android:src="@drawable/arrow" />
  26. <!-- 进度条 -->
  27. <ProgressBar
  28. android:id="@+id/lvHeaderProgressBar"
  29. style="?android:attr/progressBarStyleSmall"
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. android:layout_gravity="center"
  33. android:visibility="gone" />
  34. </FrameLayout>
  35. <!-- 提示、最近更新 -->
  36. <LinearLayout
  37. android:layout_width="wrap_content"
  38. android:layout_height="wrap_content"
  39. android:layout_centerHorizontal="true"
  40. android:gravity="center_horizontal"
  41. android:orientation="vertical" >
  42. <!-- 提示 -->
  43. <TextView
  44. android:id="@+id/lvHeaderTipsTv"
  45. android:layout_width="wrap_content"
  46. android:layout_height="wrap_content"
  47. android:text="下拉刷新"
  48. android:textColor="@color/white"
  49. android:textSize="20sp" />
  50. <!-- 最近更新 -->
  51. <TextView
  52. android:id="@+id/lvHeaderLastUpdatedTv"
  53. android:layout_width="wrap_content"
  54. android:layout_height="wrap_content"
  55. android:text="上次更新"
  56. android:textColor="@color/gold"
  57. android:textSize="10sp" />
  58. </LinearLayout>
  59. </RelativeLayout>
  60. </LinearLayout>

在Main.xml中进行设置,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:background="#000000"
  6. android:orientation="vertical" >
  7. <net.loonggg.listview.MyListView
  8. android:id="@+id/lv"
  9. android:layout_width="fill_parent"
  10. android:layout_height="fill_parent" />
  11. </LinearLayout>

然后就是在MainActivity中实现,代码如下:

  1. package net.loonggg.listview;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import net.loonggg.listview.MyListView.OnRefreshListener;
  5. import android.app.Activity;
  6. import android.os.AsyncTask;
  7. import android.os.Bundle;
  8. import android.view.View;
  9. public class MainActivity extends Activity {
  10. private List<String> list;
  11. private MyListView lv;
  12. private LvAdapter adapter;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17. lv = (MyListView) findViewById(R.id.lv);
  18. list = new ArrayList<String>();
  19. list.add("loonggg");
  20. list.add("我们都是开发者");
  21. list.add("我们都是开发者");
  22. list.add("我们都是开发者");
  23. list.add("我们都是开发者");
  24. list.add("我们都是开发者");
  25. list.add("我们都是开发者");
  26. list.add("我们都是开发者");
  27. list.add("我们都是开发者");
  28. list.add("我们都是开发者");
  29. list.add("我们都是开发者");
  30. list.add("我们都是开发者");
  31. list.add("我们都是开发者");
  32. list.add("我们都是开发者");
  33. list.add("我们都是开发者");
  34. list.add("我们都是开发者");
  35. list.add("我们都是开发者");
  36. adapter = new LvAdapter(list, this);
  37. lv.setAdapter(adapter);
  38. lv.setonRefreshListener(new OnRefreshListener() {
  39. @Override
  40. public void onRefresh() {
  41. new AsyncTask<Void, Void, Void>() {
  42. protected Void doInBackground(Void... params) {
  43. try {
  44. Thread.sleep(1000);
  45. } catch (Exception e) {
  46. e.printStackTrace();
  47. }
  48. list.add("刷新后添加的内容");
  49. return null;
  50. }
  51. @Override
  52. protected void onPostExecute(Void result) {
  53. adapter.notifyDataSetChanged();
  54. lv.onRefreshComplete();
  55. }
  56. }.execute(null, null, null);
  57. }
  58. });
  59. }
  60. }

这里还需要为ListView设置一下Adapter,自定义的Adapter如下:

  1. package net.loonggg.listview;
  2. import java.util.List;
  3. import android.content.Context;
  4. import android.view.View;
  5. import android.view.ViewGroup;
  6. import android.widget.BaseAdapter;
  7. import android.widget.TextView;
  8. public class LvAdapter extends BaseAdapter {
  9. private List<String> list;
  10. private Context context;
  11. public LvAdapter(List<String> list, Context context) {
  12. this.list = list;
  13. this.context = context;
  14. }
  15. @Override
  16. public int getCount() {
  17. return list.size();
  18. }
  19. @Override
  20. public Object getItem(int position) {
  21. return list.get(position);
  22. }
  23. @Override
  24. public long getItemId(int position) {
  25. return position;
  26. }
  27. @Override
  28. public View getView(int position, View convertView, ViewGroup parent) {
  29. TextView tv = new TextView(context.getApplicationContext());
  30. tv.setText(list.get(position));
  31. return tv;
  32. }
  33. }

到这里就完了,代码中的解释非常详细,具体的我就不多说了,也不解释了,自己看看并研究吧!