ListView下拉刷新

public class MyListView extends ListView implements OnScrollListener {
private static final int STATE_NORMAL = 0;  // 正常状态
private static final int STATE_PULL = 1;    // 下来状态
private static final int STATE_RELEASE = 2; // 释放状态
private static final int STATE_LOADING = 3; // 数据加载状态
 
private View mHeaderView;   // listview的headerview
private ImageView mImage;   // headerview中图片
private ProgressBar mProgress; // headerview中的progressbar
private TextView mText;      // headerview中的文本
 
private RotateAnimation mAnim1;  // headerview中图片的旋转动画
private RotateAnimation mAnim2;  // 同上
private boolean isNowAtUp;  // 判断当前是不是在listview的最上面
 
private int mHeaderHeight;  // headerview的高度
private int mFirstVisibleItem;  // 保存listview中第一个可见项
private int mScrollState;  // 保存listview滑动的状态
private int mState;  // 保存判断的状态,同上面的常量进行比较,
private int mStartY; // 开始下拉的y值
private OnPullRefreshListener mListener;  // 回调接口
 
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
 
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
 
// 初始化view
private void initView() {
// 加载headerview
mHeaderView = LayoutInflater.from(getContext()).inflate(
R.layout.header_layout, null);
//获取headerview中组件
mImage = (ImageView) mHeaderView.findViewById(R.id.image);
mProgress = (ProgressBar) mHeaderView.findViewById(R.id.progress);
mText = (TextView) mHeaderView.findViewById(R.id.text);
// 初始化动画
initAnimation();
// 因为下面要用到headerview的高度,但在处理化的时候还没有进行onMeasure
// 所以要手工进行测量
measureView(mHeaderView);
// 获取headerview的高度
mHeaderHeight = mHeaderView.getMeasuredHeight();
// 设置headerview的上padding
// 为负的高度,则隐藏掉headerview
setPaddingTop(-mHeaderHeight);
// 将headerview添加到listview中
addHeaderView(mHeaderView);
 
// 监听滚动
setOnScrollListener(this);
}
 
// 初始化动画
private void initAnimation() {
mAnim1 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f); 
mAnim2 = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mAnim1.setDuration(500);
mAnim2.setDuration(500);
mAnim1.setFillAfter(true);
mAnim2.setFillAfter(true);
}
 
// 设置headerview的padding
private void setPaddingTop(int top) {
mHeaderView.setPadding(
mHeaderView.getPaddingLeft(),
top,
mHeaderView.getPaddingRight(),
mHeaderView.getPaddingBottom());
 
invalidate();
}
 
// 测量view
private void measureView(View view) {
// 先获取LayoutParams
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (null == lp) {
lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
 
// 第一个参数是spec
// 第二个参数是padding
// 第三个参数是我期望的大小
// 如果第三个参数小于0
// 则返回的结果是第一个参数减去第二个参数
// 如果第三个参数不小于0
// 则返回第三个参数
int widthMeasureSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int height = lp.height;
int heightMeasureSpec;
 
// 生成height的Spec
if (height > 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
} else {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
 
// 测量view,这样就可以获取headerview的高度了
// 否则headerview的高度获取出来为0
// MATCH_PARENT=-1, WRAP_CONTENT=-2
view.measure(widthMeasureSpec, heightMeasureSpec);
}
 
// 滑动状态该类
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 保存下当前的状态
mScrollState = scrollState;
}
 
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 保存当前第一个可见项
mFirstVisibleItem = firstVisibleItem;
}
 
// 监听touch事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 如果是按下操作
// 并且现在第一个可见项是listview的第一项
// 即:现在在最上面
if (mFirstVisibleItem == 0) {
isNowAtUp = true;  // 记录现在是在最上面
mStartY = (int) ev.getY(); // 获取点击的y坐标
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);  // 在onMove中处理移动事件
break;
case MotionEvent.ACTION_UP:
// 如果当前状态是可释放状态
if(mState == STATE_RELEASE) {
// 那个up后, 修改状态为数据加载状态
mState = STATE_LOADING;
// 刷新界面
refreshView();
// 调用回调接口中的方法
// 具体实现在activity中
mListener.onPull();
}else if(mState == STATE_PULL){  // 如果是下拉状态
mState = STATE_NORMAL; // 则更改状态为正常状态,因为还没有拉到加载数据的程度
refreshView(); // 刷新界面
}
break;
}
return super.onTouchEvent(ev);
}
 
