很久没有写博客了,感觉都懒惰了,今天说一下一个自定义的空间,就是ListView下拉列表可以刷新的功能,相信很多同学都看到过这种功能,最典型的就是新浪微博的下拉刷新列表了。

  废话不多说,首先呢,下拉刷新的那个带有progressBar的是ListView的headView,所以首先我们需要自定义一个HeadView,如下所示:

 <?xml version="1.0" encoding="utf-8"?>

 <!-- ListView的头部 -->

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" > <!-- 内容 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/head_contentLayout" > <!-- 箭头头像、进度条 -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"> <!-- 箭头 -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/head_arrowImageView"
android:src="@drawable/arrow"/> <!-- 进度条 -->
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleSmall"
android:layout_gravity="center"
android:visibility="gone"
android:id="@+id/head_progressBar"/>
</FrameLayout> <!-- 提示、最近更新 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="vertical"
android:gravity="center_horizontal"> <!-- 提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:id="@+id/head_tipsTextView"
android:textSize="@dimen/head_tips_size"
android:text="@string/head_tips_text"/> <!-- 最近更新 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/head_lastUpdatedTextView"
android:textColor="@color/gold"
android:text="@string/lash_updated_text"
android:textSize="@dimen/last_updated_size"/>
</LinearLayout> </RelativeLayout> </LinearLayout>

  FrameLayout里面是两个图标,一个是下拉的箭头,另一个是刷新的ProgressBar,因为这两个不会同时出现,所以我们把他们房子一个FrameLayout里面。

  右边是两个TextView,一个显示提示的信息,一个显示上次更新的详情。

  headView完成了,我们要继续ListView,并添加HeadView,在ListView中的onTouchEvent事件里面对滑动做监听之类的。

  如下是代码:

 package com.alex.helloworld;

 import java.util.Date;

 import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView; public class CustomListView extends ListView implements OnScrollListener { private final static int RELEASE_TO_REFRESH = 0;
private final static int PULL_TO_REFRESH = 1;
//正在刷新
private final static int REFRESHING = 2;
//刷新完成
private final static int DONE = 3;
private final static int LOADING = 4; private final static int RADIO = 3; private LayoutInflater mInflater;
private LinearLayout mHeadView;
private TextView mTipsTextView;
private TextView mLastUpdatedTextView;
private ImageView mArrowImageView;
private ProgressBar mProgressBar; private RotateAnimation mAnimation;
private RotateAnimation mReverseAnimation;
private boolean mIsRecored;
private int mHeadContentWidth;
private int mHeadContentHeight;
private int mStartY;
private int mFirstItemIndex;
private int mState;
private boolean mIsBack;
private boolean mISRefreshable;
private OnRefreshListener mRefreshListener; public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
} private void init(Context context) {
// setCacheColorHint(android.R.color.black);
mInflater = LayoutInflater.from(context);
mHeadView = (LinearLayout) mInflater.inflate(R.layout.head, null);
mArrowImageView = (ImageView) mHeadView.findViewById(R.id.head_arrowImageView);
// mArrowImageView.setMinimumWidth(70);
// mArrowImageView.setMinimumHeight(50);
mProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressBar);
mTipsTextView = (TextView) mHeadView.findViewById(R.id.head_tipsTextView);
mLastUpdatedTextView = (TextView) mHeadView.findViewById(R.id.head_lastUpdatedTextView); measureView(mHeadView);
mHeadContentHeight = mHeadView.getMeasuredHeight();
System.out.println("mHeadContentHeight = " + mHeadContentHeight);
mHeadContentWidth = mHeadView.getMeasuredWidth();
System.out.println("mHeadContentWidth = " + mHeadContentWidth);
mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0);
mHeadView.invalidate();
addHeaderView(mHeadView, null, false);
setOnScrollListener(this); mAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mAnimation.setInterpolator(new LinearInterpolator());
mAnimation.setDuration(250);
mAnimation.setFillAfter(true); mReverseAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseAnimation.setInterpolator(new LinearInterpolator());
mReverseAnimation.setDuration(250);
mReverseAnimation.setFillAfter(true); mState = DONE;
mISRefreshable = false;
} private void measureView(View child) {
android.view.ViewGroup.LayoutParams params = child.getLayoutParams();
System.out.println("params = " + params);
if(params == null) {
params = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
System.out.println("lpWidth = " + params.width);
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0+0, params.width);
System.out.println("childWidthSpec = " + childWidthSpec);
int lpHeight = params.height;
System.out.println("lpHeight = " + lpHeight);
int childHeightSpec;
if(lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.UNSPECIFIED);
}
System.out.println("childHeightSpec = " + childHeightSpec);
child.measure(childWidthSpec, childHeightSpec);
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mFirstItemIndex = firstVisibleItem;
} public interface OnRefreshListener {
public void onRefresh();
} private void onRefresh() {
if(mRefreshListener != null) {
mRefreshListener.onRefresh();
}
} public void onRefreshComplete() {
mState = DONE;
mLastUpdatedTextView.setText("已加载完成:" + new Date().toLocaleString());
changeHeaderViewByState();
} public void setonRefreshListener(OnRefreshListener onRefreshListener) {
this.mRefreshListener = onRefreshListener;
mISRefreshable = true;
} @Override
public boolean onTouchEvent(MotionEvent ev) {
if(mISRefreshable) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if(mFirstItemIndex == 0 && !mIsRecored) {
mIsRecored = true;
mStartY = (int) ev.getY();
}
break; case MotionEvent.ACTION_UP:
if(mState != REFRESHING && mState != LOADING) {
if(mState == DONE) { }
if(mState == PULL_TO_REFRESH) {
mState = DONE;
changeHeaderViewByState();
}
if(mState == RELEASE_TO_REFRESH) {
mState = REFRESHING;
changeHeaderViewByState();
onRefresh();
}
}
mIsBack = false;
mIsRecored = false;
break; case MotionEvent.ACTION_MOVE:
int tempY = (int) ev.getY();
if(!mIsRecored && mFirstItemIndex == 0) {
mIsRecored = true;
mStartY = tempY;
}
if(mState != REFRESHING && mIsRecored && mState != LOADING) {
if(mState == RELEASE_TO_REFRESH) {
setSelection(0);
if((tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0) {
mState = PULL_TO_REFRESH;
changeHeaderViewByState();
} else if(tempY - mStartY <= 0) {
mState = DONE;
changeHeaderViewByState();
}
} if(mState == PULL_TO_REFRESH) {
setSelection(0);
if((tempY - mStartY)/RADIO >= mHeadContentHeight) {
mState = RELEASE_TO_REFRESH;
mIsBack = true;
changeHeaderViewByState();
}
} else if(tempY - mStartY <= 0) {
mState = DONE;
changeHeaderViewByState();
} if(mState == DONE) {
if(tempY - mStartY > 0) {
mState = PULL_TO_REFRESH;
changeHeaderViewByState();
}
} if(mState == PULL_TO_REFRESH) {
mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0);
} if(mState == RELEASE_TO_REFRESH) {
mHeadView.setPadding(0, (tempY - mStartY)/RADIO - mHeadContentHeight, 0, 0);
}
}
break; default:
break;
}
}
return super.onTouchEvent(ev);
} private void changeHeaderViewByState() {
switch (mState) {
case PULL_TO_REFRESH:
mProgressBar.setVisibility(GONE);
mTipsTextView.setVisibility(VISIBLE);
mLastUpdatedTextView.setVisibility(VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(VISIBLE);
if(mIsBack) {
mIsBack = false;
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mReverseAnimation);
mTipsTextView.setText("isBack is true!!!");
} else {
mTipsTextView.setText("isBack is false!!!");
}
break; case DONE:
mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0);
mProgressBar.setVisibility(GONE);
mArrowImageView.clearAnimation();
mArrowImageView.setImageResource(R.drawable.arrow);
mTipsTextView.setText("已经加载完毕-DONE");
mLastUpdatedTextView.setVisibility(VISIBLE);
break; case REFRESHING:
mHeadView.setPadding(0, 0, 0, 0);
mProgressBar.setVisibility(VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(GONE);
mTipsTextView.setText("正在加载中……REFRESHING");
break; case RELEASE_TO_REFRESH:
mArrowImageView.setVisibility(VISIBLE);
mProgressBar.setVisibility(GONE);
mTipsTextView.setVisibility(VISIBLE);
mLastUpdatedTextView.setVisibility(VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mAnimation);
mTipsTextView.setText("请释放刷新");
break;
default:
break;
}
} @Override
public void setAdapter(ListAdapter adapter) {
mLastUpdatedTextView.setText("this is in MyListView:" + new Date().toLocaleString());
super.setAdapter(adapter);
}
}

  代码有点多,需要耐心的看,这里我主要说一下大体的思路,细节就不在说了

  还是先把截图看一下吧,对照着比较好说:

  

  代码里面写了一个接口,用作刷新用的。

  手指按下屏幕的时候会走Action down,这是有一堆判断条件,如果满足判断条件的话,就记录下Y轴的坐标

  如果手机继续在屏幕上滑动,会走action move,如果手指不松开,一直滑动,会一直走action move,这时也可以拿到Y轴的坐标,判断现在的状态,没有刷新,没有加载,刚开始的默认状态是done的,如果向下滑动,导致坐标差大于0,将状态改为PULL_TO_REFRESH,同时改变控件的一些状态,比如可见性等。

