不废话,代码里面注释很详细,直接上代码:

自定义的RefreshableListView代码:

 public class RefreshableListView extends ListView implements OnScrollListener {
private View header; // ListView顶部布局
private LayoutInflater inflater;
private int headerHeight; // 顶部布局Header的高度
private int firstVisisblePosition; // 当前第一个可见的Item的位置
private int scrollState; // ListView当前的滚动状态 private boolean remarkTop; // 标记,当前是在ListView的最顶端按下的
private int startY; // 手指按下时的Y值 private int state; // 指示当前的状态
private final int STATE_NORMAL = 0; // 正常状态
private final int STATE_PULL = 1; // 提示“下拉可以刷新”的状态
private final int STATE_TOREFRESH = 2; // 提示“松开手指刷新”的状态
private final int STATE_REFRESHING = 3; // 正在刷新的状态 // Header布局中的四个控件
private TextView refreshTip; // 显示“下拉可以刷新”/“松开手指刷新”的TextView
private TextView timeTip; // 显示上次刷新的时间的TextView
private ImageView arrowImg; // 向上/向下的箭头的ImageView
private ProgressBar progressBar; // 刷新数据时用到的ProgressBar private ListViewRefreshListener listener; // 刷新数据的接口 // 自定义控件都必须实现以下三个构造方法(一个参数、两个参数、三个参数的构造方法)
// 我们在一个参数的构造方法中调用两个参数的构造方法,在两个参数的构造方法中调用三个参数的构造方法,这样不管我们用哪个构造方法,最终的调用代码是一样的
// 一个参数的构造方法:这个方法是在Activity中根据上下文环境直接生成控件时调用的
public RefreshableListView(Context context) {
this(context, null);
} // 两个参数的构造方法:这个方法是在使用了系统属性,没有使用自定义属性时调用的
public RefreshableListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} // 三个参数的构造方法:这个方法是在使用了自定义属性时调用的
public RefreshableListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
// 找到Header中的控件
refreshTip = (TextView) header.findViewById(R.id.control_header_refreshtip);
timeTip = (TextView) header.findViewById(R.id.control_header_timetip);
arrowImg = (ImageView) header.findViewById(R.id.control_header_refresharrow);
progressBar = (ProgressBar) header.findViewById(R.id.control_header_progressbar);
} // 初始化界面,添加顶部布局文件到ListView中
private void initView(Context context) {
inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.sideworks_layout_header, null);
// 测量顶部布局header的高度
measureView(context);
headerHeight = header.getMeasuredHeight();
setViewTopPadding(-headerHeight); // 设置ListView的上缩进:是负值,表示将header布局缩到屏幕外面去
// 把顶部布局添加到ListView的最上面
this.addHeaderView(header);
// 设置向下滑动时逐渐显示顶部布局(接口回掉方法)
this.setOnScrollListener(this);
} // 测量控件的宽高(通知父佈局:我佔用的寬和高)
private void measureView(Context context) {
ViewGroup.LayoutParams lp = header.getLayoutParams(); // 获取header布局的宽高属性
if (lp == null) {
lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int height; // 不能用getChildMeasureSpec方法获取高度的原因是ListView的高度不确定,而宽度是确定的
int tempHeight = lp.height;
if (tempHeight > 0) { // 大于0说明定义了ListView的高度,所以我们用精确布局模式EXACTLY
height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
} else { // 如果不大于0,则表示没有定义ListView的高度,即UNSPECIFIED
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
header.measure(width, height); // 这行代码很容易报错:NullPointerException,所以SDK17以前的版本必须将布局的最外层设置为LinearLayout
} // 设置ListView的TopPadding属性
private void setViewTopPadding(int topPadding) {
this.setPadding(this.getPaddingLeft(), topPadding, this.getPaddingRight(), this.getPaddingBottom());
this.invalidate(); // invalidate()方法的作用是请求对该控件进行重绘
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.firstVisisblePosition = firstVisibleItem;
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollState;
} @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (firstVisisblePosition == 0) {
remarkTop = true;
startY = (int) ev.getY(); // 如果按下时是处在ListView最上面的Item,则记录当前的Y坐标值
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if (state == STATE_TOREFRESH) { // 滑动到了“松开手指刷新数据”的高度
state = STATE_REFRESHING;
refreshViewByState();
listener.refreshListView(); // 调用接口,刷新数据
} else if (state == STATE_PULL) { // 还是处在“下拉刷新数据”的高度
state = STATE_NORMAL;
remarkTop = false;
refreshViewByState();
}
break;
}
return super.onTouchEvent(ev);
} // 判断移动过程中的操作
private void onMove(MotionEvent ev) {
if (!remarkTop) { // 如果按下地点不是ListView的第一个Item,则不做处理,正常滑动
return;
}
int tempY = (int) ev.getY(); // 当前移动到了什么位置(Y坐标值)
int space = tempY - startY; // 判断当前移动了多大距离(即header布局被拉下来多少),向下拉时是正值
int topPadding = space - headerHeight; // 当前还在屏幕外面的header布局的高度
switch (state) {
case STATE_NORMAL:
if (space > 0) {
state = STATE_PULL;
refreshViewByState();
}
break;
case STATE_PULL:
setViewTopPadding(topPadding);
if (space > headerHeight && scrollState == SCROLL_STATE_TOUCH_SCROLL) { // 滑动过header高度的一半并且仍然在滑动
state = STATE_TOREFRESH;
refreshViewByState();
}
break;
case STATE_TOREFRESH:
setViewTopPadding(topPadding);
if (space < headerHeight) {
state = STATE_PULL;
refreshViewByState();
} else if (space <= 0) {
state = STATE_NORMAL;
remarkTop = false;
refreshViewByState();
}
break;
}
} // 根据当前状态,改变界面显示
private void refreshViewByState() {
// 箭头反转的两个动画
RotateAnimation anim1 = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim1.setDuration(500);
anim1.setFillAfter(true);
RotateAnimation anim2 = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim2.setDuration(500);
anim2.setFillAfter(true); switch (state) {
case STATE_NORMAL:
setViewTopPadding(-headerHeight);
arrowImg.clearAnimation();
break;
case STATE_PULL:
arrowImg.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
refreshTip.setText("下拉可以刷新!");
arrowImg.clearAnimation();
arrowImg.setAnimation(anim2);
break;
case STATE_TOREFRESH:
arrowImg.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
refreshTip.setText("松开立即刷新!");
arrowImg.clearAnimation();
arrowImg.setAnimation(anim1);
break;
case STATE_REFRESHING:
setViewTopPadding(0);
arrowImg.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
refreshTip.setText("正在刷新......");
arrowImg.clearAnimation();
break;
}
} public void onRefreshComplete() {
state = STATE_NORMAL;
remarkTop = false;
refreshViewByState();
String time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
timeTip.setText(time);
} // 刷新数据的接口,要通过接口回掉的方式更新数据
public interface ListViewRefreshListener {
public void refreshListView();
} public void setListViewRefreshListener(ListViewRefreshListener listener) {
this.listener = listener;
}
}

