文档章节

ListView下拉刷新

亓斌哥哥
 亓斌哥哥
发布于 2014/10/09 22:04
字数 1608
阅读 462
收藏 13
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);
}
});
}
}






© 著作权归作者所有

共有 人打赏支持
亓斌哥哥

亓斌哥哥

粉丝 28
博文 34
码字总数 12346
作品 13
莱芜
程序员
私信 提问
Jaynm/PullToRefreshListView

PullToRefreshScrollViewDemo Android使用PullToRefresh完成ListView下拉刷新和左滑删除 一、本文主要内容: 使用PullToRefresh完成ListView下拉、上拉刷新; 扩展PullToRefresh完美的实现L...

Jaynm
2016/11/03
0
0
滑动事件总结(刷新,加载更多,嵌套滑动)

下拉刷新 在Api21之前,ListView和GridView的使用相当普遍,包括下拉刷新我们也可以使用它,利用他的addHeaderView()和addFooterView()方法,或者使用父级中隐藏View的方式来实现; 在A...

卐字旗下的余晖
2016/06/07
101
0
下拉刷新ListView的实现原理

本文主要介绍如何实现类似新浪微博客户端下拉刷新效果的ListView。关于其使用见下拉刷新ListView的使用。 示例APK地址:TrineaAndroidDemo 首先让我们看下效果 四张图分别为第一次下拉、第一...

等待流星
2014/03/14
0
0
ExpandableListView和Listview可以共用一个写好的下拉刷新的工具类吗

以前我用的ListView加了下拉刷新,现在我改成了使用ExpandableListView,在他的基础上再加下拉刷新,我把关于Listview的都改成了ExpandableListView的 ,但是没效果,不知道是 ExpandableLi...

BAITOCC
2013/12/26
734
0
一起撸个朋友圈吧(step1) ListView(中)篇

项目地址:https://github.com/razerdp/FriendCircle 一起撸个朋友圈吧这是本文所处文集,所有更新都会在这个文集里面哦,欢迎关注 上篇链接:http://www.jianshu.com/p/7fa237cfddbb 下篇链...

WeiChaoFeng
2017/12/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

python机器学习及实践学习笔记1-如何打开ipynb后缀文件

python机器学习及实践学习笔记1-如何打开ipynb后缀文件 2017年02月22日 14:58:08 hustzhoutian 阅读数:45365更多 个人分类: 深度学习 需要安装ipython notebook,如果你已经安装Anaconda软...

linjin200
12分钟前
2
0
关于在vim中的查找和替换

1,查找 在normal模式下按下/即可进入查找模式,输入要查找的字符串并按下回车。 Vim会跳转到第一个匹配。按下n查找下一个,按下N查找上一个。 Vim查找支持正则表达式,例如/vim$匹配行尾的"...

休辞醉倒
17分钟前
1
0
in_array的坑

PHP in_array的坑 ps: 应该是弱类型语言的坑 php文档 顾名思义,in_array就是查找一个值是否在数组里面。 问题 事故现场 一个sql注入的测试代码如下: $type = $_GET['type'];$types = [2,3,...

o0无忧亦无怖
17分钟前
18
1
Yarn(包管理器) 的基本用法

Yarn是一个快速、可靠、安全的依赖管理工具,是npm的代替品。 Yarn对你的代码来说是一个包管理工具,你可以通过它使用全世界开发者的代码,或者分享自己的代码。 安装Yarn: 操作系统不同,安...

帝子兮
18分钟前
1
0
阿里云HBase全新发布X-Pack NoSQL数据库再上新台阶

一、八年双十一,造就国内最大最专业HBase技术团队 阿里巴巴集团早在2010开始研究并把HBase投入生产环境使用,从最初的淘宝历史交易记录,到蚂蚁安全风控数据存储。持续8年的投入,历经8年双...

阿里云官方博客
19分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部