編輯:關於Android編程
安卓系統為ListView
設計了多種Adapter
作為它的搭檔。每種Adapter
不僅為ListView
提供數據內容,也會告訴ListView
如何展示這些數據-規定好列表項的長相。
這些Adapter
都是從Adapter類
繼承而來的,它們的關系如下:
這裡我們選擇性的介紹常見的2種Adapter
-ArrayAdapter
和SimpleAdapter
。
ArrayAdapter
是最簡單的Adapter
,我們在前面已經使用過它,
ListView lv = (ListView) findViewById(R.id.list_view);
String data[] = {"a", "b", "c"};
ArrayAdapter adapter = new ArrayAdapter(context,
android.R.layout.simple_list_item_1 , data);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
//添加需要響應的操作
}
});
在創建ArrayAdapter
的時候,
要聲明這個Adapter
要接受什麼類型的數據,
new ArrayAdapter(......)
指定布局文件的layout id
,
android.R.layout.simple_list_item_1
這個布局就是一個TextView
,只不過被指定了android:id
為@android:id/text1
,
注意這裡在給控件指定ID的時候用的是`@id/text1
,而沒有用+
。這表明,這個ID值是已經存在的,而不需要編譯器再單獨分配。
這樣ArrayAdapter
會自動尋找這個布局文件中ID名字為text1
的控件,並認為它就是可以用來顯示數據的TextView
;
如果不想用系統給TextView
指定的ID,也可以給自己定義的布局的TextView
取個名字,例如show_content_textview
。
然後向ArrayAdapter
再傳入一個ID,告訴它將要顯示的文字,放到這個ID所指示的控件上,
ArrayAdapter adapter = new ArrayAdapter(context,
R.layout.custom_item_layout,
R.id.show_content_textview,
data);
傳入要顯示的數據內容,這個數據內容可以以數組的方式傳入,也可以以List
的方式放入,
//數組方式傳入
String [] data = {"a","b","c"};
ArrayAdapter adapter = new ArrayAdapter(context,
android.R.layout.simple_list_item_1 , data);
//列表方式傳入
List data =new ArrayList();
data.add("a");
data.add("b");
data.add("c");
ArrayAdapter adapter = new ArrayAdapter(context,
android.R.layout.simple_list_item_1 , data);
看得出ArrayAdapter
是相當的簡單,只能在單一的TextView
上顯示一條數據。如果有好幾條不同類型的數據要顯示到同一列表項的不同部位,那就不好辦了。SimpleAdapter
正好能幫我們解決這樣的問題。雖然它叫Simple,不過不是說功能simple,而是使用很simple。
它的設計思路是,
每一條列表項上哪些控件希望顯示數據都要告訴SimpleAdaper
,SimpleAdaper
只要知道這些控件的ID就可以了。所以在創建的時候,需要傳入這些控件的ID值(以數組的形式);
每一項待顯示的數據,要和列表上的控件一一對應。這就需要一個翻譯系統,給每個位置的數據取個名字,將待顯示數據挨個放入鍵值對的列表中(map);並將每個位置的數據名字和控件ID值對應起來;
SimpleAdapter
在綁定數據到界面上的時候,就根據對應關系,一個一個把它們放上去。
例如,
准備要顯示的數據,讓每個數據項,和一個特定的名字(title pic content)對應上,
Map family = new HashMap();
family.put("title", "家");
family.put("pic", "http://xxx.xxx.com/family.jpg");
family.put("content", "I love my family");
Map dog = new HashMap();
dog.put("title", "狗");
dog.put("pic", "http://xxx.xxx.com/dog.jpg");
dog.put("content", "I love my dog");
List
將特定的名稱做成數組,和顯示區域的ID一一對應上,例如,
title <-> R.id.title_id
pic <-> R.id.pic_id
content <-> R.id.content_id
String [] from = {"title", "pic", "content"};
int [] to = {R.id.title_id, R.id.pic_id, R.id.content_id};
創建SimpleAdapter
,
adapter = new SimpleAdapter(this,
dataList,
android.R.layout.simple_list_item_1 ,
from,
to);
雖然Android SDK為我們提供了好幾種現成的Adapter使用,但有時它們也並不能完全符合我們的要求,要麼用起來還是麻煩,要麼大材小用。另外,為了把ListView
介紹的全面一些,我們准備自定義一個Adapter。
為了讓列表的數據項按照我們設計的模樣顯示,我們需要為它設計一個布局,用展示的視頻列表為例,加以說明。
數據項的布局定義在res\layout\video_item.xml
文件中,
LinearLayout
中; 視頻縮略圖用ImageView
控件顯示,給它的android:scaleType
屬性設置center
,讓縮略圖居中放置,背景設置成應用主題的色調colorPrimary
; 其他視頻信息包含標題和創建時間,將它們豎直排列放在一個LinearLayout
中,占用高度按照2:1分配,前者使用主題中較大的字體?android:attr/textAppearanceMedium
,後者使用主題中較小的字體?android:attr/textAppearanceSmall
; 至於各個組件之間的間隔,根據自己的視覺偏好調整就好了,用android:padding
和android:layout_margin
設置;
為每個視頻信息,定義一個數據結構VideoItem
,
public class VideoItem {
String name;
String path;
Bitmap thumb;
String createdTime;
VideoItem(String strPath, String strName, String createdTime) {
this.path = strPath;
this.name = strName;
......
}
}
所有Adapter
都是繼承自BaseAdapter
的,我們自定義的Adapter
也繼承自它。
繼承BaseAdapter
,准備實現必須實現的基類函數;
public class VideoItemAdapter extends BaseAdapter {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null ;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
創建構造函數,在構造函數中,保存好布局ID,以便以後使用,通過Context
獲取Inflater
,為以後數據項布局的創建做准備;保存要展示的數據項們;
private List mData;
private final LayoutInflater mInflater;
private final int mResource;
public VideoItemAdapter(Context context, int resId, List data)
{
mData = data;
mInflater = LayoutInflater.from(context);
mResource = resId;
}
實現getCount()
函數,返回當前數據項的個數,
@Override
public int getCount() {
return mData != null ? mData.size() : 0;
}
實現getItem()
函數,根據傳入的索引號,返回對應的數據項,
@Override
public Object getItem(int position) {
return mData != null ? mData.get(position): null ;
}
實現getItemId()
函數,根據傳入的索引號,返回對應項的id值,
@Override
public long getItemId(int position) {
return position;
}
在getView()
函數中,創建數據項的布局,並為他們賦值,最後將這個布局返回給ListView
,讓它顯示,
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(mResource, parent, false);
}
VideoItem item = mData.get(position);
TextView title = (TextView) convertView.findViewById(R.id.video_title);
title.setText(item.name);
TextView createTime = (TextView) convertView.findViewById(R.id.video_date);
createTime.setText(item.createdTime);
ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
thumb.setImageBitmap(item.thumb);
return convertView;
}
這裡的convertView
就是數據項所代表的那個布局,當ListView
剛創建,還沒有產生任何數據項的時候,它就是為null
的,此時我們就需要創建一個布局,並通過getView()
將這個布局返回給ListView
。
假如ListView
上的數據項布局已經足夠了,那麼這裡傳入的convertView
就不會再是“null”,而是之前的某個數據項布局,我們就不必為此重新創建了,只需要更新上面的內容就好。這樣提高了界面刷新的效率。
當然,這裡還能通過其他方法減少使用findViewById()
,進一步提高效率,不過目前就不改進了,先把功能實現完成。
綜合以上內容,最後的代碼就是,
public class VideoItemAdapter extends BaseAdapter {
private List mData;
private final LayoutInflater mInflater;
private final int mResource;
public VideoItemAdapter(Context context, int resId, List data)
{
mData = data;
mInflater = LayoutInflater.from(context);
mResource = resId;
}
@Override
public int getCount() {
return mData != null ? mData.size() : 0;
}
@Override
public Object getItem(int position) {
return mData != null ? mData.get(position): null ;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(mResource, parent, false);
}
VideoItem item = mData.get(position);
TextView title = (TextView) convertView.findViewById(R.id.video_title);
title.setText(item.name);
TextView createTime = (TextView) convertView.findViewById(R.id.video_date);
createTime.setText(item.createdTime);
ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
thumb.setImageBitmap(item.thumb);
return convertView;
}
}
在Video List的Activity創建之時,我們在onCreate()
中創建並設置VideoAdapter
,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_list);
mVideoList = new ArrayList();
mVideoListView = (ListView) findViewById(R.id.video_list);
VideoItemAdapter adapter = new VideoItemAdapter(this, R.layout.video_item, mVideoList);
mVideoListView.setAdapter(adapter);
//設置數據項被點擊的監聽器
mVideoListView.setOnItemClickListener(this);
}
當列表中的數據有變化時,在主線程中更新數據列表,並使用notifyDataSetChanged()
刷新,
VideoItem data = xxx;
mVideoList.add(data);
VideoItemAdapter adapter = (VideoItemAdapter) mVideoListView.getAdapter();
adapter.notifyDataSetChanged();
在前面實現自定義Adapter的getView()
函數中,沒有每次都創建一個convertView
,而是復用已有的布局,這樣就節省重新創建的資源。不過每次都使用findViewById()
也會花掉不少的開銷。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//節省了資源
if (convertView == null) {
convertView = mInflater.inflate(mResource, parent, false);
}
VideoItem item = mData.get(position);
//消耗了資源
TextView title = (TextView) convertView.findViewById(R.id.video_title);
title.setText(item.name);
//消耗了資源
TextView createTime = (TextView) convertView.findViewById(R.id.video_date);
createTime.setText(item.createdTime);
//消耗了資源
ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
thumb.setImageBitmap(item.thumb);
return convertView;
}
為此,我們可以引入一個數據結構,將這些控件保存下來,在使用的時候直接獲取,不需要進行耗時的findViewById()
操作了。
創建一個數據類,准備存放控件,
private class Holder {
public TextView title;
public TextView createTime;
public ImageView thumb;
}
創建布局的時候,通過創建Holder
將控件保存起來,
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(mResource, parent, false);
Holder holder = new Holder();
holder.title = (TextView) convertView.findViewById(R.id.video_title);
holder.createTime = (TextView) convertView.findViewById(R.id.video_date);
holder.thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
//存放到View當中
convertView.setTag(holder);
}
......
}
獲取Holder
,直接使用,提高效率,
@Override
public View getView(int position, View convertView, ViewGroup parent) {
......
//從View當中取出Holder
Holder holder = (Holder) convertView.getTag()
VideoItem item = mData.get(position);
holder.title.setText(item.name);
holder.createTime.setText(item.createdTime);
holder.thumb.setImageBitmap(item.thumb);
return convertView;
}
需求NDK是由谷歌娘提供的,某種意義上就是可以讓android使用c開發的第“三”方sdk,所以,正常來說eclipse是沒有配置這個東西的,當然
前言最近做項目的時候遇到一個卡劵的效果,由於自己覺得用圖片來做的話可以會出現適配效果不好,再加上自己自定義view方面的知識比較薄弱,所以想試試用自定義View來實現。但
前一篇文章中學習了MVC框架模式在Android中的使用,不了解什麼是MVC框架模式的親戳這裡 框架模式 MVC 在Android中的使用。其實谷歌Android開發團隊
一、安裝及配置Genymotion(1)由於Eclipse中自帶的SDK模擬器,啟動之慢,不說了 現在給大家介紹一種比較快的模擬器Genymotion(2)首先去Geny