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

自定义的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. Jsoup系列学习(2)-解析html文件

    解析html文件 1.当我们通过发送http请求时,有时候返回结果是一个html格式字符串,你需要从一个网站获取和解析一个HTML文档,并查找其中的相关数据.你可以使用下面解决方法: 使用 Jsoup ...

  2. 30天的php学习之路——第一天开端

    因项目紧急只留给了我4周时间学习php.谨以此文记录我的php学习历程,仅供参考,若有错误请指正. 之前对php有过一些了解,一些语法和习惯和C#差距有些大,所以第一天准备对php中的符号有个全面的了 ...

  3. Common Bugs in C Programming

    There are some Common Bugs in C Programming. Most of the contents are directly from or modified from ...

  4. Android--sharepreference总结

    SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数. SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/ ...

  5. flex自适应高度内容高度超出容器高度自动出现滚动条的问题

    在容器中设置 flex-grow:2; overflow-y:auto;overflow-x:hidden;容器高度自适应. 内容高度不固定,无法出现滚动条,然后在容器中添加height:0,出现滚动 ...

  6. [LeetCode] Patching Array 补丁数组

    Given a sorted positive integer array nums and an integer n, add/patch elements to the array such th ...

  7. [LeetCode] Contains Duplicate 包含重复值

    Given an array of integers, find if the array contains any duplicates. Your function should return t ...

  8. background-image和img的区别

    background-img的时候外边的div必须有宽和高.并且你只能决定图片位于你div的位置不能拉伸图片,或者改变图片的宽高.但是background-image是可以重复的,所以只要你的图片不是 ...

  9. 【MySQL】函数IFNULL、设置默认时间

    MySql 函数 IFNUll用法说明 IFNULL(expr1,expr2) 如果 expr1 不是 NULL,IFNULL() 返回 expr1,否则它返回 expr2. IFNULL()返回一个 ...

  10. TCP的状态 (SYN, FIN, ACK, PSH, RST, URG)

    状态说明 SYN表示建立连接, FIN表示关闭连接, ACK表示响应, PSH表示有 DATA数据传输, RST表示连接重置. 其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同 ...