Android ListView下拉刷新

短命女 2022-07-20 15:12 298阅读 0赞

ListView的重要性我就不多说了,下拉刷新的功能以前一直用的别人的,这两天参考一些资料自己写了个ListView下拉刷新的控件,自己会写才能掌握在自己手里,以后扩展什么的,修改起来也方便点,有兴趣的可以看下我写的代码,虽然有点乱……
PS:请原谅一个英语渣的命名

效果图:
20160708110522872

代码:

RefreshLinearLayout :

  1. package wkk.refresh;
  2. import android.content.Context;
  3. import android.support.v4.widget.ScrollerCompat;
  4. import android.util.AttributeSet;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.animation.RotateAnimation;
  8. import android.widget.ImageView;
  9. import android.widget.LinearLayout;
  10. import android.widget.TextView;
  11. /**
  12. * Created by wkk on 2016/7/5.
  13. */
  14. public class RefreshLinearLayout extends LinearLayout {
  15. private View header;
  16. private RefreshListView listView;
  17. private int h;
  18. private ScrollerCompat scrollerCompat;
  19. private boolean isFirst = true;
  20. public RefreshLinearLayout(Context context, AttributeSet attrs) {
  21. super(context, attrs);
  22. setOrientation(LinearLayout.VERTICAL);
  23. scrollerCompat = ScrollerCompat.create(context);
  24. header = LayoutInflater.from(getContext()).inflate(R.layout.view_refresh_linear_layout_header, null);
  25. addView(header);
  26. }
  27. @Override
  28. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  29. super.onLayout(changed, l, t, r, b);
  30. if (isFirst) {
  31. int childcount = getChildCount();
  32. for (int i = 0; i < childcount; i++) {
  33. View view = getChildAt(i);
  34. if (view instanceof RefreshListView) {
  35. listView = (RefreshListView) view;
  36. }
  37. }
  38. h = header.getMeasuredHeight();
  39. setPaddingTop(-h);
  40. listView.setRefreshLinearLayout(this);
  41. imageView = (ImageView) header.findViewById(R.id.image);
  42. text = (TextView) header.findViewById(R.id.text);
  43. isFirst = false;
  44. }
  45. }
  46. private ImageView imageView;
  47. private TextView text;
  48. /**
  49. * 设置提示下拉刷新时的状态
  50. */
  51. public void setXLSH() {
  52. text.setText("下拉刷新");
  53. imageView.clearAnimation();
  54. RotateAnimation rotateAnimation1 = new RotateAnimation(180, 0,
  55. RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  56. rotateAnimation1.setDuration(100);
  57. rotateAnimation1.setFillAfter(true);
  58. imageView.setAnimation(rotateAnimation1);
  59. }
  60. /**
  61. * 设置提示松开刷新时的状态
  62. */
  63. public void setSKSX() {
  64. text.setText("松开刷新");
  65. imageView.clearAnimation();
  66. RotateAnimation rotateAnimation = new RotateAnimation(0, 180,
  67. RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  68. rotateAnimation.setDuration(100);
  69. rotateAnimation.setFillAfter(true);
  70. imageView.setAnimation(rotateAnimation);
  71. }
  72. /**
  73. * 设置刷新完成时的状态
  74. */
  75. public void setzhengchang() {
  76. text.setText("下拉刷新");
  77. imageView.clearAnimation();
  78. }
  79. /**
  80. * 滑动
  81. *
  82. * @param sy 开始的位置
  83. * @param ey 偏移量
  84. */
  85. public void sc(int sy, int ey) {
  86. scrollerCompat.startScroll(0, sy, 0, ey, 350);
  87. postInvalidate();
  88. }
  89. @Override
  90. public void computeScroll() {
  91. super.computeScroll();
  92. if (scrollerCompat.computeScrollOffset()) {
  93. //scrollerCompat.getCurrX() 获取当前x轴的滑动位置
  94. setPaddingTop(scrollerCompat.getCurrY());
  95. }
  96. }
  97. public int getH() {
  98. return h;
  99. }
  100. /**
  101. * 设置此线性布局的paddingTop
  102. *
  103. * @param paddingTop
  104. */
  105. public void setPaddingTop(int paddingTop) {
  106. setPadding(getPaddingLeft(), paddingTop, getPaddingRight(), getPaddingBottom());
  107. postInvalidate();
  108. }
  109. }

RefreshListView :

  1. package wkk.refresh;
  2. import android.content.Context;
  3. import android.os.Handler;
  4. import android.os.Message;
  5. import android.support.v4.widget.ScrollerCompat;
  6. import android.util.AttributeSet;
  7. import android.view.LayoutInflater;
  8. import android.view.MotionEvent;
  9. import android.view.View;
  10. import android.view.ViewGroup;
  11. import android.widget.AbsListView;
  12. import android.widget.ListView;
  13. /**
  14. * Created by wkk on 2016/7/5.
  15. */
  16. public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
  17. //包裹listview的布局
  18. private RefreshLinearLayout refreshLinearLayout;
  19. //当前第一个可见item
  20. private int firstVisibleItem;
  21. //listview的头部布局
  22. private View header;
  23. //按下时的y轴坐标
  24. private float yDown;
  25. //按下时的x轴坐标
  26. private float xDown;
  27. //当前刷新状态
  28. private static int state;
  29. //正常状态
  30. private final static int NULL = 0;
  31. //提示下拉刷新
  32. private final static int XLSH = 1;
  33. //提示松开刷新
  34. private final static int SKSX = 2;
  35. //刷新中
  36. private final static int SXZ = 3;
  37. //当前操作是在x轴上进行还是在y轴上进行
  38. private static int xy;
  39. //在x轴上的操作不去处理
  40. private final static int x = 4;
  41. //处理在y轴上的操作
  42. private final static int y = 5;
  43. //当前移动的y轴距离
  44. private int nowMoveY;
  45. //最小移动距离
  46. private int minMove = 3;
  47. //父类的headerView高度
  48. private int h;
  49. //此ListView头部布局的高度
  50. private int thisHeaderHeight;
  51. //用于传递刷新时间的handler
  52. private Handler handler;
  53. //传递给外部的message的what
  54. public static int REFRESH_WHAT = 123;
  55. private ScrollerCompat scrollerCompat;
  56. //处理刷新时的操作
  57. private int SX = -1;
  58. //移动时x坐标
  59. private float xEvent;
  60. //x轴移动距离
  61. private float x1;
  62. public RefreshListView(Context context, AttributeSet attrs) {
  63. super(context, attrs);
  64. //设置滚动监听
  65. setOnScrollListener(this);
  66. //屏蔽魅族手机的下拉悬停
  67. setOverScrollMode(ListView.OVER_SCROLL_NEVER);
  68. //初始化状态
  69. state = NULL;
  70. xy = NULL;
  71. minMove = dp2px(context, minMove);
  72. scrollerCompat = ScrollerCompat.create(context);
  73. //添加并隐藏头部布局
  74. header = LayoutInflater.from(context).inflate(R.layout.view_refresh_listview_header, null);
  75. addHeaderView(header, null, false);
  76. measureView(header);
  77. thisHeaderHeight = header.getMeasuredHeight();
  78. setThisHeaderPaddingTop(-thisHeaderHeight);
  79. }
  80. /**
  81. * 设置头部布局
  82. *
  83. * @param view
  84. */
  85. private void measureView(View view) {
  86. ViewGroup.LayoutParams p = view.getLayoutParams();
  87. if (p == null) {
  88. p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
  89. }
  90. int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
  91. int height;
  92. int tempHeight = p.height;
  93. if (tempHeight > 0) {
  94. height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
  95. } else {
  96. height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
  97. }
  98. view.measure(width, height);
  99. }
  100. @Override
  101. public boolean onTouchEvent(MotionEvent ev) {
  102. if (firstVisibleItem != 0) {
  103. xy = x;
  104. if (ev.getAction() == MotionEvent.ACTION_UP) xy = NULL;
  105. return super.onTouchEvent(ev);
  106. }
  107. switch (ev.getAction()) {
  108. case MotionEvent.ACTION_DOWN:
  109. //回头此处加判断
  110. yDown = ev.getRawY();
  111. xDown = ev.getRawX();
  112. break;
  113. case MotionEvent.ACTION_MOVE:
  114. xEvent = ev.getRawX();
  115. x1 = Math.abs(xEvent - xDown);
  116. switch (xy) {
  117. case NULL:
  118. int dy = (int) Math.abs(ev.getRawY() - yDown);
  119. if (dy > minMove) {
  120. xy = y;
  121. } else if (x1 > minMove) {
  122. xy = x;
  123. }
  124. break;
  125. case x:
  126. super.onTouchEvent(ev);
  127. return true;
  128. case y:
  129. return move_Y(ev);
  130. }
  131. break;
  132. case MotionEvent.ACTION_UP:
  133. if (xy == y) {
  134. boolean rt = up(ev);
  135. xy = NULL;
  136. return rt;
  137. } else
  138. xy = NULL;
  139. break;
  140. }
  141. return super.onTouchEvent(ev);
  142. }
  143. /**
  144. * 处理xy为y时的up事件
  145. *
  146. * @param ev
  147. */
  148. private boolean up(MotionEvent ev) {
  149. switch (state) {
  150. case NULL:
  151. break;
  152. case XLSH:
  153. //从当前位置 滑动到-h
  154. refreshLinearLayout.sc(nowMoveY, h - nowMoveY - h - h);
  155. state = NULL;
  156. break;
  157. case SKSX:
  158. setThisHeaderPaddingTop(nowMoveY);
  159. refreshLinearLayout.setPaddingTop(-h);
  160. scrollerCompat.startScroll(0, nowMoveY, 0, -nowMoveY, 350);
  161. state = SXZ;
  162. if (handler != null) {
  163. //提示外部执行刷新
  164. Message msg = handler.obtainMessage();
  165. msg.what = REFRESH_WHAT;
  166. handler.sendMessage(msg);
  167. }
  168. break;
  169. case SXZ:
  170. if (SX == y) {
  171. scrollerCompat.startScroll(0, nowMoveY, 0, -nowMoveY, 350);
  172. postInvalidate();
  173. ev.setAction(MotionEvent.ACTION_CANCEL);
  174. super.onTouchEvent(ev);
  175. } else {
  176. super.onTouchEvent(ev);
  177. }
  178. SX = -1;
  179. return true;
  180. }
  181. return true;
  182. }
  183. /**
  184. * 处理xy为y时的move事件
  185. *
  186. * @param ev
  187. * @return
  188. */
  189. private boolean move_Y(MotionEvent ev) {
  190. float yEvent = ev.getRawY();
  191. nowMoveY = (int) (yEvent - yDown);
  192. switch (state) {
  193. case NULL:
  194. if (nowMoveY > minMove) {
  195. state = XLSH;
  196. xy = y;
  197. } else {
  198. xy = x;
  199. }
  200. return super.onTouchEvent(ev);
  201. case XLSH:
  202. nowMoveY = nowMoveY / 3 - h;
  203. if (nowMoveY <= -h) {
  204. state = NULL;
  205. nowMoveY = -h;
  206. }
  207. if (nowMoveY > 0) {
  208. state = SKSX;
  209. refreshLinearLayout.setSKSX();
  210. }
  211. refreshLinearLayout.setPaddingTop(nowMoveY);
  212. ev.setAction(MotionEvent.ACTION_CANCEL);
  213. super.onTouchEvent(ev);
  214. return true;
  215. case SKSX:
  216. nowMoveY = nowMoveY / 3 - h;
  217. if (nowMoveY < 0) {
  218. state = XLSH;
  219. refreshLinearLayout.setXLSH();
  220. }
  221. if (nowMoveY > refreshLinearLayout.getHeight() / 2 - h) {
  222. nowMoveY = refreshLinearLayout.getHeight() / 2 - h;
  223. }
  224. refreshLinearLayout.setPaddingTop(nowMoveY);
  225. break;
  226. case SXZ:
  227. if (SX == -1) {
  228. xEvent = ev.getRawX();
  229. x1 = Math.abs(xEvent - xDown);
  230. int dy = (int) (ev.getRawY() - yDown);
  231. if (dy > minMove) {
  232. SX = y;
  233. refreshSX_y(ev);
  234. } else if (x1 > minMove) {
  235. SX = x;
  236. super.onTouchEvent(ev);
  237. }
  238. } else if (SX == y) {
  239. refreshSX_y(ev);
  240. } else if (SX == x) {
  241. super.onTouchEvent(ev);
  242. }
  243. return true;
  244. }
  245. return true;
  246. }
  247. private void refreshSX_y(MotionEvent ev) {
  248. nowMoveY = nowMoveY / 3;
  249. if (nowMoveY < -h) {
  250. nowMoveY = -h;
  251. }
  252. if (nowMoveY > refreshLinearLayout.getHeight() / 2 - h) {
  253. nowMoveY = refreshLinearLayout.getHeight() / 2 - h;
  254. }
  255. setThisHeaderPaddingTop(nowMoveY);
  256. ev.setAction(MotionEvent.ACTION_CANCEL);
  257. super.onTouchEvent(ev);
  258. }
  259. public void setRefreshLinearLayout(RefreshLinearLayout refreshLinearLayout) {
  260. this.refreshLinearLayout = refreshLinearLayout;
  261. h = refreshLinearLayout.getH();
  262. }
  263. /**
  264. * 刷新完成
  265. */
  266. public void refreshOver() {
  267. refreshLinearLayout.setzhengchang();
  268. scrollerCompat.startScroll(0, 0, 0, -thisHeaderHeight, 350);
  269. xy = NULL;
  270. state = NULL;
  271. SX = -1;
  272. postInvalidate();
  273. }
  274. @Override
  275. public void computeScroll() {
  276. super.computeScroll();
  277. if (scrollerCompat.computeScrollOffset()) {
  278. setThisHeaderPaddingTop(scrollerCompat.getCurrY());
  279. }
  280. }
  281. /**
  282. * 设置listview头部布局的paddingTop
  283. *
  284. * @param paddingTop
  285. */
  286. public void setThisHeaderPaddingTop(int paddingTop) {
  287. header.setPadding(header.getPaddingLeft(), paddingTop, header.getPaddingRight(), header.getPaddingBottom());
  288. postInvalidate();
  289. }
  290. /**
  291. * 设置handler以及what
  292. * 相较于用接口传递 在此时我更喜欢用handler传递
  293. *
  294. * @param handler
  295. * @param what
  296. */
  297. public void setRefreshHandler(Handler handler, int what) {
  298. this.handler = handler;
  299. REFRESH_WHAT = what;
  300. }
  301. @Override
  302. public void onScrollStateChanged(AbsListView view, int scrollState) {
  303. }
  304. @Override
  305. public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  306. this.firstVisibleItem = firstVisibleItem;
  307. }
  308. /**
  309. * 单位转换
  310. */
  311. public static int dp2px(Context context, int dp) {
  312. float scale = context.getResources().getDisplayMetrics().density;
  313. return (int) (dp * scale + 0.5f);
  314. }
  315. }

