文章沒有仔細看:
http://www.cnblogs.com/wader2011/archive/2011/10/10/2205103.html
但是有一點是對的, listView 必須在綁定 Adapter 之前 addFooter addHeader ..
自定義組件—MyListView
注:該文章爲(男人應似海)原創,如需轉載請註明出處!
該組件的功能有三個:
(1) 任意加頭去頭,加腳去腳。
我們知道ListView在setAdapter之後再調用addHeader方法會拋出異常,而加腳有時管用,有時不管用。Android開發文檔中也明確指出ListView在setAdapter之後不應該再進行setHeader和setFooter方法。這明顯不能滿足我們的實際需求。
我的解決方案是:在setAdapter之前給ListView先加上一個空頭佈局和空腳佈局,佈局高度設爲wrap_content,這樣當頭或腳佈局中沒有任何組件時,頭和腳是看不到的。當需要顯示頭和腳時,直接向頭和腳佈局中添加要顯示的組件即可。
(2) 可任意添加或刪除ListView中的數據。
有過實際開發經驗的同學可能會有所體會,我們每次改變數據後都要調一次adapter.notifyDataSetChanged(),而且當ListView添加或刪除數據的操作未完成時,再來一次添加或刪除數據操作的話會報異常。比如用ListView顯示網絡獲取的數據時,不斷的刷新數據就會出現這種情況。這時候“同步“獲取數據的線程是無法避免這個問題的,因爲當線程獲取網絡數據特別快時還是會出現Listview添加數據操作的同時發生。
我的解決辦法是:在該組件中自己實現添加和刪除數據的方法並用一個變量來標記當前ListView是否在進行添加或刪除數據操作,是的話則不允許在進行數據操作。
(3) 實現了向下滑動出現一個顯示正在加載的頭佈局,手離開屏幕時頭佈局自動隱藏的功能。如YiBo微博首頁的下拉刷新效果。效果圖如下:
拉動前圖片
按下拉動後圖片
代碼:
package diy.ts.wader.widget;
import java.util.Collection;
import java.util.List;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
/******************************************************************
* 文件名稱 : MyListView.java
* 作者 : wader
* 創建時間 : 2011-7-20上午10:40:05
* 文件描述 :
* 實現了下拉顯示組件頭、任意添加頭腳、添加刪除數據功能的ListView組件
******************************************************************/
public class MyListView extends ListView {
private BaseAdapter adapter;
private List<Object> dataList;
private Context context;
private LinearLayout header;
private LinearLayout footer;
/*
* 在touch時間後的move動作時顯示的頭部動畫
*/
private LinearLayout headerContent;
ProgressBar headerProgressBar;
TextView headertextView;
private OnHeadShowOrHideAcion onHeadShowOrHideAcion = null;
/*
* 在init時是否加頭佈局,在init方法前設置
*/
private boolean addHeader = false;
/*
* 在init時是否加腳佈局,在init方法前設置
*/
private boolean addFooter = false;
/*
* 在touch時間後的move動作時是否顯示頭部動畫
*/
private boolean showAnimationHead = false;
/*
* 在顯示頭部動畫時是否執行HeadShowOrHideAcion的doActionOnShowHead方法
*/
private boolean doActionOnShowHead = true;
/*
* 在顯示頭部動畫時是否執行HeadShowOrHideAcion的doActionOnHideHead方法
*/
private boolean doActionOnHideHead = true;
/*
* 頭佈局中是否已有內容(headerContent!=null)並已顯示出來
*/
private boolean haveHead = false;
/*
* 是否可以對list進行添加或刪除數據操作,保證list數據同步
*/
private boolean ableOperateData = true;
private float initY = 0;
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/*
* 設置在init時是否加頭佈局,在init方法前設置
*/
public void setAddHeader(boolean addHeader) {
this.addHeader = addHeader;
}
/*
* 設置在init時是否加腳佈局,在init方法前設置
*/
public void setAddFooter(boolean addFooter) {
this.addFooter = addFooter;
}
/*
* 設置在touch時間後的move動作時是否顯示頭部動畫
*/
public void setShowAnimationHead(boolean param) {
this.showAnimationHead = param;
}
/*
* 設置在顯示頭部動畫時是否執行HeadShowOrHideAcion的doActionOnShowHead方法
*/
public void setDoActionOnShowHead(boolean param) {
this.doActionOnShowHead = param;
}
/*
* 設置在隱藏頭部動畫時是否執行HeadShowOrHideAcion的doActionOnHideHead方法
*/
public void setDoActionOnHideHead(boolean param) {
this.doActionOnHideHead = param;
}
/*
* 對listview進行初始化,在使用該組件前必須先執行這個方法
*/
public void init(Context context) {
this.context = context;
this.addHeaderAndFooterLayout();
}
/*
* 加頭佈局和腳佈局
*/
private void addHeaderAndFooterLayout() {
if (addHeader) {
header = new LinearLayout(context);
addHeaderView(header);
header.setVisibility(View.GONE);
}
if (addFooter) {
footer = new LinearLayout(context);
addFooterView(footer);
footer.setVisibility(View.GONE);
}
}
/*
* 顯示指定的頭內容
*/
public void showHeader(View view) {
if (view.getLayoutParams() == null) {
view.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
}
header.setVisibility(View.VISIBLE);
if (header.getChildCount() <= 0)
header.addView(view);
this.postInvalidate();
}
/*
* 顯示指定的頭動畫
*/
public void showAnimationHeader() {
if (!haveHead && addHeader) {
if (headerContent == null) {
headerContent = new LinearLayout(context);
headerContent.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT));
headerContent.setBackgroundColor(Color.LTGRAY);
headerContent.setGravity(Gravity.CENTER);
headerContent.setOrientation(LinearLayout.HORIZONTAL);
} else
headerContent.removeAllViews();
if (headertextView == null) {
headertextView = new TextView(context);
headertextView.setTextColor(Color.BLACK);
headertextView.setTextSize(20);
headertextView.setText("鬆開即可更新");
}
headerProgressBar = new ProgressBar(context);
headerProgressBar.setPadding(0, 3, 20, 3);
headerContent.addView(headerProgressBar);
headerContent.addView(headertextView);
this.showHeader(headerContent);
haveHead = true;
if (onHeadShowOrHideAcion != null && doActionOnShowHead) {
doActionOnShowHead = false;
onHeadShowOrHideAcion.doActionOnShowHead(this);
}
}
}
/*
* 刪除頭內容
*/
public void hideHeader() {
if (addHeader) {
header.removeAllViews();
haveHead = false;
header.setVisibility(View.GONE);
this.postInvalidate();
if (onHeadShowOrHideAcion != null && doActionOnHideHead)
onHeadShowOrHideAcion.doActionOnHideHead(this);
}
}
/*
* 顯示指定的腳內容
*/
public void showFooter(View view) {
if (addFooter) {
if (view.getLayoutParams() == null) {
view.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
}
footer.setVisibility(View.VISIBLE);
if (footer.getChildCount() <= 0)
footer.addView(view);
this.postInvalidate();
this.postInvalidate();
}
}
/*
* 刪除腳內容
*/
public void hideFooter(View view) {
if (addFooter) {
footer.removeAllViews();
footer.setVisibility(View.GONE);
this.postInvalidate();
}
}
/*
* 爲listview設置adapter和與該adapter相關的list,應該在init方法之後執行
*/
@SuppressWarnings("unchecked")
public void setData(BaseAdapter adapter, List<? extends Object> dataList) {
this.adapter = adapter;
this.dataList = (List<Object>) dataList;
setAdapter(adapter);
}
/*
* 在list最後添加新數據
*/
public void addData(Collection<? extends Object> c) {
if (ableOperateData) {
ableOperateData = false;
if (dataList == null || adapter == null)
throw new NullPointerException(
"You do not call the setData() method");
dataList.addAll(c);
adapter.notifyDataSetChanged();
}
ableOperateData = true;
}
/*
* 在list的指定位置添加新數據
*/
public void addData(int index, Collection<? extends Object> c) {
if (ableOperateData) {
ableOperateData = false;
if (dataList == null || adapter == null)
throw new NullPointerException(
"You don not call the setData() method or the Adapter or List param in setData() is null");
dataList.addAll(index, c);
adapter.notifyDataSetChanged();
}
ableOperateData = true;
}
/*
* 刪除list中指定位置的數據
*/
public void deleteData(int index) {
if (ableOperateData) {
ableOperateData = false;
dataList.remove(index);
adapter.notifyDataSetChanged();
}
ableOperateData = true;
}
/*
* 刪除list中的全部數據
*/
public void deleteAll() {
if (ableOperateData) {
ableOperateData = false;
dataList.clear();
adapter.notifyDataSetChanged();
}
ableOperateData = true;
}
/*
* 觸屏事件,主要是爲了顯示頭動畫
*/
public void touchEventAction(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
if (this.getFirstVisiblePosition() == 0 && (ev.getY() - initY > 30)
&& !haveHead) {// 當向下滑動30個單位時顯示頭部動畫
showAnimationHeader();
}
break;
case MotionEvent.ACTION_UP:
hideHeader();
break;
case MotionEvent.ACTION_DOWN:
initY = ev.getY();
break;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (showAnimationHead)
this.touchEventAction(ev);
super.dispatchTouchEvent(ev);
return true;
}
/*
* 設置在顯示頭部動畫時要執行HeadShowOrHideAcion
*/
public void setHeadShowOrHideAcion(
OnHeadShowOrHideAcion paramOnHeadShowOrHideAcion) {
this.onHeadShowOrHideAcion = paramOnHeadShowOrHideAcion;
}
/*
* 自定義內部接口
*/
public abstract interface OnHeadShowOrHideAcion {
public void doActionOnShowHead(ListView paramListView);
public void doActionOnHideHead(ListView paramListView);
}
}
注:
(1)OnHeadShowOrHideAcion接口是爲了實現當下拉刷新時要進行的操作,如啓動線程獲取網絡數據。
(2)ListView加頭佈局後item下標是從1開始而不是從0開始。
(3)該組件用起來可能比較麻煩,大家可以根據自身需要對它進行瘦身和改造。