依照这篇博文里的思路分析和理解的

先要理解Scroller,看过的博文:

http://ipjmc.iteye.com/blog/1615828

http://blog.csdn.net/wangjinyu501/article/details/32339379

还要理解View的touch时间传递:

http://www.codekk.com/open-source-project-analysis/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8BView%20%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92

在实现中遇到的问题:

1、下拉时,下拉区域不会尾随下拉而变化,仅仅显示当中一部分。

图:

解决:採用设置下拉区域的paddind,实现尾随滚动效果。终于图:

2、当下拉超过极限高度后向上滑动时。listview会尾随滑动。

解决方法是通过在onTouchEvent推断这一情况推断这一情况,具体在代码里。

代码:

下拉区域布局文件

  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:orientation="vertical" >
  6.  
  7. <RelativeLayout
  8. android:id="@+id/xlistview_header_content"
  9. android:layout_width="fill_parent"
  10. android:layout_height="60dp"
  11. android:layout_marginBottom="2dp"
  12. android:gravity="center_horizontal" >
  13.  
  14. <TextView
  15. android:id="@+id/xlistview_header_hint_textview"
  16. android:layout_width="100dp"
  17. android:layout_height="wrap_content"
  18. android:layout_centerInParent="true"
  19. android:gravity="center"
  20. android:text="正在载入"
  21. android:textColor="@android:color/black"
  22. android:textSize="14sp" />
  23.  
  24. <ImageView
  25. android:id="@+id/xlistview_header_image"
  26. android:layout_width="30dp"
  27. android:layout_height="wrap_content"
  28. android:layout_centerVertical="true"
  29. android:layout_toLeftOf="@id/xlistview_header_hint_textview"
  30. android:src="@drawable/indicator_arrow" />
  31.  
  32. <ProgressBar
  33. android:id="@+id/xlistview_header_progressbar"
  34. android:layout_width="30dp"
  35. android:layout_height="30dp"
  36. android:layout_centerVertical="true"
  37. android:layout_toLeftOf="@id/xlistview_header_hint_textview"
  38. android:visibility="invisible" />
  39. </RelativeLayout>
  40.  
  41. </LinearLayout>

