开发中最常用的下拉刷新控件
package com.pull.refresh;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class Pull2RefreshListView extends ListView {
/**
* 下拉刷新状态
*/
public static final int PULL_REFRESH_STATE = 0x1;
/**
* 松开刷新状态
*/
public static final int DISENTANGLE_REFRESH_STATE = 0x2;
/**
* 刷新状态
*/
public static final int REFRESHING_STATE = 0x3;
/**
* 当前状态
*/
private int mCurrentState = PULL_REFRESH_STATE;
/**
* 头布局
*/
private View mHeaderView;
/**
* 箭头
*/
private ImageView arrow;
/**
* 进度圆
*/
private ProgressBar round;
/**
* 下拉刷新
*/
private TextView pull;
/**
* 时间
*/
private TextView date;
/**
* 布局高度
*/
private int mHeight;
/**
* 按下时竖直方向的开始坐标
*/
private int startY;
/**
* 向上旋转动画
*/
private RotateAnimation animUp;
/**
* 向下旋转动画
*/
private RotateAnimation animDown;
public Pull2RefreshListView(Context context) {
this(context, null);
}
public Pull2RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Pull2RefreshListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView() {
mHeaderView = View.inflate(getContext(), R.layout.list_header, null);
arrow = (ImageView) mHeaderView.findViewById(R.id.iv_arrow);
round = (ProgressBar) mHeaderView.findViewById(R.id.pb_round);
pull = (TextView) mHeaderView.findViewById(R.id.tv_pull);
date = (TextView) mHeaderView.findViewById(R.id.tv_date);
this.addHeaderView(mHeaderView);
initData();
initAnim();
}
/**
* 初始化数据
*/
private void initData() {
mHeaderView.measure(0, 0);
mHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -mHeight, 0, 0);
}
/**
* 滑动事件
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:// 按下获取竖直方向的开始坐标
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:// 移动获取竖直方西的差值
if (startY == -1) {
// 当用户按住头条新闻的viewpager进行下拉时,ACTION_DOWN可能被消费掉,
// 导致startY没有赋值,此处需要重新获取一下
startY = (int) ev.getY();
}
if (mCurrentState == REFRESHING_STATE) {
// 如果是正在刷新, 跳出循环
break;
}
int endY = (int) ev.getY();
int dy = endY - startY;
int firstVisiblePosition = getFirstVisiblePosition();// 当前显示的第一个item的位置
int padding = -mHeight + dy;// 计算当前下拉控件的padding值
if (padding > 20) {//限制下拉的距离
padding = 20;
}
// 必须下拉,并且当前显示的是第一个item
if (dy > 0 && firstVisiblePosition == 0) {
mHeaderView.setPadding(0, padding, 0, 0);
if (padding > 0 && mCurrentState != DISENTANGLE_REFRESH_STATE) {// 松开刷新,把当前状态改为松开刷新
mCurrentState = DISENTANGLE_REFRESH_STATE;
refreshState();
} else if (padding < 0 && mCurrentState != PULL_REFRESH_STATE) {// 下拉刷新状态,把当前状态改为改为下拉刷新
mCurrentState = PULL_REFRESH_STATE;
refreshState();
}
return true;
}
break;
case MotionEvent.ACTION_UP:// 抬起
startY = -1;
if (mCurrentState == DISENTANGLE_REFRESH_STATE) {
mCurrentState = REFRESHING_STATE;
refreshState();
// 完整展示头布局
mHeaderView.setPadding(0, 0, 0, 0);
// 进行回调
if (listener != null) {
listener.onRefresh();
}
} else if (mCurrentState == PULL_REFRESH_STATE) {
// 隐藏头布局
mHeaderView.setPadding(0, -mHeight, 0, 0);
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
/**
* 根据当前状态刷新界面
*/
private void refreshState() {
switch (mCurrentState) {
case PULL_REFRESH_STATE:
pull.setText("下拉刷新");
round.setVisibility(View.INVISIBLE);
arrow.setVisibility(View.VISIBLE);
arrow.startAnimation(animDown);
break;
case DISENTANGLE_REFRESH_STATE:
pull.setText("松开刷新");
round.setVisibility(View.INVISIBLE);
arrow.setVisibility(View.VISIBLE);
arrow.startAnimation(animUp);
break;
case REFRESHING_STATE:
pull.setText("正在刷新...");
arrow.clearAnimation();// 清除箭头动画,否则无法隐藏
round.setVisibility(View.VISIBLE);
arrow.setVisibility(View.INVISIBLE);
break;
default:
break;
}
}
/**
* 设置箭头动画
*/
private void initAnim() {
animUp = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
animUp.setDuration(200);
animUp.setFillAfter(true);
animDown = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animDown.setDuration(200);
animDown.setFillAfter(true);
}
/**
* 下拉刷新的回调接口
*/
public interface OnRefreshListener {
/**
* 刷新状态时调用
*/
public void onRefresh();
/**
* 下拉加载更多
*/
public void onLoadMore();
}
/**
* 接口引用
*/
private OnRefreshListener listener;
/**
* 暴露接口,设置监听
*/
public void setOnRefreshListener(OnRefreshListener listener) {
this.listener = listener;
}
/**
* 刷新结束,隐藏头布局,还原状态
*/
public void onRefreshComplete() {
mHeaderView.setPadding(0, -mHeight, 0, 0);
mCurrentState = PULL_REFRESH_STATE;
pull.setText("下拉刷新");
round.setVisibility(View.INVISIBLE);
arrow.setVisibility(View.VISIBLE);
}
}
头布局
<?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="horizontal" >
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp" >
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/common_listview_headview_red_arrow" />
<ProgressBar
android:id="@+id/pb_round"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"/>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_pull"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="下拉刷新"
android:textSize="20dp" />
<TextView
android:id="@+id/tv_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="2016-09-02 15-23-43"
android:textSize="16dp" />
</LinearLayout>
</LinearLayout>