下拉刷新
-
继承已有的控件
-
继承ListView
-
给ListView 加个Header头部, 用来下拉刷新
- 静态的头部加上去
- 根据用户手指触摸, 动态的修改mHeaderView的paddingTop
- 手指抬起时, 根据当前状态,更新头布局
给ListView 加个Footer脚部, 用来加载更多
-
-
自定义头布局步骤:
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="360" > <shape android:innerRadiusRatio="2.5" android:shape="ring" android:thicknessRatio="10" android:useLevel="false" > <gradient android:centerColor="#FF6666" android:endColor="#FF0000" android:startColor="#FFFFFF" android:type="sweep" /> </shape> </rotate> * 在xml布局中使用: <ProgressBar android:id="@+id/pb_header_loading" android:layout_width="50dp" android:layout_height="50dp" android:indeterminateDrawable="@drawable/red_progress_bar" android:layout_gravity="center" />
-
MainActivity类的内容
public class MainActivity extends Activity {
private List<String> mListData;
private MyAdapter adapter;
private RefreshListView rlv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
rlv = (RefreshListView) findViewById(R.id.rlv);
setData(rlv);
rlv.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefreshRequest() {
System.out.println("用户有刷新请求");
new Thread(){
public void run() {
SystemClock.sleep(3000);
mListData.add(0, "这是下拉刷新出来的数据");
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
rlv.onRefreashFinish();
}
});
};
}.start();
}
@Override
public void onLoadMore() {
System.out.println("用户有加载更多请求");
new Thread(){
public void run() {
SystemClock.sleep(3000);
mListData.add("这是加载更多 出来的数据1");
mListData.add("这是加载更多 出来的数据2");
mListData.add("这是加载更多 出来的数据3");
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
rlv.onRefreashFinish();
}
});
};
}.start();
}
});
}
private void setData(RefreshListView rlv) {
// 初始化数据
mListData = new ArrayList<String>();
for (int i = 0; i < 30; i++) {
mListData.add("这是一条ListView的数据: " + i);
}
// 设置Header, 必须在setAdapter之前设置
// Button button = new Button(this);
// button.setText("这是一个按钮");
// rlv.addHeaderView(button);
//设置适配器
adapter = new MyAdapter();
rlv.setAdapter(adapter);
}
class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return mListData.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = new TextView(MainActivity.this);
textView.setTextSize(18);
textView.setText(mListData.get(position));
return textView;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
}
新建RefreshListView类继承ListView
/**
* 下拉刷新控件, 上拉加更多
* @author poplar
*
*/
public class RefreshListView extends ListView implements OnScrollListener{
private View mHeaderView; // 头布局
private int downY; // 按下时 Y 轴坐标值
private int mHeaderViewHeight;
private final int PULL_DOWN = 0; // 下拉刷新状态
private final int RELEASE_REFRESH = 1; // 释放刷新状态
private final int REFRESHING = 2; // 正在刷新中...
int currentState = PULL_DOWN;// 默认是下拉刷新状态
private ProgressBar pbHeaderLoading;
private TextView tvHeaderState;
private TextView tvHeaderLastRefreshTime;
private ImageView ivHeaderArrow;
private RotateAnimation rotateUp;
private RotateAnimation rotateDown;
private OnRefreshListener listener; // 监听回调
private View mFooterView; // 脚布局
private int mFooterViewHeight; //脚布局高度
private boolean isLoadMore = false; // 是否是加载更多
public RefreshListView(Context context) {
super(context);
init();
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
/**
* 初始化
* onMeasure -> onLayout -> onDraw
*
*/
private void init() {
initHeaderView();
initAnimation();
initFooterView();
setOnScrollListener(this);
}
/**
* 初始化脚布局
*/
private void initFooterView() {
mFooterView = View.inflate(getContext(), R.layout.layout_list_footer, null);
// 隐藏脚布局
mFooterView.measure(0, 0);
mFooterViewHeight = mFooterView.getMeasuredHeight();
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
this.addFooterView(mFooterView);
}
private void initAnimation() {
// 箭头向上的动画
rotateUp = new RotateAnimation(
0, -180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateUp.setDuration(500);
rotateUp.setFillAfter(true);
// 箭头向下的动画
rotateDown = new RotateAnimation(
-180, -360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateDown.setDuration(500);
rotateDown.setFillAfter(true);
}
/**
* 初始化Header, 头布局
*/
private void initHeaderView() {
mHeaderView = View.inflate(getContext(), R.layout.layout_list_header, null);
pbHeaderLoading = (ProgressBar) mHeaderView.findViewById(R.id.pb_header_loading);
pbHeaderLoading.setVisibility(View.INVISIBLE);
tvHeaderState = (TextView) mHeaderView.findViewById(R.id.tv_header_state);
tvHeaderLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_header_last_refresh_time);
ivHeaderArrow = (ImageView) mHeaderView.findViewById(R.id.iv_header_arrow);
int height = mHeaderView.getHeight(); // 只有在布局被绘制到界面上时, 才能获取到高度
// 获取测量的高度
mHeaderView.measure(0, 0); // 手动测量, 指定 宽高0,0. 表示按其默认设置测量.
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
System.out.println("height: " + height + "measuredHeight: " + mHeaderViewHeight);
// 设置内边距
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
addHeaderView(mHeaderView);
}
// 根据用户手指触摸, 动态的修改mHeaderView的paddingTop
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
// 如果正在刷新中, 返回, 按父类处理
if(currentState == REFRESHING){
return super.onTouchEvent(ev);
}
int moveY = (int) ev.getY();
// 获取Y轴的移动量
int diffY = moveY - downY;
// System.out.println("diffY: " + diffY);
// 当前ListView最上边能看到的条目, 是ListView所有条目的第一条.
if(diffY > 0 && getFirstVisiblePosition() == 0){
// 下拉, 把头布局显示出来
// int paddingTop= - 60 + (moveY - downY)
int paddingTop = - mHeaderViewHeight + diffY / 2;
mHeaderView.setPadding(0, paddingTop, 0, 0);
if(paddingTop > 0 && currentState != RELEASE_REFRESH){ // 刚刚不是释放刷新模式才进来
// 头布局已经完全显示了, 切换成 释放刷新 模式
System.out.println("切换 释放刷新 模式");
currentState = RELEASE_REFRESH;
updateHeaderView();
}else if(paddingTop < 0 && currentState != PULL_DOWN){// 刚刚不是下拉刷新模式才进来
// 头布局没有完全显示, 切换成 下拉刷新 模式
System.out.println("切换成 下拉刷新 模式");
currentState = PULL_DOWN;
updateHeaderView();
}
return true; // 子类处理的触摸事件, 不再传递给父类处理
}
break;
case MotionEvent.ACTION_UP:
// 手指抬起了
if(currentState == PULL_DOWN){
// 抬起时, 是下拉刷新状态, 隐藏
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
}else if(currentState == RELEASE_REFRESH){
// 抬起时, 是释放刷新, 恢复成初始大小
mHeaderView.setPadding(0, 0, 0, 0);
currentState = REFRESHING;
updateHeaderView();
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
/**
* 刷新结束时调用
*/
public void onRefreashFinish() {
if(isLoadMore){
System.out.println("加载更多完毕");
// 如果是加载更多, 恢复脚布局
isLoadMore = false;
// 隐藏脚布局
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
}else {
// 如果是下拉刷新, 恢复头布局
currentState = PULL_DOWN;
ivHeaderArrow.setVisibility(View.VISIBLE);
pbHeaderLoading.setVisibility(View.INVISIBLE);
tvHeaderState.setText("下拉刷新");
tvHeaderLastRefreshTime.setText("最后刷新时间: " + getLastRefreshTime());
// 隐藏头布局
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
}
}
// 2015-5-22 11:26:53
private String getLastRefreshTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = format.format(new Date());
return str;
}
// private final int PULL_DOWN = 0; // 下拉刷新状态
// private final int RELEASE_REFRESH = 1; // 释放刷新状态
// private final int REFRESHING = 2; // 正在刷新中...
/**
* 根据当前的状态更新 头布局
*/
private void updateHeaderView() {
switch (currentState) {
case PULL_DOWN:
// 更新 下拉刷新状态 时头布局
// 更新文本
tvHeaderState.setText("下拉刷新");
// 执行动画
ivHeaderArrow.startAnimation(rotateDown);
break;
case RELEASE_REFRESH:
// 更新 释放刷新状态 时头布局
// 更新文本
tvHeaderState.setText("释放刷新");
// 执行动画
ivHeaderArrow.startAnimation(rotateUp);
break;
case REFRESHING:
// 更新 正在刷新中...时头布局
ivHeaderArrow.clearAnimation();
ivHeaderArrow.setVisibility(View.INVISIBLE);
pbHeaderLoading.setVisibility(View.VISIBLE);
tvHeaderState.setText("正在刷新中...");
if(listener != null){
listener.onRefreshRequest();
}
break;
default:
break;
}
}
public void setOnRefreshListener(OnRefreshListener listener){
this.listener = listener;
}
// public static int SCROLL_STATE_IDLE = 0;
// public static int SCROLL_STATE_TOUCH_SCROLL = 1;
// public static int SCROLL_STATE_FLING = 2;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
System.out.println("scrollState");
// 当状态改变时候调用
if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING){
// 如果当前状态是 闲置/滑翔 状态
// 到最后一条
int lastVisiblePosition = getLastVisiblePosition();
System.out.println("lastVisiblePosition: " + lastVisiblePosition);
if(lastVisiblePosition == getCount() - 1){
// 显示脚布局
mFooterView.setPadding(0, 0, 0, 0);
// 让ListView滚动到最下边
setSelection(getCount());
isLoadMore = true;
if(listener != null){
listener.onLoadMore();
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 当状滑动时调用
}
/**
* 刷新接口,监听
* @author poplar
*
*/
public interface OnRefreshListener{
void onRefreshRequest();
void onLoadMore();
}
}