// 刷新界面,其实是更改headerview中子view的状态
private void refreshView() {
switch (mState) {
case STATE_NORMAL:
setPaddingTop(-mHeaderHeight);  // 如果是正常状态,则将headerview隐藏了
break;
case STATE_PULL:  // 如果是下拉状态
mProgress.setVisibility(View.GONE); // 隐藏progressbar
mImage.setVisibility(View.VISIBLE); // 将图片显示出来
mImage.clearAnimation(); // 清除动画
mImage.startAnimation(mAnim1); // 设置旋转动画
mText.setText("下拉刷新!");  // 更新textview的文本
break;
case STATE_RELEASE:  // 释放状态
mProgress.setVisibility(View.GONE);  // 隐藏progressbar
mImage.setVisibility(View.VISIBLE);  // 将图片显示出来
mImage.clearAnimation(); // 清除动画
mImage.startAnimation(mAnim2); // 设置旋转动画
mText.setText("释放加载新数据!"); // 更新textview的文本
break;
case STATE_LOADING:  // 数据加载状态
setPaddingTop(mHeaderHeight);  // 设置paddingtop为headerview的高度
mProgress.setVisibility(View.VISIBLE); // 显示progressbar
mImage.clearAnimation(); // 清除动画
mImage.setVisibility(View.GONE); // 隐藏imageview
mText.setText("正在加载数据..."); // 更新文本
break;
}
}
 
// 处理action_move事件
private void onMove(MotionEvent ev) {
// 如果当前不是在顶部,直接返回
if (!isNowAtUp) {
return;
}
 
int nowY = (int) ev.getY();  // 记录当前的y坐标
int space = nowY - mStartY;  // 计算下拉的程度
int top = space - mHeaderHeight; // 计算top的值
 
switch (mState) {
case STATE_NORMAL:  // 如果是正常状态
if (space > 0) {  // 如果下拉了
mState = STATE_PULL; // 则 修改状态为下拉
refreshView(); // 并刷新headerview
}
break;
case STATE_PULL:  // 如果是下来状态
setPaddingTop(top); // 不断修改headerview的paddingtop
// 如果达到条件,并且还是在下拉中
if (space >= mHeaderHeight + 30
&& mScrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
mState = STATE_RELEASE;  // 修改状态为可释放状态
refreshView(); // 刷新headerview
}
break;
case STATE_RELEASE:  // 如果是可释放状态
setPaddingTop(top); // 不断修改headerview的paddingtop
// 如果下拉程度在STATE_PULL区间
if (space > 0 && space <= mHeaderHeight + 30) {
mState = STATE_PULL; // 则修改状态为下来状态,这样是又慢慢拉回去了
refreshView(); // 刷新headerview
} else if (space <= 0) {  // 如果没有下拉的空间了
mState = STATE_NORMAL; // 修改状态为正常状态,拉了一圈又回退回去了
isNowAtUp = false; // 还原默认值
refreshView(); // 刷新headerview
}
break;
}
}
// 外部调用的方法
// 通知我新数据加载完毕
// 还原状态为正常状态
public void endToRefresh() {
mState = STATE_NORMAL;
refreshView();
}
// 设置加载数据的回调接口
public void setOnPullRefreshListener(OnPullRefreshListener l) {
mListener = l;
}
// 定义一个回调接口
public interface OnPullRefreshListener {
public void onPull();
}
}

MyListView中使用的header_layout.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/darker_gray"
    android:orientation="vertical" >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dip"
        android:paddingLeft="30dip"
        android:paddingRight="30dip"
        android:paddingTop="10dip" >
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:orientation="horizontal" >
            <ImageView
                android:id="@+id/image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/arrow" />
            <ProgressBar
                android:id="@+id/progress"
                style="?android:attr/progressBarStyleSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:visibility="gone" />
            <TextView
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="下拉可以刷新" />
        </LinearLayout>
    </RelativeLayout>
</LinearLayout>

使用:

在布局文件中:

<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"
    tools:context=".MainActivity" >
    <org.load.listview.MyListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</RelativeLayout>

在MainActivity中:

public class MainActivity extends Activity {
private MyListView mListView;
// 模拟数据
private String[] mData = { "aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg",
"hhh", "iii" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (MyListView) findViewById(R.id.list);
// 数据适配器
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, mData);
// 设置适配器
mListView.setAdapter(adapter);
// 这里就用到了回调接口
// 表示当可以加载新数据时干什么
mListView.setOnPullRefreshListener(new OnPullRefreshListener() {
@Override
public void onPull() {
// 模拟获取新数据
// 延迟2s后更新前两个数据
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
mData[i] = "new data " + new Random().nextInt(100);
adapter.notifyDataSetChanged();
mListView.endToRefresh();  // 通知MyListView数据加载完毕了
}
}
}, 2000);
}
});
}
}
时间: 2024-12-22 19:30:00

