下拉刷新的操作流程:
1.用户手指在ListView页面按下并下拉
2.出现一个提示View在listView顶部
3.listView内容更新,顶部view显示后隐藏
具体实现步骤
创建继承Listview的RefreshListView,并添加顶部提示view
public class RefreshListView extends ListView {
View header;// 顶部提示View
public RefreshListView(Context context) {
super(context);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
// LayoutInflater作用是加载布局
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.header_layout, null);
this.addHeaderView(header);
super.setOnScrollListener(this);
}
}
顶部提示View布局文件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="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dip"
android:paddingBottom="10dip"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/layout"
android:layout_centerInParent="true"
android:gravity="center"
>
<!-- 提示文字 -->
<TextView
android:id="@+id/tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉可以刷新"
/>
<!-- 距上次更新至今的时间 -->
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<!-- 箭头 -->
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/layout"
android:src="@drawable/pull_to_refresh_arrow"
/>
<!-- 更新进度条 -->
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleSmall"
android:layout_toLeftOf="@id/layout"
android:visibility="gone"
/>
</RelativeLayout>
</LinearLayout>
运行效果图
默认加载的header是显示出来的,应该隐藏,代码如下
private void initView(Context context){
//LayoutInflater作用是加载布局
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.header_layout, null);
measureView(header);
headerHeight = header.getMeasuredHeight();
topPadding(-headerHeight);
this.addHeaderView(header);
super.setOnScrollListener(this);
}
/**
* 设置顶部布局的上边距
* @param topPadding
*/
private void topPadding(int topPadding){
//设置顶部提示的边距
//除了顶部用参数值topPadding外,其他三个用header默认的值
header.setPadding(header.getPaddingLeft(), topPadding,
header.getPaddingRight(), header.getPaddingBottom());
//使header无效,将来调用onDraw()重绘View
header.invalidate();
}
/**
* 通知父布局,占用的宽和高
*/
private void measureView(View view){
//LayoutParams are used by views to tell their parents
//how they want to be laid out.
//LayoutParams被view用来告诉它们的父布局它们应该被怎样安排
ViewGroup.LayoutParams p = view.getLayoutParams();
if(p==null){
//两个参数:width,height
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
//getChildMeasureSpec:获取子View的widthMeasureSpec、heightMeasureSpec值
int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int height;
int tempHeight = p.height;
if(tempHeight>0){
height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
}else{
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(width, height);
}
效果图
监听用户滑动屏幕操作
1.实现OnScrollListener接口
2.根据用户按下,移动,抬起手势来设置不同的响应事件
3.顶部提示View---header有四种状态
1.正常状态(NONE)
2.提示用户下拉可以刷新(PULL)
3.提示用户释放可以刷新(RELEASE)
4.提示用户正在刷新状态(REFREASH)
public class RefreshListView extends ListView implements OnScrollListener{
View header;//下拉刷新时出现的顶部布局View
int headerHeight;//header的高度
int firstVisibleItem;//当前界面第一个可见的item的位置
boolean isFlag;//标志,是在当前显示的listView是在listView最顶端时按下额
int startY;//用户按下的Y值
int state;//当前状态
final int NONE = 0;//正常状态
final int PULL = 1;//提示下拉状态
final int RELEASE = 2;//提示释放状态
final int REFRESH = 3;//提示正在刷新状态
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
this.scrollState = scrollState;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
this.firstVisibleItem = firstVisibleItem;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if(firstVisibleItem == 0){
isFlag = true;//ListView最顶端按下,标志值设为真
startY = (int)ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if(state == RELEASE){
state = REFRESH;
//加载数据
refreshViewByState();
iRefreshlistener.onRefresh();
}else if(state == PULL){
state = NONE;
isFlag = false;
refreshViewByState();
}
break;
}
return super.onTouchEvent(ev);
onMove()方法----根据用户下拉距离的不同设置不同的响应方法
private void onMove(MotionEvent ev){
//如果不是最顶端按下,则直接返回
if(!isFlag){
return;
}
int currentY = (int)ev.getY();//当前的Y值
int space = currentY - startY;//用户向下拉的距离
int topPadding = space - headerHeight;//顶部提示View距顶部的距离值
switch (state) {
//正常状态
case NONE:
if(space>0){
state = PULL;//下拉的距离大于0,则将状态改为PULL(提示下拉更新)
refreshViewByState();//根据状态的不同更新View
}
break;
case PULL:
topPadding(topPadding);
if(space>headerHeight+30//下拉的距离大于header的高度加30且用户滚动屏幕,手指仍在屏幕上
&&scrollState == SCROLL_STATE_TOUCH_SCROLL ){
state = RELEASE;//将状态改为RELEASE(提示松开更新)
refreshViewByState();
}
break;
case RELEASE:
topPadding(topPadding);
if(space<headerHeight+30){//用户下拉的距离不够
state = PULL; //将状态改为PULL
refreshViewByState();
}else if(space<=0){ //用户下拉的距离为非正值
state = NONE; //将状态改为NONE
isFlag = false; //标志改为false
refreshViewByState();
}
break;
}
}
根据不同状态,改变用户界面的显示
/**
* 根据当前状态state,改变界面显示
* state:
* NONE:无操作
* PULL:下拉可以刷新
* RELEASE:松开可以刷新
* REFREASH:正在刷新
*/
private void refreshViewByState(){
//提示
TextView tips = (TextView)header.findViewById(R.id.tips);
//箭头
ImageView arrow = (ImageView)header.findViewById(R.id.arrow);
//进度条
ProgressBar progress = (ProgressBar)header.findViewById(R.id.progress);
//箭头的动画效果1,由0度转向180度,即箭头由朝下转为朝上
RotateAnimation animation1 = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF,0.5f,
RotateAnimation.RELATIVE_TO_SELF,0.5f);
animation1.setDuration(500);
animation1.setFillAfter(true);
//箭头的动画效果2,由180度转向0度,即箭头由朝上转为朝下
RotateAnimation animation2 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF,0.5f,
RotateAnimation.RELATIVE_TO_SELF,0.5f);
animation2.setDuration(500);
animation2.setFillAfter(true);
switch (state) {
case NONE: //正常状态
arrow.clearAnimation(); //清除箭头动画效果
topPadding(-headerHeight); //设置header距离顶部的距离
break;
case PULL: //下拉状态
arrow.setVisibility(View.VISIBLE); //箭头设为可见
progress.setVisibility(View.GONE); //进度条设为不可见
tips.setText("下拉可以刷新"); //提示文字设为"下拉可以刷新"
arrow.clearAnimation(); //清除之前的动画效果,并将其设置为动画效果2
arrow.setAnimation(animation2);
break;
case RELEASE: //下拉状态
arrow.setVisibility(View.VISIBLE); //箭头设为可见
progress.setVisibility(View.GONE); //进度条设为不可见
tips.setText("松开可以刷新"); //提示文字设为"松开可以刷新"
arrow.clearAnimation(); //清除之前的动画效果,并将其设置为动画效果2
arrow.setAnimation(animation1);
break;
case REFRESH: //更新状态
topPadding(50); //距离顶部的距离设置为50
arrow.setVisibility(View.GONE); //箭头设为不可见
progress.setVisibility(View.VISIBLE); //进度条设为可见
tips.setText("正在刷新..."); //提示文字设为""正在刷新..."
arrow.clearAnimation(); //清除动画效果
break;
}
}
更新数据 由于RefreshListView中不能直接更新数据,必须设置回调接口来实现更新数据功能
IRefreshListener iRefreshlistener;//刷新数据的接口
...
public void setInterface(IRefreshListener listener){
this.iRefreshlistener = listener;
}
/**
* 刷新数据接口
* @author lenovo
*
*/
public interface IRefreshListener{
public void onRefresh();
}
在MainActivity中实现RefreshListView接口并实现onRefresh()方法
public class MainActivity extends Activity implements IRefreshListener{
......
@Override
public void onRefresh() {
// TODO Auto-generated method stub
//handler设置刷新延时效果
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//获取最新数据
getRefreshData();
//通知界面显示
adapter.notifyDataSetChanged();
//通知listView刷新数据完毕
listView.refreshComplete();
}
}, 2000);
}
刷新完成的操作:
public void refreshComplete(){
state = NONE; //状态设为正常状态
isFlag = false; //标志设为false
refreshViewByState();
//设置提示更新时间间隔
Time t = new Time();
t.setToNow();
time = t.hour*60+t.minute-updateTime;
updateTime = t.hour*60+t.minute;
TextView lastUpdateTime = (TextView)findViewById(R.id.time);
lastUpdateTime.setText(time+"分钟前更新");
}
运行效果图