繼承Listview,增強Listview的功能
實現(xiàn)下拉刷新的功能
1捎琐、繼承Listview
public class RefreshListView extends ListView {
private int downY;
private View header;
private int headerHeight;
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
2蓄髓、添加頭布局妹懒,關鍵方法addHeaderView
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeader();
}
private void initHeader() {
header = View.inflate(getContext(), R.layout.refresh_header, null);
// 把布局添加到Listview的頭上
this.addHeaderView(header);
}
3、隱藏頭布局双吆,關鍵方法setPadding
private void initHeader() {
header = View.inflate(getContext(), R.layout.refresh_header, null);
// 隱藏頭布局
// 主動測量控件眨唬,獲取測量的寬高
header.measure(0, 0);// 把布局中的寬高給測量出來
// 獲取測量的寬高
headerHeight = header.getMeasuredHeight();
header.setPadding(0, -headerHeight, 0, 0);
// 把布局添加到Listview的頭上
this.addHeaderView(header);
}
4、處理事件好乐,讓頭布局隨手指移動匾竿,關鍵方法setPadding,計算手指移動的距離蔚万,再計算出頭布局要設置的頂部padding值岭妖,通過setPadding方法達到移動頭布局的效果
// 處理觸摸事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
// 計算手指移動的距離
int diffY = moveY - downY;
// 只處理從上往下的事件
if(diffY>0){
// 計算頭布局距離頂部的padding值
int topPadding = diffY - headerHeight;
header.setPadding(0, topPadding, 0, 0);
return true;// 自己處理的從上往下的觸摸事件,需要消費掉
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
5反璃、當Listview第一個條目沒有完全展示時昵慌,給頭布局設置padding沒有效果,需要判斷當?shù)谝粋€條目完全展示時淮蜈,才處理下拉刷新
// 處理觸摸事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
// 計算手指移動的距離
int diffY = moveY - downY;
// 只有當Listview中的第一個條目完全展示時斋攀,header.setPadding才有效果,才能自己處理事件
if (getFirstVisiblePosition() != 0) {
// 如果自己不處理事件梧田,每次移動需要給downY重新賦值
downY = (int) ev.getY();
break;
}
// 只處理從上往下的事件
if(diffY>0){
// 計算頭布局距離頂部的padding值
int topPadding = diffY - headerHeight;
header.setPadding(0, topPadding, 0, 0);
return true;// 自己處理的從上往下的觸摸事件淳蔼,需要消費掉
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
6、根據(jù)手指移動的距離裁眯,設置刷新狀態(tài)
private static final int PULLREFRESH_STATE = 1;// 下拉刷新狀態(tài)
private static final int RELEASE_STATE = 2;// 松開刷新狀態(tài)
private static final int REFRESHING_STATE = 3;// 正在刷新狀態(tài)
private int current_state = PULLREFRESH_STATE;// 當前刷新狀態(tài)
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
// 計算手指移動的距離
int diffY = moveY - downY;
// 只有當Listview中的第一個條目完全展示時鹉梨,header.setPadding才有效果,才能自己處理事件
if (getFirstVisiblePosition() != 0) {
// 如果自己不處理事件穿稳,每次移動需要給downY重新賦值
downY = (int) ev.getY();
break;
}
// 只處理從上往下的事件
if (diffY > 0) {
// 計算頭布局距離頂部的padding值
int topPadding = diffY - headerHeight;
// 根據(jù)toppadding值是否大于0 頭布局是否完全展示存皂,判斷狀態(tài)的切換
if (topPadding >= 0 && current_state != RELEASE_STATE) {// 頭布局完全展示,切換到松開刷新
// 逢艘,如果已經(jīng)是松開刷新狀態(tài)旦袋,就不用再切換
current_state = RELEASE_STATE;
System.out.println("切換到松開刷新");
switchState();
} else if (topPadding < 0 && current_state != PULLREFRESH_STATE) {// 頭布局沒有完全展示,切換到下拉刷新
current_state = PULLREFRESH_STATE;
System.out.println("切換到下拉刷新");
switchState();
}
header.setPadding(0, topPadding, 0, 0);
return true;// 自己處理的從上往下的觸摸事件埋虹,需要消費掉
}
break;
case MotionEvent.ACTION_UP:
// 手指抬起時猜憎,根據(jù)當前的狀態(tài)判斷是否切換到正在刷新
if (current_state == PULLREFRESH_STATE) {// 抬起時,是下拉刷新搔课,頭布局沒有完全展示胰柑,不切換到正在刷新
// 隱藏頭布局
header.setPadding(0, -headerHeight, 0, 0);
} else if (current_state == RELEASE_STATE) {// 抬起時,是松開刷新爬泥,切換到正在刷新
current_state = REFRESHING_STATE;
// 讓頭布局正好完全展示
header.setPadding(0, 0, 0, 0);
System.out.println("切換到正在刷新");
switchState();
}
break;
* 箭頭動畫
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeader();
initAnimation();
}
private void initAnimation() {
up = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
up.setDuration(200);
up.setFillAfter(true);
down = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
down.setDuration(200);
down.setFillAfter(true);
}
* 根據(jù)狀態(tài)更新控件
// 切換狀態(tài)時柬讨,更新界面
private void switchState() {
switch (current_state) {
case PULLREFRESH_STATE:
state.setText("下拉刷新");
progress.setVisibility(View.INVISIBLE);
arrow.setVisibility(View.VISIBLE);
arrow.startAnimation(down);
break;
case RELEASE_STATE:
state.setText("松開刷新");
arrow.startAnimation(up);
break;
case REFRESHING_STATE:
// 由于動畫設置了setFillAfter 控件就停留在結(jié)束時的效果
arrow.clearAnimation();
state.setText("正在刷新");
progress.setVisibility(View.VISIBLE);
arrow.setVisibility(View.INVISIBLE);
break;
default:
break;
}
}
7、暴露接口袍啡,讓外界實現(xiàn)業(yè)務
// 對外暴露接口
public interface OnRefreshListener {
// 正在刷新時踩官,回調(diào)
void onRefreshing();
}
// 提供傳遞監(jiān)聽器的方法
public void setOnRefreshListener(OnRefreshListener listener) {
this.mListener = listener;
}
case MotionEvent.ACTION_UP:
// 手指抬起時,根據(jù)當前的狀態(tài)判斷是否切換到正在刷新
if (current_state == PULLREFRESH_STATE) {// 抬起時境输,是下拉刷新蔗牡,頭布局沒有完全展示颖系,不切換到正在刷新
// 隱藏頭布局
header.setPadding(0, -headerHeight, 0, 0);
} else if (current_state == RELEASE_STATE) {// 抬起時,是松開刷新辩越,切換到正在刷新
current_state = REFRESHING_STATE;
// 讓頭布局正好完全展示
header.setPadding(0, 0, 0, 0);
System.out.println("切換到正在刷新");
switchState();
// 當處于正在刷新狀態(tài)時嘁扼,回調(diào)監(jiān)聽器的onRefreshing
if (mListener != null) {
mListener.onRefreshing();
}
}
break;
* 外界監(jiān)聽刷新狀態(tài),處理業(yè)務
listview.setOnRefreshListener(new MyListener());
class MyListener implements OnRefreshListener{
@Override
public void onRefreshing() {
// 處理業(yè)務
new Thread(){
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
arrayList.add(0, "我是拉出來的");
adapter.notifyDataSetChanged();
// 刷新完成后黔攒,調(diào)用恢復下拉刷新控件的方法
listview.refreshFinished();
}
});
};
}.start();
}
}
8趁啸、刷新業(yè)務完成后,恢復下拉刷新狀態(tài)
// 下拉刷新完成后督惰,恢復狀態(tài)不傅,隱藏頭布局
public void refreshFinished() {
header.setPadding(0, -headerHeight, 0, 0);
state.setText("下拉刷新");
progress.setVisibility(View.INVISIBLE);
arrow.setVisibility(View.VISIBLE);
current_state = PULLREFRESH_STATE;
}
實現(xiàn)上拉加載更多功能
流程:
1、添加腳布局赏胚,addFooterView
2访娶、隱藏腳布局,setPadding
3栅哀、監(jiān)聽Listview的滾動狀態(tài)震肮,當處于停止或慣性停止狀態(tài)時,而且Listview最后一個條目完全展示留拾,才加載更多
4戳晌、對外暴露接口,讓外界處理加載更多的業(yè)務
5痴柔、加載更多業(yè)務完成后沦偎,恢復加載更多狀態(tài)
2.1、添加腳布局
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeader();
initAnimation();
initFooter();
}
private void initFooter() {
footer = View.inflate(getContext(), R.layout.refresh_footer, null);
// 添加腳布局
addFooterView(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:gravity="center"
android:orientation="horizontal" >
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加載更多中咳蔚。豪嚎。。"
android:textColor="#f00"
android:textSize="25sp" />
</LinearLayout>
2.2谈火、隱藏腳布局
private void initFooter() {
footer = View.inflate(getContext(), R.layout.refresh_footer, null);
footer.measure(0, 0);
footerHeight = footer.getMeasuredHeight();
footer.setPadding(0, 0, 0, -footerHeight);
// 添加腳布局
addFooterView(footer);
}
2.3侈询、監(jiān)聽Listview滾動狀態(tài),根據(jù)狀態(tài)判斷是否顯示加載更多腳布局
private void initFooter() {
footer = View.inflate(getContext(), R.layout.refresh_footer, null);
footer.measure(0, 0);
footerHeight = footer.getMeasuredHeight();
footer.setPadding(0, 0, 0, -footerHeight);
// 添加腳布局
addFooterView(footer);
// 監(jiān)聽Listview的滾動狀態(tài)
this.setOnScrollListener(new MyOnScrollListener());
}
class MyOnScrollListener implements OnScrollListener {
// 狀態(tài)發(fā)生變化時調(diào)用
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 當處于停止或慣性停止狀態(tài)時
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
|| scrollState == OnScrollListener.SCROLL_STATE_FLING) {
// 而且Listview最后一個條目完全展示
if(getLastVisiblePosition()==getCount()-1&&!isLoadMore){
isLoadMore = true;
// 顯示加載更多布局
footer.setPadding(0, 0, 0, 0);
System.out.println("加載更多了");
// 自動顯示加載更多布局
setSelection(getCount());
}
}
}
}
2.4糯耍、暴露接口扔字,讓外界處理加載更多業(yè)務
// 對外暴露接口
public interface OnRefreshListener {
// 正在刷新時,回調(diào)
void onRefreshing();
// 加載更多時温技,回調(diào)
void onLoadingMore();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 當處于停止或慣性停止狀態(tài)時
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
|| scrollState == OnScrollListener.SCROLL_STATE_FLING) {
// 而且Listview最后一個條目完全展示
if(getLastVisiblePosition()==getCount()-1&&!isLoadMore){
isLoadMore = true;
// 顯示加載更多布局
footer.setPadding(0, 0, 0, 0);
System.out.println("加載更多了");
// 自動顯示加載更多布局
setSelection(getCount());
// 當處于加載更多時革为,調(diào)用監(jiān)聽器的onLoadingMore方法
if(mListener!=null){
mListener.onLoadingMore();
}
}
}
}
2.5外界處理加載更多業(yè)務
@Override
public void onLoadingMore() {
// 處理業(yè)務
new Thread(){
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
arrayList.add("我是加載出來的");
arrayList.add("我是加載出來的");
adapter.notifyDataSetChanged();
// 加載更多完成后,調(diào)用控件恢復狀態(tài)的方法
listview.loadMoreFinished();
}
});
};
}.start();
}
2. 6舵鳞、外界處理完業(yè)務震檩,恢復加載更多狀態(tài)
// 加載更多完成后,恢復狀態(tài)
public void loadMoreFinished(){
isLoadMore = false;
footer.setPadding(0, 0, 0, -footerHeight);
}