header布局界面sideworks_layout_header.xml代码:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="50.0dip"
android:background="@color/cl_header_bg"
android:gravity="center"
android:padding="10.0dip" > <RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/cl_transparent" > <ImageView
android:id="@+id/control_header_refresharrow"
android:layout_width="wrap_content"
android:layout_height="35.0dip"
android:layout_centerVertical="true" android:layout_marginRight="15.0dip"
android:contentDescription="@string/app_name"
android:src="@drawable/refresh_arrow" /> <ProgressBar
android:id="@+id/control_header_progressbar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="15.0dip"
android:visibility="gone" /> <LinearLayout
android:id="@+id/position_header_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical"
android:paddingLeft="30.0dip" > <TextView
android:id="@+id/control_header_refreshtip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/str_header_refreshtip"
android:textColor="@color/cl_black"
android:textSize="12.0sp" /> <TextView
android:id="@+id/control_header_timetip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-7.0dip"
android:textColor="@color/cl_black"
android:textSize="12.0sp" />
</LinearLayout>
</RelativeLayout> </LinearLayout>

主界面布局activity_main.xml代码:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" > <com.view.RefreshableListView
android:id="@+id/control_main_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="@color/cl_transparent" /> </RelativeLayout>

主界面MainActivity.java代码:

 public class MainActivity extends Activity implements ListViewRefreshListener {
private RefreshableListView testList;
public static List<String> dataList;
public static ArrayAdapter<String> listAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
} private void initView() {
testList = (RefreshableListView) findViewById(R.id.control_main_listview);
testList.setListViewRefreshListener(this);
dataList = getData();
listAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, dataList);
testList.setAdapter(listAdapter);
} private List<String> getData() {
dataList = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
dataList.add("This is a test data.");
}
return dataList;
} @Override
public void refreshListView() {
// 延时两秒后显示两条新数据:This is a new data.
new Handler().postDelayed(new Runnable() {
public void run() {
for (int i = 0; i < 2; i++) {
dataList.add(0, "This is a new data.");
}
listAdapter.notifyDataSetChanged();
testList.onRefreshComplete();
}
}, 2000);
}
}

