Android 自定义下拉刷新列表
1. 自定义ListView
自定义CListView
继承ListView
,实现AbsListView.OnScrollListener
接口。CListView
添加CListViewHeaderView
作为表头,使用表头显示状态,在加入表头以后列表的数量是Adapter
的数量加上1。CListView
捕捉手势操作,监听滚动事件并设置刷新事件。
public class CListView extends ListView implements AbsListView.OnScrollListener {
private CListViewHeaderView mHeaderView;
private int mFirstItemIndex;
private float mStartY, mCurrentY;
private boolean mRefreshEnable, mRecord;
public CListView(Context context) {
this(context, null);
}
public CListView(Context context, AttributeSet attrs) {
super(context, attrs);
mHeaderView = new CListViewHeaderView(context);
mHeaderView.setListView(this);
addHeaderView(mHeaderView);
mHeaderView.refreshFinish();
setOnScrollListener(this);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mFirstItemIndex = firstVisibleItem;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 保证触摸的y值只记录一次
if (!mRecord) {
mStartY = event.getY();
mRecord = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (!mRecord) {
mStartY = event.getY();
mRecord = true;
}
mCurrentY = event.getY();
if (mRefreshEnable && mFirstItemIndex == 0) {
mHeaderView.move((int) (mCurrentY - mStartY));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mCurrentY = event.getY();
if (mRefreshEnable && mFirstItemIndex == 0 && mRecord) {
mHeaderView.release((int) (mCurrentY - mStartY));
}
mRecord = false;
break;
}
return super.onTouchEvent(event);
}
/*
* 刷新回调
*/
public void refreshFinish() {
mHeaderView.refreshFinish();
}
public void setOnRefreshListener(IOnRefreshListener refreshListener) {
mRefreshEnable = (refreshListener != null);
mHeaderView.setOnRefreshListener(refreshListener);
}
public interface IOnRefreshListener {
void onRefreshStart();
}
}
2. 列表头CListViewHeaderView
CListViewHeaderView
拥有四种状态
-
DONE
,初始状态 -
PULL_TO_REFRESH
,下拉刷新 -
RELEASE_TO_REFRESH
,松开刷新 -
REFRESHING
,刷新中
CListViewHeaderView
默认是初始状态,当进行下拉操作时,显示为下拉刷新。下拉超过3倍表头高度,显示松开刷新。正在刷新时不能进行界面操作。
public class CListViewHeaderView extends LinearLayout {
private final static int DONE = 0;
private final static int PULL_TO_REFRESH = 1; // 下拉刷新
private final static int RELEASE_TO_REFRESH = 2; // 放手刷新
private final static int REFRESHING = 3; // 刷新
private final static int RATIO = 3;
private ImageView mIvArrow;
private ProgressBar mProgressBarRefresh;
private TextView mTvRefreshTips;
private Animation mAnimCycle, mAnimReverse;
private String mPullToRefresh, mReleaseToRefresh, mRefreshing;
private int mContentHeight;
private int mHeadState = DONE;
private boolean mReverse; // 图标是否旋转
private CListView mListView;
private CListView.IOnRefreshListener mListener;
public CListViewHeaderView(Context context) {
this(context, null);
}
public CListViewHeaderView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.list_view_header_view, this);
mIvArrow = findViewById(R.id.iv_arrow);
mProgressBarRefresh = findViewById(R.id.progress_bar_refresh);
mTvRefreshTips = findViewById(R.id.tv_refresh_tips);
mAnimCycle = AnimationUtils.loadAnimation(context, R.anim.anim_list_view_rotate);
mAnimReverse = AnimationUtils.loadAnimation(context, R.anim.anim_list_view_reverse);
mContentHeight = context.getResources().getDimensionPixelOffset(R.dimen.head_view_height);
mPullToRefresh = context.getString(R.string.list_view_pull_to_refresh);
mReleaseToRefresh = context.getString(R.string.list_view_release_to_refresh);
mRefreshing = context.getString(R.string.list_view_refreshing);
}
void setListView(CListView listView) {
this.mListView = listView;
}
void move(int distance) {
if (mHeadState != REFRESHING) {
if (mHeadState == DONE) {
if (distance > 0) {
mHeadState = PULL_TO_REFRESH;
changeHeaderViewByState();
}
}
if (mHeadState == PULL_TO_REFRESH) {
if (distance >= RATIO * mContentHeight) {
mHeadState = RELEASE_TO_REFRESH;
mReverse = true;
changeHeaderViewByState();
} else if (distance <= 0) {
mHeadState = DONE;
changeHeaderViewByState();
}
}
if (mHeadState == RELEASE_TO_REFRESH) {
if (distance <= 0) {
mHeadState = DONE;
changeHeaderViewByState();
} else if (distance < RATIO * mContentHeight) {
mHeadState = PULL_TO_REFRESH;
changeHeaderViewByState();
}
}
if (mHeadState == PULL_TO_REFRESH || mHeadState == RELEASE_TO_REFRESH) {
if (mListView != null) {
mListView.setSelection(0);
}
setPadding(0, distance / RATIO - mContentHeight, 0, 0);
}
}
}
void release(float distance) {
if (distance >= RATIO * mContentHeight) {
mHeadState = REFRESHING;
} else {
mHeadState = DONE;
}
changeHeaderViewByState();
if (mHeadState == REFRESHING) {
mListener.onRefreshStart();
}
}
void setOnRefreshListener(CListView.IOnRefreshListener listener) {
this.mListener = listener;
}
public void refreshFinish() {
mHeadState = DONE;
changeHeaderViewByState();
}
private void changeHeaderViewByState() {
switch (mHeadState) {
case DONE:
setPadding(0, -1 * mContentHeight, 0, 0);
mIvArrow.clearAnimation();
mProgressBarRefresh.setVisibility(View.GONE);
mTvRefreshTips.setText(mPullToRefresh);
break;
case PULL_TO_REFRESH:
mIvArrow.setVisibility(View.VISIBLE);
mIvArrow.clearAnimation();
mProgressBarRefresh.setVisibility(View.GONE);
mTvRefreshTips.setText(mPullToRefresh);
// 是由RELEASE_To_REFRESH状态转变来的
if (mReverse) {
mReverse = false;
mIvArrow.startAnimation(mAnimReverse);
}
break;
case RELEASE_TO_REFRESH:
mIvArrow.setVisibility(View.VISIBLE);
mIvArrow.clearAnimation();
mIvArrow.startAnimation(mAnimCycle);
mProgressBarRefresh.setVisibility(View.GONE);
mTvRefreshTips.setText(mReleaseToRefresh);
break;
case REFRESHING:
setPadding(0, 0, 0, 0);
mIvArrow.clearAnimation();
mIvArrow.setVisibility(View.GONE);
mProgressBarRefresh.setVisibility(View.VISIBLE);
mTvRefreshTips.setText(mRefreshing);
break;
}
}
}
资源文件list_view_header_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/header_view"
android:layout_width="match_parent"
android:layout_height="@dimen/head_view_height"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="12dp"
android:layout_height="12dp"
android:src="@drawable/list_arrow"
android:scaleType="fitCenter"
android:visibility="gone"/>
<ProgressBar
android:id="@+id/progress_bar_refresh"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<TextView
android:id="@+id/tv_refresh_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:textSize="12sp"
android:textColor="#FF000000"/>
</LinearLayout>
</LinearLayout>
3. 界面调用
在Activity
中添加CListView.IOnRefreshListener
监听器,并在3秒后结束。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
... ...
final CListView listView = findViewById(R.id.list_view);
listView.setAdapter(new ArrayAdapter<>(this,
R.layout.list_view_item, R.id.tv_name, getResources().getStringArray(R.array.month)));
listView.setOnRefreshListener(new CListView.IOnRefreshListener() {
@Override
public void onRefreshStart() {
listView.postDelayed(new Runnable() {
@Override
public void run() {
listView.refreshFinish();
}
}, 3000);
}
});
}