效果圖
思考
- Listview是viewGroup的子類,它本身提供了方法addHeaderView(View view),addFooterView(View view)去添加頭佈局和底佈局,所以我們只要監聽它的onTouchEvent方法,判斷頭佈局 底佈局的顯示狀態。
頭佈局(headerView)
- 默認的話,得隱藏。先獲取到頭佈局的高度
int headerViewHeight = headerView.getMeasuredHeight();
通過設置它的padding(0, -headerViewHeight, 0, 0); 這裏要注意:獲取頭佈局高度之前,需要等頭佈局完全加載上來後,才獲取得到,否則爲0。可以通過measure()方法主動通知系統去測量該view,也可以通過獲取視圖樹的觀察者去增加一個全局的佈局監聽器。 重寫onTouchEvent(MotionEvent event);
手指按下時 獲取該view的Y方向的座標downY。
手指移動時
獲取移動的Y方向的距離deltY = (int) (event.getY() - downY);
將移動的距離deltY+(-headerViewHeight)就是頭佈局的位置paddingTop。
當然這個paddingTop需要限制,比如你上滑時,如果已經到了-headerViewHeight,就不要繼續將頭佈局往上設置了;而且下拉時,如果不是第一個條目,就用listview本身的滑動事件,限制代碼:paddingTop > -headerViewHeight && getFirstVisiblePosition() == 0
在此條件下,繼續判斷:如果paddingTop>0 && currentState==下拉刷新狀態,就將currentState設置爲鬆開立即刷新狀態並更新頭佈局內容;如果paddingTop<0 && currentState==鬆開立即刷新狀態,就將currentState設置爲下拉刷新狀態並更新頭佈局內容。
手指擡起時
如果是下拉刷新狀態,則隱藏頭佈局
如果是鬆開立即刷新狀態,則顯示頭佈局,將當前狀態設置爲正在刷新狀態並更新頭佈局內容。同時在這裏設置暴露一個接口方法,讓用戶在該方法中去加載要刷新出來的數據。類中還應該提供刷新完成重置headerView的方法,由用戶在獲取完數據並更新完adapter之後,去在UI線程中調用該方法。
- 默認的話,得隱藏。先獲取到頭佈局的高度
- 底佈局(footerView)
- 默認隱藏。方法同頭佈局一樣
- 在初始化的時候,爲listview設置一個滑動監聽。當滑動狀態爲OnScrollListener.SCROLL_STATE_IDLE (即手指鬆開) && 是滑動listview最後一個條目 && 不是加載更多,這時,將footerView顯示出來,同時在這裏設置暴露一個接口方法,讓用戶在該方法中去加載更多要加載出來的數據。
- 提供加載完成重置footerView的方法
步驟
1.headerView佈局
<?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:gravity="center"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp">
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/indicator_arrow" />
<ProgressBar
android:id="@+id/pb_load"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:indeterminateDuration="1000"
android:indeterminateDrawable="@drawable/indeterminate_drawable"
android:visibility="invisible" />
</RelativeLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="下拉刷新"
android:textColor="#a000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="最後更新:2015-06-06"
android:textColor="@android:color/darker_gray"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
2.footerView佈局
<?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:gravity="center"
android:orientation="horizontal">
<ProgressBar
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:indeterminateDrawable="@drawable/indeterminate_drawable"
android:indeterminateDuration="1000" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:text="加載更多..."
android:textSize="16sp" />
</LinearLayout>
3.自定義listview的代碼
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
private static final String TAG = "RefreshListView1";
private View headerView;//頭佈局
private ImageView iv_arrow;
private ProgressBar pb_load;
private TextView tv_state;
private TextView tv_time;
private View footerView;//底佈局
private int headerViewHeight;//頭佈局的高度
private int downY;//手指按下時Y的座標
private static final int PULL_TO_REFRESH = 0; //下拉刷新
private static final int RELEASE_REFRESH = 1; //鬆開以刷新
private static final int REFRESHING = 2;//正在刷新
private int currentState = PULL_TO_REFRESH;
private RotateAnimation upAnimation;
private RotateAnimation downAnimation;
private int footerViewHeight;//底佈局的高度
private boolean isLoadingMore = 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();
}
private void init() {
setOnScrollListener(this);
initHeaderView();
initRotateAnimation();
initFooterView();
}
/**
* 初始化頭佈局
*/
private void initHeaderView() {
headerView = View.inflate(getContext(), R.layout.layout_header, null);
iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow);
pb_load = (ProgressBar) headerView.findViewById(R.id.pb_load);
tv_state = (TextView) headerView.findViewById(R.id.tv_state);
tv_time = (TextView) headerView.findViewById(R.id.tv_time);
// 第一種方法獲取頭佈局的高度
// final ViewTreeObserver viewTreeObserver = headerView.getViewTreeObserver();
// viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// @Override
// public void onGlobalLayout() {
// viewTreeObserver.removeOnGlobalLayoutListener(this);
// int headerViewHeight = headerView.getHeight();
// Log.i(TAG, "headerViewHeight:" + headerViewHeight);
// headerView.setPadding(0, -headerViewHeight, 0, 0);
// addHeaderView(headerView);
// }
// });
//第二種方法獲取頭佈局的高度
headerView.measure(0, 0);//主動通知系統去測量該view;
headerViewHeight = headerView.getMeasuredHeight();
Log.i(TAG, "headerViewHeight:" + headerViewHeight);
headerView.setPadding(0, -headerViewHeight, 0, 0);
addHeaderView(headerView);
}
/**
* 初始化旋轉動畫
*/
private void initRotateAnimation() {
upAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
upAnimation.setDuration(300);
upAnimation.setFillAfter(true);
downAnimation = new RotateAnimation(-180, -360,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
downAnimation.setDuration(300);
downAnimation.setFillAfter(true);
}
private void initFooterView() {
footerView = View.inflate(getContext(), R.layout.layout_footer, null);
footerView.measure(0, 0);
footerViewHeight = footerView.getMeasuredHeight();
footerView.setPadding(0, -footerViewHeight, 0, 0);
addFooterView(footerView);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (currentState == REFRESHING) {
break;
}
int deltY = (int) (event.getY() - downY);
int paddingTop = -headerViewHeight + deltY;
if (paddingTop > -headerViewHeight && getFirstVisiblePosition() == 0) {//下拉的狀態 並且 listview第一個條目可見
Log.i(TAG, "paddingTop:" + paddingTop);
headerView.setPadding(0, paddingTop, 0, 0);
if (paddingTop > 0 && currentState == PULL_TO_REFRESH) {//頭佈局可見 並且爲 下拉刷新狀態
//進入鬆開刷新狀態
currentState = RELEASE_REFRESH;
refreshHeaderView();
} else if (paddingTop < 0 && currentState == RELEASE_REFRESH) {
currentState = PULL_TO_REFRESH;
refreshHeaderView();
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (currentState == PULL_TO_REFRESH) {
//隱藏headerView
headerView.setPadding(0, -headerViewHeight, 0, 0);
} else if (currentState == RELEASE_REFRESH) {
headerView.setPadding(0, 0, 0, 0);
currentState = REFRESHING;
refreshHeaderView();
if (listener != null) {
listener.onPullRefresh();
}
}
break;
}
return super.onTouchEvent(event);
}
/**
* 刷新headerView
*/
private void refreshHeaderView() {
switch (currentState) {
case PULL_TO_REFRESH:
tv_state.setText("下拉刷新");
iv_arrow.startAnimation(downAnimation);
break;
case RELEASE_REFRESH:
tv_state.setText("鬆開立即刷新");
iv_arrow.startAnimation(upAnimation);
break;
case REFRESHING:
iv_arrow.clearAnimation();//因爲向上的旋轉動畫有可能沒有執行完
iv_arrow.setVisibility(View.INVISIBLE);
pb_load.setVisibility(View.VISIBLE);
tv_state.setText("正在刷新...");
break;
}
}
/**
* 完成刷新操作,重置狀態,在獲取完數據並更新完adapter之後,去在UI線程中調用該方法
*/
public void completeRefresh() {
if (isLoadingMore) {
//重置footerView狀態
footerView.setPadding(0, -footerViewHeight, 0, 0);
isLoadingMore = false;
} else {
//重置headerView狀態
headerView.setPadding(0, -headerViewHeight, 0, 0);
currentState = PULL_TO_REFRESH;
pb_load.setVisibility(View.INVISIBLE);
iv_arrow.setVisibility(View.VISIBLE);
tv_state.setText("下拉刷新");
tv_time.setText("最後刷新:" + getCurrentTime());
}
}
/**
* 獲取當前系統時間,並格式化
*/
private String getCurrentTime() {
SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
return format.format(new Date());
}
private OnRefreshListener listener;
public void setOnRefreshListener(OnRefreshListener listener) {
this.listener = listener;
}
public interface OnRefreshListener {
void onPullRefresh();
void onLoadingMore();
}
/**
* SCROLL_STATE_IDLE:閒置狀態,手指鬆開 值爲0
* SCROLL_STATE_TOUCH_SCROLL:觸摸滑動狀態 值爲1
* SCROLL_STATE_FLING:快速滑動狀態 值爲2
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.i(TAG, "scrollState = " + scrollState);
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
&& getLastVisiblePosition() == (getCount() - 1) && !isLoadingMore) {
isLoadingMore = true;
footerView.setPadding(0, 0, 0, 0);//顯示出footerView
setSelection(getCount());//讓listview最後一條顯示出來
if (listener != null) {
listener.onLoadingMore();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
}
4.主函數調用的佈局
<?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">
<com.example.refreshlistview.view.RefreshListView
android:id="@+id/my_refresh_listview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.refreshlistview.view.RefreshListView>
</LinearLayout>
5.主函數調用的代碼
public class MainActivity extends Activity {
private RefreshListView myRefreshListView;
private List<String> list = new ArrayList<String>();
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
adapter.notifyDataSetChanged();
myRefreshListView.completeRefresh();
}
};
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initViews();
initData();
}
private void initViews() {
setContentView(R.layout.activity_main);
myRefreshListView = (RefreshListView) this.findViewById(R.id.my_refresh_listview);
}
private void initData() {
for (int i = 0; i < 20; i++){
list.add("listview的原始數據:"+i);
}
initListView();
}
private void initListView() {
adapter = new MyAdapter();
myRefreshListView.setAdapter(adapter);
myRefreshListView.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onPullRefresh() {
getDataFromServer(false);
}
@Override
public void onLoadingMore() {
getDataFromServer(true);
}
});
}
/**
* 模擬向服務器請求數據
*/
private void getDataFromServer(final boolean isLoadingMore){
new Thread(){
@Override
public void run() {
SystemClock.sleep(3000);//模擬請求服務器的一個時間長度
if (!isLoadingMore) {
list.add(0, "下拉刷新的數據-1");
} else {
list.add("加載更多的數據-1");
list.add("加載更多的數據-2");
list.add("加載更多的數據-3");
}
//在UI線程更新UI
handler.sendEmptyMessage(0);
}
}.start();
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = new TextView(MainActivity.this);
textView.setText(list.get(position));
textView.setTextSize(18);
textView.setPadding(20, 20 ,20, 20);
return textView;
}
}