同时要改变headView的padding状态,导致headview渐渐的变为可见:if(mState == PULL_TO_REFRESH) {
      mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0);
     }

如果把padding设置为-1*高度,就会导致headView看不见了。

这时手指继续下滑,如果终点和起点的差值/3 还要大于headView的高度的话,这时改变状态为RELEASE_TO_REFRESH,mState = RELEASE_TO_REFRESH;
       mIsBack = true;这就意味着松手就可以刷新了,当然是要满足终点和起点的差值/3 还要大于headView的高度,如果tempY - mStartY <= 0,说明在向回滑动,状态改为mState = DONE;同时改变控件的状态。

现在只能向回滑动,因为向下滑动不会有任何效果,(tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0,继续下滑,改变状态不做操作,同时改变控件状态,向回滑动,改变状态,mState = DONE;同时改变控件状态。

当状态为mState == PULL_TO_REFRESH时,松开手指,就是action up,不做任何改变,当状态为mState == RELEASE_TO_REFRESH时,松手,开始刷新操作。

布局文件:

 <?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="match_parent"
android:orientation="vertical" > <com.alex.helloworld.CustomListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView"/> </LinearLayout>

Activity的文件:

 package com.alex.helloworld;

 import java.util.ArrayList;

 import com.alex.helloworld.CustomListView.OnRefreshListener;

 import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.widget.ArrayAdapter;
import android.widget.ListView; public class HelloWord extends Activity { // private TextView mTextView;
private SlidingLayout slidingLayout;
private ListView contentList;
private ArrayAdapter<String> contentListAdapter;
private String[] contentItems = {
"Content Item 1", "Content Item 2", "Content Item 3",
"Content Item 4", "Content Item 5", "Content Item 6", "Content Item 7",
"Content Item 8", "Content Item 9", "Content Item 10", "Content Item 11",
"Content Item 12", "Content Item 13", "Content Item 14", "Content Item 15",
"Content Item 16"
}; private ArrayList<String> mArrays = new ArrayList<String>(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
for(int i=0; i<contentItems.length; i++) {
mArrays.add(contentItems[i]);
}
// slidingLayout = (SlidingLayout) findViewById(R.id.sliding_layout);
// Button showLeftButton = (Button) findViewById(R.id.show_left_menu);
// Button showRightButton = (Button) findViewById(R.id.show_right_menu);
// contentList = (ListView) findViewById(R.id.contentList);
// contentListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contentItems);
// contentList.setAdapter(contentListAdapter);
// mTextView = (TextView) findViewById(R.id.tv);
// mTextView.setText("");
// String product = Build.PRODUCT;
// mTextView.append(product + "\n");
// mTextView.append(Build.MANUFACTURER + "\n");
// mTextView.append(Build.DISPLAY + "\n");
// mTextView.append(Build.VERSION.SDK);
// checkUpdate(); final CustomListView listView = (CustomListView) findViewById(R.id.listView);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mArrays);
listView.setAdapter(adapter);
listView.setonRefreshListener(new OnRefreshListener() { @Override
public void onRefresh() {
new AsyncTask<Void, Void, Void> () { @Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mArrays.add("add a new one");
return null;
} protected void onPostExecute(Void result) {
adapter.notifyDataSetChanged();
listView.onRefreshComplete();
}; }.execute();
}
});
} // @Override
// public boolean onCreateOptionsMenu(Menu menu) {
// getMenuInflater().inflate(R.menu.hello_word, menu);
// return true;
// } // @Override
// protected void onDestroy() {
// super.onDestroy();
// } // private void checkUpdate() {
// System.out.println("checkUpdate");
// int localVersion = 0;
// int serverVersion = 0;
// try {
// localVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
// serverVersion = 2;
// } catch (NameNotFoundException e) {
// e.printStackTrace();
// }
//
// if(serverVersion > localVersion) {
// Builder builder = new Builder(this);
// builder.setTitle("更新软件");
// builder.setMessage("更新");
// builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
//
// @Override
// public void onClick(DialogInterface dialog, int which) {
//
// }
// }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
//
// @Override
// public void onClick(DialogInterface dialog, int which) {
// dialog.dismiss();
// }
// });
// builder.show();
// }
// }
}