Android之下拉刷新的ListView的更多相关文章

  1. android 之下拉刷新

    一.概述 Android 下拉刷新几乎是每个应用都必带的功能, 并且现在下拉刷新第三方库也越来越多了,很方便就能实现该功能, 下面我介绍一下 自己常用的几个方法. 二.例子 第一种方式:就是集成Lis ...

  2. Android之下拉刷新,上啦加载的实现(一)

    转载地址http://blog.csdn.net/leehong2005/article/details/12567757#t5 前段时间项目中用到了下拉刷新功能,之前在网上也找到过类似的demo,但 ...

  3. Xamarin.Android之下拉刷新

    一.前言 当今任何一个App中只要存在列表,基本上都会使用下拉刷新,而身为Xamarin一族的我们自然也不会落后,下面笔者将带领大家在Xamarin下实现Android中的下拉刷新的效果. 二.准备工 ...

  4. android之下拉刷新(reflush)

    package com.example.reflush; import android.app.ListActivity; import android.os.Bundle; import andro ...

  5. Android UI之下拉刷新上拉刷新实现

    在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的demo,有一个自定义的下拉刷新控件 只需要在布局文件中直接引用就可以使用,非常方便,非常使用,以下是源代码: 自定义的 ...

  6. 打造通用的Android下拉刷新组件(适用于ListView、GridView等各类View)

    前言 近期在做项目时,使用了一个开源的下拉刷新ListView组件.极其的不稳定,bug还多.稳定的组件又写得太复杂了,jar包较大.在我的一篇博客中也讲述过下拉刷新的实现,即Android打造(Li ...

  7. android 自定义控件之下拉刷新源码详解

    下拉刷新 是请求网络数据中经常会用的一种功能. 实现步骤如下: 1.新建项目   PullToRefreshDemo,定义下拉显示的头部布局pull_to_refresh_refresh.xml &l ...

  8. 【FastDev4Android框架开发】RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout(三十一)

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息] ...

  9. [转]Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能

    版权声明:本文出自郭霖的博客,转载必须注明出处. 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最近项目中需要用到L ...

随机推荐

  1. db2学习笔记--数据类型对表的影响

    创建表的时为列选择合适的数据类型,可以提高数据库性能. 1.选择合适的数据类型,避免出现数据类型转换.例如日期,有人使用字符串来存放日期.时间戳,最后我们还要在程序中使用to_date做数据类型转换, ...

  2. 腾讯云CentOS系统配置apache和tomcat

    本文使用yum软件包管理工具基于CentOS7.2版本配置apache和tom. 云服务器选购完毕后,安装Xshell软件,输入用户名密码即可远程登陆登录(centos用户名默认是root). 1,下 ...

  3. win 7下建立FTP

    1.安装FTP服务 鼠标桌面右击个性化-卸载程序-打开或关闭windows功能 2.在IIS控制面板里添加FTP站点 下一步 下一步 鼠标右击 下一步 下一步 3.配置ftp站点 4.测试站点是否正常 ...

  4. Wiki安装(PHP +Sqlite+Cache)

    前期准备 PHP http://windows.php.net/download   WinCache Extension for PHP URL:http://sourceforge.net/pro ...

  5. Hanoi问题java解法

    用什么语言解法都差不多,思路都是一样,递归,这其中只要注重于开始和结果的状态就可以了,对于中间过程,并不需要深究.(我细细思考了一下,还是算了.=_=) 代码其实很简单注重的是思路. 问题描述:有一个 ...

  6. 1597: [Usaco2008 Mar]土地购买

    1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4023  Solved: 1470[Submit] ...

  7. [LeetCode] Queue Reconstruction by Height 根据高度重建队列

    Suppose you have a random list of people standing in a queue. Each person is described by a pair of ...

  8. [LeetCode] Line Reflection 直线对称

    Given n points on a 2D plane, find if there is such a line parallel to y-axis that reflect the given ...

  9. Java正则表达式入门——转自RUNOOB.COM

    Java 正则表达式 正则表达式定义了字符串的模式. 正则表达式可以用来搜索.编辑或处理文本. 正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别. Java正则表达式和Perl的是最为相似 ...

  10. 字节、字、bit、byte的关系

    字 word 字节 byte 位 bit 字长是指字的长度 1字=2字节(1 word = 2 byte) 1字节=8位(1 byte = 8bit)  一个字的字长为16 一个字节的字长是8 bps ...