下拉区域

  1. package com.example.test;
  2.  
  3. import android.annotation.SuppressLint;
  4. import android.content.Context;
  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 XListViewHeader extends LinearLayout {
  15.  
  16. private static final String HINT_NORMAL = "下拉刷新";
  17. private static final String HINT_READY = "松开刷新数据";
  18. private static final String HINT_LOADING = "正在载入...";
  19.  
  20. // 正常状态,下拉未超过head高度
  21. public final static int STATE_NORMAL = 0;
  22. // 准备刷新状态,也就是箭头方向发生改变之后的状态,可是没有刷新
  23. public final static int STATE_READY = 1;
  24. // 刷新状态。箭头变成了progressBar,正在刷新
  25. public final static int STATE_REFRESHING = 2;
  26. // 布局容器,也就是根布局
  27. private LinearLayout mContentLayout;
  28. // 箭头图片
  29. private ImageView mImageView;
  30. // 刷新状态显示
  31. private ProgressBar mProgressBar;
  32. // 说明文本
  33. private TextView mHintTextView;
  34. // 记录当前的状态
  35. private int mState = -1;
  36. // 用于改变箭头的方向的动画
  37. private Animation mRotateUpAnim;
  38. private Animation mRotateDownAnim;
  39. // 动画持续时间
  40. private final int ROTATE_ANIM_DURATION = 180;
  41.  
  42. private int headHeight;
  43. private Context context;
  44.  
  45. public XListViewHeader(Context context) {
  46. super(context);
  47. this.context = context;
  48. init();
  49. }
  50.  
  51. private void init() {
  52. LinearLayout.LayoutParams lp = new LayoutParams(
  53. LayoutParams.MATCH_PARENT, 0);// 初始化高度为0
  54. mContentLayout = (LinearLayout) LayoutInflater.from(context).inflate(
  55. R.layout.xlistview_header, null);
  56. mContentLayout.setLayoutParams(lp);
  57. addView(mContentLayout);
  58.  
  59. mImageView = (ImageView) mContentLayout
  60. .findViewById(R.id.xlistview_header_image);// 箭头图片
  61. mHintTextView = (TextView) mContentLayout
  62. .findViewById(R.id.xlistview_header_hint_textview);// 提示文本
  63. mProgressBar = (ProgressBar) mContentLayout
  64. .findViewById(R.id.xlistview_header_progressbar);// 进度条
  65. mRotateUpAnim = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF,
  66. 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);// 箭头向上旋转的动画
  67. mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);// 动画持续时间
  68. mRotateUpAnim.setFillAfter(true);// 动画终止时停留在最后,也就是保留动画以后的状态
  69. mRotateDownAnim = new RotateAnimation(-180, 0,
  70. Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
  71. 0.5f);
  72. mRotateDownAnim.setFillAfter(true);
  73. setState(STATE_NORMAL);// 初始化设置为正常模式
  74. }
  75.  
  76. public void setState(int state) {
  77. if (state == mState) {
  78. return;
  79. }
  80. if (state == STATE_REFRESHING) {// 设置为正在刷新状态时,清楚全部动画,箭头隐藏, 进度条显示
  81. mImageView.clearAnimation();
  82. mImageView.setVisibility(View.GONE);
  83. mProgressBar.setVisibility(View.VISIBLE);
  84. } else {
  85. mImageView.setVisibility(View.VISIBLE);
  86. mProgressBar.setVisibility(View.GONE);
  87. }
  88. switch (state) {
  89. case STATE_NORMAL:
  90. if (mState == STATE_READY) {// 由准备状态变为正常状态。开启向下动画
  91. mImageView.startAnimation(mRotateDownAnim);
  92. } else {
  93. mImageView.clearAnimation();
  94. }
  95. mHintTextView.setText(HINT_NORMAL);
  96. break;
  97. case STATE_READY:
  98. if (mState == STATE_NORMAL) {
  99. mImageView.startAnimation(mRotateUpAnim);
  100. }
  101. mHintTextView.setText(HINT_READY);
  102. break;
  103. case STATE_REFRESHING:
  104. mHintTextView.setText(HINT_LOADING);
  105. break;
  106. }
  107. mState = state;
  108. }
  109.  
  110. @SuppressLint("NewApi")
  111. public void setVisitHeight(int height) {
  112. if (height < 0) {
  113. height = 0;
  114. }
  115. LinearLayout.LayoutParams lp = (LayoutParams) mContentLayout
  116. .getLayoutParams();
  117. lp.height = height;
  118. mContentLayout.setLayoutParams(lp);
  119. mContentLayout.setPadding(mContentLayout.getPaddingLeft(), height
  120. - headHeight, mContentLayout.getPaddingRight(),
  121. mContentLayout.getPaddingBottom());// 设置padding是为了下拉时,head尾随着下拉。更好看
  122. }
  123.  
  124. public int getVisitHeight() {
  125. return mContentLayout.getHeight();
  126. }
  127.  
  128. public void show() {
  129. mContentLayout.setVisibility(View.VISIBLE);
  130. }
  131.  
  132. public void hide() {
  133. mContentLayout.setVisibility(View.INVISIBLE);
  134. }
  135.  
  136. public int getHeadHeight() {
  137. return headHeight;
  138. }
  139.  
  140. public void setHeadHeight(int headHeight) {
  141. this.headHeight = headHeight;
  142. }
  143.  
  144. }