就这么多吧。

ListView实现下拉刷新功能的更多相关文章

  1. android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)

    Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableL ...

  2. Xamarin. Android实现下拉刷新功能

    PS:发现文章被其他网站或者博客抓取后发表为原创了,给图片加了个水印 下拉刷新功能在安卓和iOS中非常常见,一般实现这样的功能都是直接使用第三方的库,网上能找到很多这样的开源库.然而在Xamarin. ...

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

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

  4. Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最 近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在 ...

  5. Android学习之listview的下拉刷新、上拉载入

    本例是在上例的基础上完成的.本例实现的listview上拉载入.下拉刷新功能,是在开源网站上别人写好的listview,主要是对listview的控件进行重写,添加了footer和header. 1. ...

  6. android--------自定义控件ListView实现下拉刷新和上拉加载

    开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,为了方便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能. Android下拉刷新可以分为两种情况: 1.获取 ...

  7. Android StaggeredGrid 加下拉刷新功能 PullToRefresh

    https://github.com/etsy/AndroidStaggeredGrid  用的github上面提供瀑布流,继承于abslistview,回收机制不错,并且提供了OnScrollLis ...

  8. ListView实现下拉刷新(三)实现下拉刷新

    该准备的东西都已经准备好了.在这篇文章里,我们就开始实现下拉刷新功能吧. 一.大体的逻辑分析 我们来简单分析一下需要做的逻辑吧.首先分析头布局有几种状态.不下拉时,为正常状态,此时头布局隐藏.下拉到一 ...

  9. ListView实现下拉刷新(一)建立头布局

    一.效果演示 ListView实现下拉刷新,是很常见的功能.下面是一个模拟的效果,如下图:                                   效果说明:当往下拉ListView的时候 ...

