編輯:關於Android編程
關於ListView網上也有許多的介紹,我選了一些結合到了一起,僅供參考。
在android開發中ListView是比較常用的組件
官方API地址:http://www.android-doc.com/reference/android/widget/ListView.html
1.ListVeiw 用來展示列表的View。
2.適配器用來把數據映射到ListView上的中介。
3.數據具體的將被映射的字符串,圖片,或者基本組件。
根據列表的適配器類型,列表分為三種,ArrayAdapter,SimpleAdapter和SimpleCursorAdapter
其中以ArrayAdapter最為簡單,只能展示一行字。SimpleAdapter有最好的擴充性,可以自定義出各種效果。SimpleCursorAdapter可以認為是SimpleAdapter對數據庫的簡單結合,可以方面的把數據庫的內容以列表的形式展示出來。
我們從最簡單的ListView開始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30/**
* @author allin
*
*/
public
class
MyListView
extends
Activity {
private
ListView listView;
//private List data = new ArrayList();
@Override
public
void
onCreate(Bundle savedInstanceState){
super
.onCreate(savedInstanceState);
listView =
new
ListView(
this
);
listView.setAdapter(
new
ArrayAdapter(
this
, android.R.layout.simple_expandable_list_item_1,getData()));
setContentView(listView);
}
private
List getData(){
List data =
new
ArrayList();
data.add(
"測試數據1"
);
data.add(
"測試數據2"
);
data.add(
"測試數據3"
);
data.add(
"測試數據4"
);
return
data;
}
}
上面代碼使用了windows/android-sdk-windows/docs/reference/android/widget/ArrayAdapter.html>)" target="_blank">ArrayAdapter(Contextcontext, int textViewResourceId,List
SimpleCursorAdapter
sdk的解釋是這樣的:An easy adapter to map columns from a cursor to TextViews or ImageViews defined in an XML file. You can specify which columns you want, which views you want to display the columns, and the XML file that defines the appearance of these views。簡單的說就是方便把從游標得到的數據進行列表顯示,並可以把指定的列映射到對應的TextView中。
下面的程序是從電話簿中把聯系人顯示到類表中。先在通訊錄中添加一個聯系人作為數據庫的數據。然後獲得一個指向數據庫的Cursor並且定義一個布局文件(當然也可以使用系統自帶的)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28/**
* @author allin
*
*/
public
class
MyListView2
extends
Activity {
private
ListView listView;
//private List data = new ArrayList();
@Override
public
void
onCreate(Bundle savedInstanceState){
super
.onCreate(savedInstanceState);
listView =
new
ListView(
this
);
Cursor cursor = getContentResolver().query(People.CONTENT_URI,
null
,
null
,
null
,
null
);
startManagingCursor(cursor);
ListAdapter listAdapter =
new
SimpleCursorAdapter(
this
, android.R.layout.simple_expandable_list_item_1,
cursor,
new
String[]{People.NAME},
new
int
[]{android.R.id.text1});
listView.setAdapter(listAdapter);
setContentView(listView);
}
}
Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);先獲得一個指向系統通訊錄數據庫的Cursor對象獲得數據來源。
startManagingCursor(cursor);我們將獲得的Cursor對象交由Activity管理,這樣Cursor的生命周期和Activity便能夠自動同步,省去自己手動管理Cursor。
SimpleCursorAdapter 構造函數前面3個參數和ArrayAdapter是一樣的,最後兩個參數:一個包含數據庫的列的String型數組,一個包含布局文件中對應組件id的int型數組。其作用是自動的將String型數組所表示的每一列數據映射到布局文件對應id的組件上。上面的代碼,將NAME列的數據一次映射到布局文件的id為text1的組件上。
注意:需要在AndroidManifest.xml中如權限:
運行後效果如下圖:
SimpleAdapter
simpleAdapter的擴展性最好,可以定義各種各樣的布局出來,可以放上ImageView(圖片),還可以放上Button(按鈕),CheckBox(復選框)等等。下面的代碼都直接繼承了ListActivity,ListActivity和普通的Activity沒有太大的差別,不同就是對顯示ListView做了許多優化,方面顯示而已。
下面的程序是實現一個帶有圖片的類表。
首先需要定義好一個用來顯示每一個列內容的xml
vlist.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30xml
version="1.0" encoding="utf-8"?>
<
LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<
ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5px"/>
<
LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<
TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="22px" />
<
TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="13px" />
LinearLayout
>
LinearLayout
>
下面是實現代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42/**
* @author allin
*
*/
public
class
MyListView3
extends
ListActivity {
// private List data = new ArrayList();
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
SimpleAdapter adapter =
new
SimpleAdapter(
this
,getData(),R.layout.vlist,
new
String[]{
"title"
,
"info"
,
"img"
},
new
int
[]{R.id.title,R.id.info,R.id.img});
setListAdapter(adapter);
}
private
List
List
new
ArrayList
Map map =
new
HashMap();
map.put(
"title"
,
"G1"
);
map.put(
"info"
,
"google 1"
);
map.put(
"img"
, R.drawable.i1);
list.add(map);
map =
new
HashMap();
map.put(
"title"
,
"G2"
);
map.put(
"info"
,
"google 2"
);
map.put(
"img"
, R.drawable.i2);
list.add(map);
map =
new
HashMap();
map.put(
"title"
,
"G3"
);
map.put(
"info"
,
"google 3"
);
map.put(
"img"
, R.drawable.i3);
list.add(map);
return
list;
}
}
使用simpleAdapter的數據用一般都是HashMap構成的List,list的每一節對應ListView的每一行。HashMap的每個鍵值數據映射到布局文件中對應id的組件上。因為系統沒有對應的布局文件可用,我們可以自己定義一個布局vlist.xml。下面做適配,new一個SimpleAdapter參數一次是:this,布局文件(vlist.xml),HashMap的 title 和 info,img。布局文件的組件id,title,info,img。布局文件的各組件分別映射到HashMap的各元素上,完成適配。
運行效果如下圖:
有按鈕的ListView
但是有時候,列表不光會用來做顯示用,我們同樣可以在在上面添加按鈕。添加按鈕首先要寫一個有按鈕的xml文件,然後自然會想到用上面的方法定義一個適配器,然後將數據映射到布局文件上。但是事實並非這樣,因為按鈕是無法映射的,即使你成功的用布局文件顯示出了按鈕也無法添加按鈕的響應,這時就要研究一下ListView是如何現實的了,而且必須要重寫一個類繼承BaseAdapter。下面的示例將顯示一個按鈕和一個圖片,兩行字如果單擊按鈕將刪除此按鈕的所在行。並告訴你ListView究竟是如何工作的。效果如下:
vlist2.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36xml
version="1.0" encoding="utf-8"?>
<
LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<
ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5px"/>
<
LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<
TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="22px" />
<
TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="13px" />
LinearLayout
>
<
Button
android:id="@+id/view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/s_view_btn"
android:layout_gravity="bottom|right" />
LinearLayout
>
程序代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 12/**
* @author allin
*
*/
public
class
MyListView4
extends
ListActivity {
private
List
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
mData = getData();
MyAdapter adapter =
new
MyAdapter(
this
);
setListAdapter(adapter);
}
private
List
List
new
ArrayList
Map map =
new
HashMap();
map.put(
"title"
,
"G1"
);
map.put(
"info"
,
"google 1"
);
map.put(
"img"
, R.drawable.i1);
list.add(map);
map =
new
HashMap();
map.put(
"title"
,
"G2"
);
map.put(
"info"
,
"google 2"
);
map.put(
"img"
, R.drawable.i2);
list.add(map);
map =
new
HashMap();
map.put(
"title"
,
"G3"
);
map.put(
"info"
,
"google 3"
);
map.put(
"img"
, R.drawable.i3);
list.add(map);
return
list;
}
// ListView 中某項被選中後的邏輯
@Override
protected
void
onListItemClick(ListView l, View v,
int
position,
long
id) {
Log.v(
"MyListView4-click"
, (String)mData.get(position).get(
"title"
));
}
/**
* listview中點擊按鍵彈出對話框
*/
public
void
showInfo(){
new
AlertDialog.Builder(
this
)
.setTitle(
"我的listview"
)
.setMessage(
"介紹..."
)
.setPositiveButton(
"確定"
,
new
DialogInterface.OnClickListener() {
@Override
public
void
onClick(DialogInterface dialog,
int
which) {
}
})
.show();
}
public
final
class
ViewHolder{
public
ImageView img;
public
TextView title;
public
TextView info;
public
Button viewBtn;
}
public
class
MyAdapter
extends
BaseAdapter{
private
LayoutInflater mInflater;
public
MyAdapter(Context context){
this
.mInflater = LayoutInflater.from(context);
}
@Override
public
int
getCount() {
// TODO Auto-generated method stub
return
mData.size();
}
@Override
public
Object getItem(
int
arg0) {
// TODO Auto-generated method stub
return
null
;
}
@Override
public
long
getItemId(
int
arg0) {
// TODO Auto-generated method stub
return
0
;
}
@Override
public
View getView(
int
position, View convertView, ViewGroup parent) {
ViewHolder holder =
null
;
if
(convertView ==
null
) {
holder=
new
ViewHolder();
convertView = mInflater.inflate(R.layout.vlist2,
null
);
holder.img = (ImageView)convertView.findViewById(R.id.img);
holder.title = (TextView)convertView.findViewById(R.id.title);
holder.info = (TextView)convertView.findViewById(R.id.info);
holder.viewBtn = (Button)convertView.findViewById(R.id.view_btn);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder)convertView.getTag();
}
holder.img.setBackgroundResource((Integer)mData.get(position).get(
"img"
));
holder.title.setText((String)mData.get(position).get(
"title"
));
holder.info.setText((String)mData.get(position).get(
"info"
));
holder.viewBtn.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
showInfo();
}
});
return
convertView;
}
}
}
下面將對上述代碼,做詳細的解釋,listView在開始繪制的時候,系統首先調用getCount()函數,根據他的返回值得到listView的長度(這也是為什麼在開始的第一張圖特別的標出列表長度),然後根據這個長度,調用getView()逐一繪制每一行。如果你的getCount()返回值是0的話,列表將不顯示同樣return 1,就只顯示一行。
系統顯示列表時,首先實例化一個適配器(這裡將實例化自定義的適配器)。當手動完成適配時,必須手動映射數據,這需要重寫getView()方法。系統在繪制列表的每一行的時候將調用此方法。getView()有三個參數,position表示將顯示的是第幾行,covertView是從布局文件中inflate來的布局。我們用LayoutInflater的方法將定義好的vlist2.xml文件提取成View實例用來顯示。然後將xml文件中的各個組件實例化(簡單的findViewById()方法)。這樣便可以將數據對應到各個組件上了。但是按鈕為了響應點擊事件,需要為它添加點擊監聽器,這樣就能捕獲點擊事件。至此一個自定義的listView就完成了,現在讓我們回過頭從新審視這個過程。系統要繪制ListView了,他首先獲得要繪制的這個列表的長度,然後開始繪制第一行,怎麼繪制呢?調用getView()函數。在這個函數裡面首先獲得一個View(實際上是一個ViewGroup),然後再實例並設置各個組件,顯示之。好了,繪制完這一行了。那再繪制下一行,直到繪完為止。在實際的運行過程中會發現listView的每一行沒有焦點了,這是因為Button搶奪了listView的焦點,只要布局文件中將Button設置為沒有焦點就OK了。
運行效果如下圖:
源碼下載
ListView的屬性
[java]view plaincopy ListView去除滑動顏色。[html]view plaincopy android:cacheColorHint="@android:color/transparent"
傳值只能大於item內容大小,小於不起作用
去除滾動條
[html]view plaincopy android:scrollbars="none"
listItem 中包含ImageButton,Button,CheckBox等子控件會與 listView搶奪焦點
在根布局中添加
[html]view plaincopy android:descendantFocusability="blocksDescendants"
此時我在調用listView.removeFootView(view);的時候出現了一個錯無,查找原因後終於明白
listView在set Adapter 類之前,一定要先listView.addFooterView(footerView);下面來看下小馬從網上找來的示例代碼,網址搞丟了,只有一個word文檔,只能 copy過來,不然直接貼網址,結合上面的原理圖一起加深理解,如下:
C# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class MultipleItemsList extends ListActivity { private MyCustomAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdapter = new MyCustomAdapter(); for (int i = 0; i < 50; i++) { mAdapter.addItem("item " + i); } setListAdapter(mAdapter); } private class MyCustomAdapter extends BaseAdapter { private ArrayList mData = new ArrayList(); private LayoutInflater mInflater; public MyCustomAdapter() { mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void addItem(final String item) { mData.add(item); notifyDataSetChanged(); } @Override public int getCount() { return mData.size(); } @Override public String getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { System.out.println("getView " + position + " " + convertView); ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.item1, null); holder = new ViewHolder(); holder.textView = (TextView)convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.textView.setText(mData.get(position)); return convertView; } } public static class ViewHolder { public TextView textView; } }
執行程序,查看日志:
getView 被調用 9 次 ,convertView 對於所有的可見項目是空值(如下):
然後稍微向下滾動List,直到item10出現:
convertView仍然是空值,因為recycler中沒有視圖(item1的邊緣仍然可見,在頂端)再滾動列表,繼續滾動:
convertView不是空值了!item1離開屏幕到Recycler中去了,然後item11被創建,再滾動下:
此時的convertView非空了,在item11離開屏幕之後,它的視圖(…0f8)作為convertView容納item12了,好啦,結合以上原理,下面來看看今天最主要的話題,主角ListView的優化:
首先,這個地方先記兩個ListView優化的一個小點:
1. ExpandableListView 與 ListActivity 由官方提供的,裡面要使用到的ListView是已經經過優化的ListView,如果大家的需求可以用Google自帶的ListView滿足的的話盡量用官方的,絕對沒錯!
2.其次,像小馬前面講的,說ListView優化,其實並不是指其它的優化,就是內存是的優化,提到內存…(想到OOM,折騰了我不少時間),很多很多,先來寫下,如果我們的ListView中的選項僅僅是一些簡單的TextView的話,就好辦啦,消耗不了多少的,但如果你的Item是自定義的Item的話,例如你的自定義Item布局ViewGroup中包含:按鈕、圖片、flash、CheckBox、RadioButton等一系列你能想到的控件的話, 你要在getView中單單使用文章開頭提到的ViewHolder是遠遠不夠的,如果數據過多,加載的圖片過多過大,你BitmapFactory.decode的猛多的話,OOM搞死你,這個地方再警告下大家,是警告……….也提醒下自己:
小馬碰到的問題大家應該也都碰到過的,自定義的ListView項亂序問題,我很天真的在getView()中強制清除了下ListView的緩存數據convertView,也就是convertView = null了,雖然當時是解決了這個問題讓其它每次重繪,但是犯了大錯了,如果數據太多的話,出現最最惡心的錯,手機卡死或強制關機,關機啊哥哥們……O_O,客戶殺了我都有可能,但大家以後別犯這樣的錯了,單單使用清除緩存convertView是解決不了實際問題的,繼續……
下面是小記:圖片用完了正確的釋放…
下面來列舉下真正意義上的優化吧:
C# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public abstract class WeakAsyncTask
其實在ListView適配器的getView()方法中可以做很多的優化,我記得還有可以優化findViewById()這個方法來尋址資源信息效率的方法。
分析 : 根據敵機類型區分 敵機 運動邏輯 以及繪制public class Enemy { // 敵機的種類標識 public int type;
第三課(第三步):支持以手指觸控的任意點為中心開始縮放關鍵部分是在縮放的時候不斷進行邊界檢測,防止放大後縮小後出現白邊: /** * 在縮放的時候進行邊界控制
本文實例講述了Android編程中的消息機制。分享給大家供大家參考,具體如下:在分析Android消息機制之前,我們先來看一段代碼:public class MainAc
本文較為詳細的總結分析了Android編程下拉菜單spinner用法。分享給大家供大家參考,具體如下:Spinner控件也是一種列表類型的控件,它的繼承關系如下:java