Android自定義上拉載入下拉重新整理PullToRefreshListView
阿新 • • 發佈:2018-12-15
轉自http://blog.csdn.net/allen315410/article/details/39965327
最近專案中要用到重新整理和載入的功能,網上查了有好多關於重新整理和載入的例子,挑了一個比較好的,有些地方更完善了些。例如下拉不完全時沒有做處理,還有載入後滑動有些問題都一一解決了。話不多說,先看效果圖,後直接上程式碼了。
RefreshListView:
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
private final static String TAG = "RefreshListView";
private View mHeaderView;//頭佈局物件
private ImageView mImageViewArror;//頭佈局的箭頭
private ProgressBar mProgressBar;//進度條
private TextView mTextViewState;//頭佈局的狀態
private TextView mTextViewTime;//頭佈局的時間
private int mHeaderViewHeight;//mHeaderView的高度
private Animation mUpAnimation;//頂部的動畫
private Animation mDownAnimation;//底部的動畫
private View mFooterView;//腳佈局物件
private int mFooterViewHeight;//mFooterView的高度
private boolean mIsScrollToBottom;//是否滑動到底部
private boolean mIsLoadingMore = false;//是否正在載入更多中
private OnRefreshListener mOnRefreshListener;
private int mDownY;//按下時y軸的偏移量
private final static int DOWM_PULL_REFRESH = 0;//下拉重新整理狀態
private final static int RELEASE_REFRESH = 1;//鬆開重新整理
private final static int REFRESHING = 2;//正在重新整理
private int currentState = DOWM_PULL_REFRESH;//當前佈局的狀態,預設為下拉重新整理狀態
private int mFirstVisibleItemPosition;//螢幕顯示在第一個的item的索引
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initFooterView();
this.setOnScrollListener(this);
}
/**
* 初始化頭佈局
*/
private void initHeaderView() {
mHeaderView = View.inflate(getContext(), R.layout.header, null);
mImageViewArror = (ImageView) mHeaderView.findViewById(R.id.iv_header);
mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.progressBar);
mTextViewState = (TextView) mHeaderView.findViewById(R.id.tv_header_state);
mTextViewTime = (TextView) mHeaderView.findViewById(R.id.tv_header_time);
//設定最後的重新整理時間
mTextViewTime.setText("最後重新整理時間:" + getLastUpdateTime());
mHeaderView.measure(0, 0);//系統會幫我們測量出mHeaderView的高度
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
this.addHeaderView(mHeaderView);//向ListView的頂部新增一個View物件
initAnimation();
}
/**
* 獲取系統最新時間
*
* @return
*/
@TargetApi(Build.VERSION_CODES.N)
private String getLastUpdateTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
return format.format(System.currentTimeMillis());
}
/**
* 初始化動畫
*/
private void initAnimation() {
mUpAnimation = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation
.RELATIVE_TO_SELF, 0.5f);
mUpAnimation.setDuration(500);
mUpAnimation.setFillAfter(true);//動畫結束後,停留在結束的位置上
mDownAnimation = new RotateAnimation(-180f, -360f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mDownAnimation.setDuration(500);
mDownAnimation.setFillAfter(true);
}
/**
* 初始化腳佈局
*/
private void initFooterView() {
mFooterView = View.inflate(getContext(), R.layout.loading, null);
mFooterView.measure(0, 0);
mFooterViewHeight = mFooterView.getMeasuredHeight();
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
this.addFooterView(mFooterView);
}
/**
* 當滾動狀態改變時回撥
*/
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) {
//判斷當前是否已經載入到了底部
if (mIsScrollToBottom && !mIsLoadingMore) {
mIsLoadingMore = true;
//當前到底部
Log.i(TAG, "載入更多資料");
mFooterView.setPadding(0, 0, 0, 0);
this.setSelection(this.getCount());
if (mOnRefreshListener != null) {
mOnRefreshListener.onLoadingMore();
}
}
}
}
/**
* 當滾動時呼叫
*
* @param firstVisibleItem 當前螢幕顯示在頂部的item的position
* @param visibleItemCount 當前螢幕顯示了多少個條目的總數
* @param totalItemCount ListView的總條目數
*/
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int
totalItemCount) {
mFirstVisibleItemPosition = firstVisibleItem;
if (getLastVisiblePosition() == (totalItemCount - 1) && !mIsLoadingMore) {
mIsScrollToBottom = true;
} else {
mIsScrollToBottom = false;
}
}
public interface OnRefreshListener {
/**
* 下拉重新整理
*/
void onDownPullToRefresh();
/**
* 上拉載入更多
*/
void onLoadingMore();
}
/**
* 設定重新整理監聽事件
*/
public void setmOnRefreshListener(OnRefreshListener listener) {
this.mOnRefreshListener = listener;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
//移動後的y-按下的y = 間距
int distanceY = (moveY - mDownY) / 2;
//-頭佈局的高度+間距=paddingTop
int paddingTop = -mHeaderViewHeight + distanceY;
//如果:- 頭佈局的高度>paddingTop的值,執行super.onTouchEvent(ev);
if (mFirstVisibleItemPosition == 0 && -mHeaderViewHeight < paddingTop) {
if (paddingTop > 0 && currentState == DOWM_PULL_REFRESH) {//完全顯示了
Log.i(TAG, "鬆開重新整理");
currentState = RELEASE_REFRESH;
refreshHeaderView();
} else if (paddingTop < 0 && currentState == RELEASE_REFRESH) {
Log.i(TAG, "下拉重新整理");
currentState = DOWM_PULL_REFRESH;
refreshHeaderView();
}
//下拉頭佈局
mHeaderView.setPadding(0, paddingTop, 0, 0);
return true;
}
break;
case MotionEvent.ACTION_UP:
//判斷當前狀態是鬆開重新整理還是下拉重新整理
switch (currentState) {
case RELEASE_REFRESH:
Log.i(TAG, "重新整理資料");
//把頭佈局設定為完全顯示狀態
mHeaderView.setPadding(0, 0, 0, 0);
//進入到正在重新整理中狀態
currentState = REFRESHING;
refreshHeaderView();
if (mOnRefreshListener != null) {
mOnRefreshListener.onDownPullToRefresh();//呼叫使用者的監聽方法
}
}
break;
case DOWM_PULL_REFRESH:
//隱藏頭佈局
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
break;
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
/**
* 根據currentState重新整理頭佈局
*/
private void refreshHeaderView() {
switch (currentState) {
case DOWM_PULL_REFRESH://下拉重新整理
mTextViewState.setText("下拉重新整理");
mImageViewArror.startAnimation(mDownAnimation);
break;
case RELEASE_REFRESH://鬆開重新整理
mTextViewState.setText("鬆開重新整理");
mImageViewArror.startAnimation(mUpAnimation);
break;
case REFRESHING://正在重新整理
mImageViewArror.clearAnimation();
mImageViewArror.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
mTextViewState.setText("正在重新整理中...");
break;
}
}
/**
* 隱藏頭佈局
*/
public void hideHeaderView() {
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
mImageViewArror.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
mTextViewState.setText("下拉重新整理");
mTextViewTime.setText("最後重新整理時間:" + getLastUpdateTime());
currentState = DOWM_PULL_REFRESH;
}
public void hideFooterView() {
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
mIsLoadingMore = false;
}
}
MainActivity:
public class MainActivity extends AppCompatActivity implements RefreshListView.OnRefreshListener {
private RefreshListView mRefreshListView;
private List<String> mData;
private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRefreshListView = (RefreshListView) findViewById(R.id.refresh_listview);
mData = new ArrayList<>();
for(int i = 0;i < 25 ; i++){
mData.add(i,"這是一條listview的資料"+i);
}
mAdapter = new MyAdapter(mData,this);
mRefreshListView.setAdapter(mAdapter);
mRefreshListView.setmOnRefreshListener(this);
}
@Override
public void onDownPullToRefresh() {
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(2000);
for (int i = 0; i < 2;i++) {
mData.add(0,"這是下拉刷新出來的資料"+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
mAdapter.notifyDataSetChanged();
mRefreshListView.hideHeaderView();
}
}.execute(new Void[]{});
}
@Override
public void onLoadingMore() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(3000);
mData.add("這是載入的資料1");
mData.add("這是載入的資料2");
mData.add("這是載入的資料3");
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
mAdapter.notifyDataSetChanged();
//隱藏腳佈局
mRefreshListView.hideFooterView();
}
}.execute(new Void[]{});
}
}
MyAdapter:
public class MyAdapter extends BaseAdapter{
private List<String> mData;
private Context mContext;
private LayoutInflater mInflater;
public MyAdapter(List<String> mData, Context mContext) {
if(mData == null){
this.mData = mData;
}
this.mData = mData;
this.mContext = mContext;
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int i) {
return mData.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
ViewHolder holder;
if(convertView == null){
convertView = mInflater.inflate(R.layout.listview_item,viewGroup,false);
holder = new ViewHolder();
convertView.setTag(holder);
holder.tvItem = (TextView) convertView.findViewById(R.id.tv_item);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.tvItem.setText(mData.get(i));
return convertView;
}
private class ViewHolder {
private TextView tvItem;
}
}
activity_main的佈局:
<?xml version="1.0" encoding="utf-8"?>
<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.ihavau.www.pulltorefreshlistviewdemo.MainActivity">
<com.ihavau.www.pulltorefreshlistviewdemo.widget.RefreshListView
android:id="@+id/refresh_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
header.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="wrap_content"
android:orientation="horizontal">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<ImageView
android:id="@+id/iv_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="30dp"
android:src="@drawable/ptr_pulltorefresh_arrow"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateDrawable="@anim/common_progressbar"
android:visibility="gone"/>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tv_header_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉重新整理"
android:textColor="#FF0000"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_header_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="最後重新整理時間:2016-6-7 17:47:30"/>
</LinearLayout>
</LinearLayout>
footer的佈局:
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateDrawable="@anim/common_progressbar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="載入更多..."
android:textColor="#FF0000"
android:textSize="18sp"/>
</LinearLayout>
</LinearLayout>
listview_item.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">
<TextView
android:id="@+id/tv_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:textSize="18sp"/>
</LinearLayout>
程式碼下載地址:http://download.csdn.net/detail/zuozuoshenghen/9593649