项目中如果需要实现下拉刷新一般有以下几个选择:
1.使用开源库Android-pullToRefresh。
2.使用support.v4包提供的SwipeRefreshLayout。
3.自己实现一个。
下面分别简单介绍:
注:以listView下拉刷新为例.
方案1:使用开源库Android-pullToRefresh
1.下载Android-PullToRefresh开源库(https://github.com/chrisbanes/Android-PullToRefresh)
2.将library工程导入到eclipse中
3.创建一个新工程,并在properties->android选项中引用library库
4.编写代码
该库提供了PullToRefreshListView,我们用它替换ListView,它在使用上跟ListView一致。
但是其提供了一个监听下拉更新的接口,我们通过setOnRefreshListener注册该监听器,然后在onRefresh回调方法中异步更新数据即可,更新完成后需调用onRefreshComplete方法完成更新。
示例:
页面布局:
<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="com.example.flushlistview.MainActivity" >
<com.handmark.pulltorefresh.library.PullToRefreshListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#19000000"
android:dividerHeight="4dp" >
</com.handmark.pulltorefresh.library.PullToRefreshListView>
</RelativeLayout>
listView的item布局:
<?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" >
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000"
android:textSize="20sp" />
</LinearLayout>
界面逻辑:
package com.example.flushlistview;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;
import com.handmark.pulltorefresh.library.PullToRefreshListView;
/**
* @author Rowandjj
*
*使用pull-to-refresh库实现下拉刷新操作
*/
public class MainActivity extends Activity
{
private PullToRefreshListView lv;
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (PullToRefreshListView) findViewById(R.id.lv);
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add("啊啊");
list.add("呵呵");
list.add("嘻嘻");
list.add("嘿嘿");
adapter = new ArrayAdapter<String>(this,
R.layout.item, R.id.tv, list);
lv.setAdapter(adapter);
//实现刷新接口
lv.setOnRefreshListener(new OnRefreshListener<ListView>()
{
@Override
public void onRefresh(PullToRefreshBase<ListView> refreshView)
{
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日hh时mm分ss秒",Locale.CHINA);
String updateTime = format.format(date);
refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(updateTime);
//异步刷新
new UpdateTask().execute();
}
});
}
private class UpdateTask extends AsyncTask<Void, Void,List<String>>
{
@Override
protected List<String> doInBackground(Void... params)
{
try
{
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
List<String> newData = new ArrayList<String>();
newData.add("新数据1");
newData.add("新数据2");
newData.add("新数据3");
return newData;
}
@Override
protected void onPostExecute(List<String> result)
{
adapter.addAll(result);
//刷新完成
lv.onRefreshComplete();
}
}
}
效果:
方案2:使用support.v4包提供的SwipeRefreshLayout
SwipeRefreshLayout是support.v4包提供的一个类,可以实现具有Material Design效果的下拉刷新。
使用方式上也很简单,只要将ListView/RecyclerView或者其他View放置在此layout之中,然后调用setOnRefreshListener注册数据更新接口,并在onRefresh方法中处理数据更新事件即可。当数据更新完毕,调用setRefreshing并将参数置为false即可。
注:
1. SwipeRefreshLayout只能包裹一个子View。
2.如果你的support.v4包中没有这个类,那么需要更新之。
示例:
页面布局:
<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="com.example.swiperefreshlayoutdemo.MainActivity" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/activity_main_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ListView
android:id="@+id/activity_main_listview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
item布局:
<?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:orientation="vertical" >
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="40dip"
android:textColor="#000"
android:gravity="center_vertical"
android:textSize="18sp" />
</LinearLayout>
页面逻辑:
package com.example.swiperefreshlayoutdemo;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends Activity
{
private ListView mListView;
private SwipeRefreshLayout mRefreshLayout;
private ArrayAdapter<String> mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.activity_main_listview);
mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.activity_main_swipe_refresh_layout);
List<String> data = new ArrayList<String>();
for (int i = 0; i < 15; i++)
{
data.add("这是数据" + i);
}
mAdapter = new ArrayAdapter<String>(this, R.layout.item, R.id.tv, data);
mListView.setAdapter(mAdapter);
mRefreshLayout.setOnRefreshListener(new OnRefreshListener()
{
@Override
public void onRefresh()
{
new UpdateTask().execute();
}
});
}
private class UpdateTask extends AsyncTask<Void, Void, List<String>>
{
@Override
protected List<String> doInBackground(Void... params)
{
try
{
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
List<String> newData = new ArrayList<String>();
newData.add("新数据1");
newData.add("新数据2");
newData.add("新数据3");
return newData;
}
@Override
protected void onPostExecute(List<String> result)
{
mAdapter.addAll(result);
//通知数据更新完毕
mRefreshLayout.setRefreshing(false);
}
}
}
效果:
方案3:自己实现一个
要想实现一个下拉刷新效果需要对android的事件分发、事件回调机制有清晰的认识。
这里我直接继承自ListView,然后通过实现OnScrollListener监听屏幕滚动事件,因为只有ListView滑动到首部才能下滑刷新,另外,需要重写onTouchEvent方法,根据手指位置动态更新布局。
我们通过分析传统的下拉刷新,发现有四种状态,1.正常态、2.下拉刷新态、3.释放刷新态、4.刷新态
当手指按下时,首先判断当前listView的第一个item是否可见,如果不可见那么不做任何处理,否则记下当前位置。
手指移动时,计算竖直方向的偏移量,然后根据偏移量改变当前状态,并根据当前状态更新布局。这里的布局指的是listView的headerView,当然,默认情况下,这个view是隐藏的,我们可以设置其padding为负的headerview的高度。
手指松开时,判断当前状态,如果是刷新态,那么调用回调接口处理更新逻辑。
先贴出代码:
package com.example.mypulltorefreshlistview.ui;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.mypulltorefreshlistview.R;
public class PullToRefreshListView extends ListView implements OnScrollListener
{
private static final String TAG = "PullToRefreshListView";
/**
*顶部布局
*/
private View mHeaderView;
/**
* header的高度
*/
private int mHeaderHeight;
/**
* 当前页面已经滑到顶部
*/
private boolean flag;
/**
* 初始滑动时的y坐标
*/
private int mStartY;
/**
* 正常状态
*/
public static final int STATE_NORMAL = 0;
/**
* 下拉刷新状态
*/
public static final int STATE_PULL_TO_REFRESH = 1;
/**
* 释放刷新状态
*/
public static final int STATE_RELEASE_TO_REFRESH = 2;
/**
* 正在刷新状态
*/
public static final int STATE_REFRESH = 3;
/**
* 当前状态
*/
private int mCurrentState;
private static final int DEFAULT_LENGTH = 70;
private OnRefreshListener mRefreshListener;
public PullToRefreshListView(Context context, AttributeSet attrs,
int defStyle)
{
super(context, attrs, defStyle);
init(context);
}
public PullToRefreshListView(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context);
}
public PullToRefreshListView(Context context)
{
super(context);
init(context);
}
public void setOnRefreshListener(OnRefreshListener listener)
{
this.mRefreshListener = listener;
}
/**
* 初始化操作
* @param context
*/
private void init(Context context)
{
//添加headerview
mHeaderView = LayoutInflater.from(context).inflate(R.layout.header_layout,null);
this.addHeaderView(mHeaderView);
//设置滚动监听器
this.setOnScrollListener(this);
//通过设置padding将hider隐藏
//注:因为此时无法获得header的高度,所以放入MessageQueue中
post(new HideHeaderAction());
}
/**
* 设置header的padding
* @param topPadding
*/
private void setHeaderTopPadding(int topPadding)
{
if(mHeaderView != null)
{
mHeaderView.setPadding(mHeaderView.getPaddingLeft(),topPadding,mHeaderView.getPaddingRight(),mHeaderView.getPaddingBottom());
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount)
{
if(firstVisibleItem == 0)
flag = true;
else
flag = false;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
}
@Override
public boolean onTouchEvent(MotionEvent ev)
{
int action = ev.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
if(flag)
{
mStartY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
performMove(ev);
break;
case MotionEvent.ACTION_UP:
if(mCurrentState == STATE_RELEASE_TO_REFRESH)
{
mCurrentState = STATE_REFRESH;
updateUIByState();
//TODO 加载新数据
if(mRefreshListener == null)
{
throw new RuntimeException("you must call setOnRefreshListener before...");
}else
{
mRefreshListener.onRefresh(this);
}
}else if(mCurrentState == STATE_PULL_TO_REFRESH)
{
mCurrentState = STATE_NORMAL;
flag = false;
updateUIByState();
}
break;
}
return super.onTouchEvent(ev);
}
private void updateUIByState()
{
TextView tip = (TextView) findViewById(R.id.tip);
ImageView arrow = (ImageView) findViewById(R.id.arrow);
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress);
Animation anim1 = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_1);
Animation anim2 = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_2);
switch (mCurrentState)
{
case STATE_NORMAL:
setHeaderTopPadding(-mHeaderHeight);
break;
case STATE_PULL_TO_REFRESH:
arrow.clearAnimation();
arrow.setAnimation(anim1);
arrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
tip.setText("下拉可以刷新...");
break;
case STATE_RELEASE_TO_REFRESH:
arrow.clearAnimation();
arrow.setAnimation(anim2);
arrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
tip.setText("松开可以刷新...");
break;
case STATE_REFRESH:
arrow.clearAnimation();
setHeaderTopPadding(mHeaderHeight);
arrow.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
tip.setText("正在刷新...");
break;
default:
break;
}
}
private void performMove(MotionEvent ev)
{
if(!flag)
{
return;
}
int currY = (int) ev.getY();
int deltaY = currY-mStartY;
if(deltaY > mHeaderHeight+DEFAULT_LENGTH)
deltaY = mHeaderHeight+DEFAULT_LENGTH;
switch (mCurrentState)
{
case STATE_NORMAL:
if(deltaY > 0)
{
mCurrentState = STATE_PULL_TO_REFRESH;
}
break;
case STATE_PULL_TO_REFRESH:
setHeaderTopPadding(deltaY-mHeaderHeight);
updateUIByState();
if(deltaY >= mHeaderHeight+DEFAULT_LENGTH)
{
mCurrentState = STATE_RELEASE_TO_REFRESH;
}else if(deltaY <= 0)
{
mCurrentState = STATE_NORMAL;
}
break;
case STATE_RELEASE_TO_REFRESH:
// setHeaderTopPadding(deltaY-mHeaderHeight);
updateUIByState();
if(deltaY < mHeaderHeight+DEFAULT_LENGTH)
{
mCurrentState = STATE_PULL_TO_REFRESH;
}else if(deltaY <= 0)
{
mCurrentState = STATE_NORMAL;
}
break;
}
}
/**
* 更新完毕时调用
*/
public void refreshComplete()
{
TextView lastUpdateTime = (TextView) findViewById(R.id.last_update_time);
SimpleDateFormat format = new SimpleDateFormat("MM-dd hh:mm",Locale.CHINA);
String updateTime = format.format(new Date(System.currentTimeMillis()));
lastUpdateTime.setText("更新于 "+updateTime);
mCurrentState = STATE_NORMAL;
updateUIByState();
}
public interface OnRefreshListener
{
public void onRefresh(PullToRefreshListView listView);
}
private class HideHeaderAction implements Runnable
{
@Override
public void run()
{
//获取header的高度
mHeaderHeight = mHeaderView.getMeasuredHeight();//view在被渲染前无法获得其宽高
Log.d(TAG,"Headerheight:"+mHeaderHeight);
//通过设置header的padding来隐藏header
setHeaderTopPadding(-mHeaderHeight);
}
}
}
使用上跟上面差不多,也是需要添加监听器,然后处理回调方法,数据更新完调用refreshComplete。
当然,这个只是一个简单的demo,效果可能不是很好,仅供参考~