view_refresh_linear_layout_header.xml :

  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="center">
  6. <ImageView
  7. android:id="@+id/image"
  8. android:layout_width="wrap_content"
  9. android:layout_height="45dp"
  10. android:background="@drawable/xiala" />
  11. <TextView
  12. android:id="@+id/text"
  13. android:layout_width="wrap_content"
  14. android:layout_height="70dp"
  15. android:layout_marginLeft="15dp"
  16. android:gravity="center"
  17. android:text="下拉可刷新" />
  18. </LinearLayout>

view_refresh_listview_header.xml :

  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="center">
  6. <ProgressBar
  7. android:layout_width="40dp"
  8. android:layout_height="45dp" />
  9. <TextView
  10. android:id="@+id/text"
  11. android:layout_width="wrap_content"
  12. android:layout_height="70dp"
  13. android:layout_marginLeft="10dp"
  14. android:gravity="center"
  15. android:text="正在刷新中" />
  16. </LinearLayout>

使用方法:
RefreshLinearLayout 中包裹RefreshListView
设置handle监听刷新
调用refreshOver()方法结束刷新

Demo下载地址:
http://download.csdn.net/detail/w18756901575/9570607

PS:
你下载的是一个Project而不是module
这个例子里面忘记在结束刷新的方法中添加一句:
postInvalidate();
所以请使用时自行添加


2016/9/18
今天来了兴致,想要去将上拉加载的也写下,将代码给完善下,但是,,发现设置padding的方式貌似写不了上拉加载,设置了paddingbottom,却不会将listview顶上去,也就是滑倒最底部,上拉加载padding的时候,不会将listview的最后一行顶上去

发表评论

表情:
评论列表 (有 0 条评论,298人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android ListView刷新

    ListView的重要性我就不多说了,下拉刷新的功能以前一直用的别人的,这两天参考一些资料自己写了个ListView下拉刷新的控件,自己会写才能掌握在自己手里,以后扩展什么的,