listview

  1. package com.example.test;
  2.  
  3. import android.content.Context;
  4. import android.view.MotionEvent;
  5. import android.view.ViewTreeObserver.OnGlobalLayoutListener;
  6. import android.view.animation.DecelerateInterpolator;
  7. import android.widget.ListView;
  8. import android.widget.RelativeLayout;
  9. import android.widget.Scroller;
  10.  
  11. public class XListView extends ListView {
  12. private Context context;
  13. // 滑动时长
  14. private final static int SCROLL_DURATION = 400;
  15. // 滑动比例
  16. private final static float OFFSET_RADIO = 2f;
  17. // 记录按下点的y坐标
  18. private float lastY;
  19. // 用来回滚
  20. private Scroller scroller;
  21. private IXListViewListener mListViewListener;
  22. private XListViewHeader headerView;
  23. private RelativeLayout headerViewContent;
  24. // header的高度
  25. private int headerHeight;
  26. // 是否可以刷新
  27. private boolean enableRefresh = true;
  28. // 是否正在刷新
  29. private boolean isRefreashing = false;
  30. // 记录当前手势是向上还是向下
  31. private int TOUCH_UP = 0, TOUCH_DOWN = 1;
  32. private int mTouch;
  33.  
  34. public XListView(Context context) {
  35. super(context);
  36. this.context = context;
  37. init();
  38. }
  39.  
  40. private void init() {
  41. scroller = new Scroller(context, new DecelerateInterpolator());
  42. headerView = new XListViewHeader(context);
  43. headerViewContent = (RelativeLayout) headerView
  44. .findViewById(R.id.xlistview_header_content);
  45. // 获得head的高度
  46. headerView.getViewTreeObserver().addOnGlobalLayoutListener(
  47. new OnGlobalLayoutListener() {
  48. @SuppressWarnings("deprecation")
  49. @Override
  50. public void onGlobalLayout() {
  51. headerHeight = headerViewContent.getHeight();
  52. headerView.setHeadHeight(headerHeight);
  53. getViewTreeObserver()
  54. .removeGlobalOnLayoutListener(this);
  55. }
  56. });
  57. addHeaderView(headerView);
  58. }
  59.  
  60. @Override
  61. public boolean onTouchEvent(MotionEvent ev) {
  62. switch (ev.getAction()) {
  63. case MotionEvent.ACTION_DOWN:
  64. lastY = ev.getRawY();
  65. break;
  66. case MotionEvent.ACTION_MOVE:
  67. float t = ev.getRawY() - lastY;
  68. lastY = ev.getRawY();
  69. if (t > 0) {
  70. mTouch = TOUCH_DOWN;
  71. } else {
  72. mTouch = TOUCH_UP;
  73. }
  74. // 当前是第一个item,且手势是向下,就显示下拉条,更新高度
  75. if (getFirstVisiblePosition() == 0
  76. && (headerView.getVisitHeight() > 0 || t > 0)) {
  77. updateHeaderViewHeight(t / OFFSET_RADIO);
  78. }
  79. if (!isRefreashing && mTouch == TOUCH_UP
  80. && headerView.getVisitHeight() > 0) {
  81. return true;// 当下拉高度达到header高度时候,松开就可以刷新。若此刻向上滑,listview会尾随滑动,return
  82. // true 代表消费这个事件,listview禁止滚动
  83. }
  84. break;
  85. case MotionEvent.ACTION_UP:
  86. if (getFirstVisiblePosition() == 0) {
  87. if (enableRefresh && headerView.getVisitHeight() > headerHeight) {
  88. isRefreashing = true;
  89. headerView.setState(headerView.STATE_REFRESHING);
  90. if (mListViewListener != null) {
  91. mListViewListener.onRefresh();//刷新事件
  92. }
  93. }
  94. }
  95. resetHeaderHeight();
  96. break;
  97. }
  98. return super.onTouchEvent(ev);
  99. }
  100.  
  101. public void updateHeaderViewHeight(float f) {
  102. headerView.setVisitHeight((int) f + headerView.getVisitHeight());
  103. // 未处于刷新状态,更新箭头
  104. if (enableRefresh && !isRefreashing) {
  105. if (headerView.getVisitHeight() > headerHeight) {
  106. headerView.setState(XListViewHeader.STATE_READY);
  107. }else{
  108. headerView.setState(XListViewHeader.STATE_NORMAL);
  109. }
  110. }
  111. }
  112.  
  113. // 下拉条动态消失
  114. public void resetHeaderHeight() {
  115. int height = headerView.getVisitHeight();
  116. int endheight = 0;
  117. if (isRefreashing) {
  118. endheight = headerHeight;
  119. }
  120. // y轴方向由 height 到 endheight 。第三个參数是增量,假设不是刷新则高度变为0,假设是,高度变为head原始高度
  121. scroller.startScroll(0, height, 0, endheight - height, SCROLL_DURATION);
  122. invalidate();
  123. }
  124.  
  125. public void computeScroll() {
  126. if (scroller.computeScrollOffset()) {
  127. // 利用scroller 。设置高度。重复重绘
  128. headerView.setVisitHeight(scroller.getCurrY());
  129. postInvalidate();
  130. }
  131. super.computeScroll();
  132. }
  133.  
  134. public void stopRefresh() {
  135. if (isRefreashing == true) {
  136. isRefreashing = false;
  137. resetHeaderHeight();
  138. }
  139. }
  140.  
  141. public void setIxListener(IXListViewListener listener) {
  142. this.mListViewListener = listener;
  143. }
  144.  
  145. interface IXListViewListener {
  146. public void onRefresh();// 刷新事件的回调函数
  147. }
  148. }