随机推荐

  1. 【codeforces】【比赛题解】#872 CF Round #440 (Div.2)

    链接. [A]寻找漂亮数字 题意: 给定了两列非零数字.我们说一个数是漂亮的,当它的十进制表达中有至少一个数从数列一中取出,至少有一个数从数列二中取出.最小的漂亮数字是多少? 输入: 第一行两个数\( ...

  2. python面向对象——类

    from:http://www.runoob.com/python3/python3-class.html Python3 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在P ...

  3. 10款常见MySQL高可用方案选型解读【转】

    我们在考虑MySQL数据库的高可用架构时,主要考虑如下几方面: 如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中断. 用作备份. ...

  4. mysql insert锁机制【转】

    最近再找一些MySQL锁表原因,整理出来一部分sql语句会锁表的,方便查阅,整理的不是很全,都是工作中碰到的,会持续更新 笔者能力有限,如果有不正确的,或者不到位的地方,还请大家指出来,方便你我,方便 ...

  5. java 判断上传文件大小

    /** * 判断文件大小 * * @param file * 文件 * @param size * 限制大小 * @param unit * 限制单位(B,K,M,G) * @return */ pu ...

  6. 【转载】ajaxFileUpload 报这错jQuery.handleError is not a function

    今天刚打个一个技术群,里面有人问标题上的问题,嘿,我恰好遇过,现在大家至少也在用jquery1.9以上的版本,ajaxfileupload的版本早就不更新了,大家可以下载看:地址这里,它例子里使用的J ...

  7. ubuntu使用百度云盘插件

    Firefox 插件地址 https://addons.mozilla.org/zh-CN/firefox/addon/baidu-pan-exporter/ 安装后重启Firefox,然后百度云下载 ...

  8. vue总结 06组件

    组件基础 基本示例 这里有一个 Vue 组件的示例: // 定义一个名为 button-counter 的新组件Vue.component('button-counter', { data: func ...

  9. js权威指南---学习笔记01

    1.当函数赋值给对象的属性时,就变为了方法:2.被零整除不报错,只会返回无穷大(Infinity)或者负无穷大.例外:零除以零等于非数字(NaN).3.NaN与任何值都不相等! 4.Javascrip ...

  10. 20165203实验四 Andriod程序设计

    20165203实验四 Andriod程序设计 实验内容 安装 Android Stuidio 学习Android Stuidio调试应用程序 实验要求 1.没有Linux基础的同学建议先学习< ...