ListView下拉刷新的相关文章

Android—自定义控件实现ListView下拉刷新

这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家): 首先下拉未松手时候手机显示这样的界面: 下面的代码是自定的扎样的控件: package com.dhsr.smartID.view; import android.content.Context; import android.util.AttributeSet; import android.view.Gravity; import andr

Android ListView 下拉刷新 点击加载更多

最近项目中用到了ListView的下拉刷新的功能,总结了一下前辈们的代码,单独抽取出来写了一个demo作为示例. 效果图 下拉刷新: 加载更多: CustomListView.java [java] view plaincopy package com.example.uitest.view; import java.util.Date; import com.example.uitest.R; import android.content.Context; import android.uti

十分钟实现ListView下拉刷新上滑加载更多

说到ListView下拉刷新几乎每个APP都会用到,所以ListView下拉刷新是很重要的,就像ListView优化一样是你必会的东西. ListView实现下拉刷新如果我们开发人员自己编写相对来说比较费事的,当我们使用第三方库之后我们再来开发这个功能就会省事很多.相比与自己实现可以少编写不少代码,Android-PullToRefresh库可以轻松实现ListView的下拉刷新功能. 要使用Android—PullToRefesh库对ListView实现下拉刷新要经过以下几个步骤: 1.下载A

基于Android的计步器(Pedometer)的讲解(六)——ListView下拉刷新页面

计步器(Pedometer)整个项目的源代码,最近做了比较大的修改,可能以前下载的不能运行,感兴趣的朋友可以下载来看看(记得帮小弟在github打个星~) https://github.com/296777513/pedometer 今天实现实现的下拉刷新的功能,先上几张效果图: 如图所示,今天就是要实现的这个效果 首先,分析ListView下拉刷新实现方式 1.需要添加顶部下拉加载页面 2.需要监听onScrollListener来判断当前是否显示在listview的最顶部 3.因为顶部下拉加

ListView下拉刷新、上拉载入更多之封装改进

在Android中ListView下拉刷新.上拉载入更多示例一文中,Maxwin兄给出的控件比较强大,前面有详细介绍,但是有个不足就是,里面使用了一些资源文件,包括图片,String,layout,这样不利于封装打包,下面我将源码进行改进,所有布局全部用代码实现,这样直接将src和assets打包成jar就成了一个非常方便以后使用的扩展ListView控件,代码如下: XListView: /** * @file XListView.java * @package me.maxwin.view

Qt qml listview 下拉刷新上拉分页控件

Qt qml listview下拉刷新和上拉分页主要根据contentY来判断.但要加上顶部下拉指示器.滚动条,并封装成可简单调用的组件,着实花了我不少精力:) [先看效果]    [功能] 1 下拉刷新和上拉分页逻辑 2 /下拉刷新 3 /上拉更多 4 /滚动栏 5 /工具栏半拉显隐 6 Author: surfsky.cnblogs.com 7 Lisence: MIT 请保留此文档声明 8 History: 9 init. surfsky.cnblogs.com, 2015-01 10 a

Android自定义ListView下拉刷新

实现的目标是本地有数据并没有刷新.下拉数据及时刷新数据. 我在网上找了某位写的MyListView,这个东西的下拉核心部分还是没有弄明白.非常感谢这位作者. XML布局文件源代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layou

ListView下拉刷新模仿微信眼睛

ListView下拉刷新模仿微信眼睛 下载地址:http://www.devstore.cn/code/info/747.html 运行截图:

(UWP开发)更为合理的一种ListView下拉刷新(PullToRefresh)实现方法

最近在做的一个项目需要用到下拉刷新,但是参考了现在网络上比较普遍的方法,觉得都不太好,因为要在外部套上一个SrollViewer,容易出现滚动错误.于是刚开始的时候就把思路定到了ListView内部的ScrollViewer上. 最初的想法是在ScrollViewer的Manipulation相关事件上下手,确实做好了,效果也不错,如图: 当时得意满满的看着自己的作品,心里是说不出的激动啊,结果放在手机上想试试触屏设备的效果,结果发现好坑爹:在触屏设备上,手指在ListView的上下滑动默认是移