mainactivity

  1. package com.example.test;
  2.  
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.os.Handler;
  6. import android.os.Message;
  7. import android.widget.ArrayAdapter;
  8.  
  9. import com.example.test.XListView.IXListViewListener;
  10.  
  11. public class MainActivity extends Activity {
  12. private XListView xListView;
  13. private Handler handler = new Handler() {
  14. public void handleMessage(Message msg) {
  15. xListView.stopRefresh();
  16. }
  17. };
  18.  
  19. protected void onCreate(Bundle savedInstanceState) {
  20. super.onCreate(savedInstanceState);
  21. xListView = new XListView(this);
  22. ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
  23. android.R.layout.simple_expandable_list_item_1);
  24. xListView.setAdapter(adapter);
  25. for (int i = 0; i < 10; i++) {
  26. adapter.add("text" + i);
  27. }
  28. setContentView(xListView);
  29. xListView.setIxListener(new IXListViewListener() {
  30. public void onRefresh() {
  31. new Thread() {
  32. public void run() {
  33. try {
  34. Thread.sleep(2000);
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. handler.sendEmptyMessage(0);
  39. }
  40. }.start();
  41. }
  42. });
  43. }
  44. }

下载

csdn博文编辑不能撤销么。写的东西都没了

下拉刷新XListView的简单分析的更多相关文章

  1. mui实现分页上拉加载更多 下拉刷新数据的简单实现 移动端下拉上拉

    空下来把mui上拉加载更多,下拉刷新数据做了一个简单的实现,希望可以帮助到需要的朋友 demo项目的结构 <!DOCTYPE html> <html> <head> ...

  2. IOS UIWebView 下拉刷新功能的简单实现

    1.运行效果图 2.swift 代码的实现 import UIKit class RefreshWebViewController: UIViewController,UIScrollViewDele ...

  3. 手把手教你轻松实现listview下拉刷新

    很多人觉得自定义一个listview下拉刷新上拉加载更多是一件很牛x的事情,不是大神写不出来,我想大多数童鞋都是做项目用到时就百度,什么pulltorefresh,xlistview...也不看原理, ...

  4. iOS下拉刷新和上拉刷新

    在iOS开发中,我们经常要用到下拉刷新和上拉刷新来加载新的数据,当前这也适合分页.iOS原生就带有该方法,下面就iOS自带的下拉刷新方法来简单操作. 上拉刷新 1.在TableView里,一打开软件, ...

  5. Android之XListView下拉刷新,更新网络美女图

    一.简介:   下拉刷新是一种特定的手动刷新交互,和其他的同类操作不同的地方在于它采用了更加直觉的下拉操作,所以它的交互足够清晰明显. 下拉刷新主要用在类似ListView这样的控件,设计下拉刷新有三 ...

  6. XListView下拉刷新和上拉加载更多详解

    转载本专栏每一篇博客请注明转载出处地址,尊重原创.博客链接地址:小杨的博客 http://blog.csdn.net/qq_32059827/article/details/53167655 市面上有 ...

  7. Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件

    前言: 忙完了结婚乐APP的开发,终于可以花一定的时间放在博客上了.好了,废话不多说,今天我们要带来的效果是苹果版本的QQ下拉刷新.首先看一下目标效果以及demo效果:      因为此效果实现的步骤 ...

  8. 下拉刷新和UITableView的section headerView冲突的原因分析与解决方案

    UITableView:下拉刷新和上拉加载更多 [转载请注明出处] 本文将说明具有多个section的UITableView在使用下拉刷新机制时会遇到的问题及其解决方案. 工程地址在帖子最下方,只需要 ...

  9. 简单的下拉刷新以及优化--SwipeRefreshLayout

    代码工程简要说明:以一个SwipeRefreshLayout包裹ListView,SwipeRefreshLayout接管ListView的下拉事件,若ListView被用户触发下拉动作后,Swipe ...

随机推荐

  1. Zabbix监控JVM内存

    上篇最后提到了jstat,jstat可以查看统计JVM内存信息,那么结合Zabbix,就可以监控多实例的JVM内存了. 1.下面两个脚本部署在被监控主机: vm.py 用于JVM实例PID查找,ps命 ...

  2. Namespace declaration statement has to be the very first statement or after any declare call in the script

    0x00缘起 代码部署在windows上,出现了一个bug,临时用记事本打开修改了一下,于是出现了500错误 0x01排错 查看log,提示如下 "Namespace declaration ...

  3. jieba user guide

    import sysimport jiebaimport jieba.analyseimport jieba.posseg as posg sentence=u'''深圳新闻网讯 10月30日,世界城 ...

  4. [转]JSTL 与 JSP 或者 Java 相互传递变量的代码

    原文地址:http://blog.csdn.net/joyous/article/details/6689861 两种方式 <c:set var="s1" value=&qu ...

  5. <[成长股基本面]【怎样选择成长股】>读书笔记

    书在这里 投资想赚大钱,必须有耐性 这家公司的产品或服务有没有充分的市场潜力,至少几年内营业额能否大幅成长? 为了进一步提高总体销售水平,发现新的产品增长点,管理层是不是决心继续开发新产品或新工艺? ...

  6. virtualbox ubuntu 虚拟画面卡顿问题

    要在虚拟机全局配置里面添加选项:

  7. iis7.5 发布mvc出错的解决办法

    发布mvc,配置iis7.5时,遇到这个错误. xxxx'System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b ...

  8. MVC个人网站开发笔记-150302

    上传图片 参考这篇文章:http://www.cnblogs.com/kissdodog/archive/2012/12/15/2819025.html 调用ajaxFileUpload,控制器里面编 ...

  9. UI设计 - 首页(主页)的任务

    什么是首页 首页,又可以叫主页,是我们的网站或者APP的主要页面,它是我们接触的第一个页面(如果不包含闪屏页和登陆页的话). 特点 首页是一个开始的地方,我们开始真正接触网站提供给我们的内容. 首页是 ...

  10. ashx session 赋值 获取

    ashx想获取session值 需要继承 IRequiresSessionState接口 ExcelHelper : IHttpHandler, IRequiresSessionState publi ...