從0系統學Android-- 3.5 最常用和最難用的控制元件---ListView
本系列文章目錄:更多精品文章分類本系列持續更新中....
3.5 最常用和最難用的控制元件---ListView
ListView 是我們在開發中最常使用的控制元件之一。由於手機螢幕空間比較有限,能夠一次性在螢幕上顯示的內容不多,ListView 允許使用者可以通過手指上下滑動,可以呈現更多的資料。
3.5.1 ListView 的簡單使用
首先還是在 Layout 中新增 ListView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
複製程式碼
private String[] data = {
"Apple","fdsfs","fdsfssfs","wwww","wwwqqq","qqqqq","eeeee","rrrrrr","tttttt","yyyyyy","uuuuuu","aaaaaa","sssss"
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listviwe);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,data);
ListView listView = findViewById(R.id.lv);
listView.setAdapter(adapter);
}
複製程式碼
ListView 是用於展示大量資料的,因此資料需要提供好,這些往往都是從網路上或者資料庫讀取的,這裡就用假資料簡單測試一下。
資料不能直接傳遞給 ListView 需要藉助介面卡來完成。Android 中提供了許多介面卡的實現類。這裡使用最簡單的 ArrayAdapter
,android.R.layout.simple_list_itme_1
是 Android 內建的佈局檔案,裡面只有一個簡單的 TextView。然後使用 setAdapter
就將構建好的介面卡物件傳遞進去,這樣 Listview 和 資料之間的關聯就建立了。
3.5.2 定製 LIstView 的介面
只顯示一行文字太過單調了,下面對 ListView 的介面進行定製,顯示更加複雜的內容。
定義一個實體類,作為 ListView 介面卡的適配型別。新建 Fruit
類
public class Fruit {
private String name;
private int imgId;
public Fruit(String name,int imgId){
this.name = name;
this.imgId = imgId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImgId() {
return imgId;
}
public void setImgId(int imgId) {
this.imgId = imgId;
}
}
複製程式碼
其中 name
表示名字 imgId
對應圖片的資源id。
下面為 ListView 的子項建立一個自定義的佈局fruit_item.xml
<?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:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/iv"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv_name"
android:layout_marginLeft="10dp"/>
</LinearLayout>
複製程式碼
下面自己建立介面卡,這個介面卡繼承自 ArrayAdapter,並且制定泛型的型別是 Fruit
型別的。
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
super(context, resource, objects);
resourceId = resource;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
// 獲取當前項的 Fruit 例項
Fruit fruit = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
TextView textView = view.findViewById(R.id.tv_name);
ImageView imageView = view.findViewById(R.id.iv);
// 這裡沒有圖片,就偷懶使用了系統的
imageView.setImageResource(R.mipmap.ic_launcher);
textView.setText(fruit.getName());
return view;
}
}
複製程式碼
FruitAdapter 繼承 ArrayAdapter 比較簡單,只需要重寫一組構造方法和一個 getView()
方法就可以了,其他的方法 ArrayAdapter
都實現了。
getItem()
方法在每個子項被滾動到螢幕內的時候都會被呼叫。
這裡使用了 LayoutInflater
來為這個子項載入我們傳入的佈局,LayoutInflater 的 inflate
方法需要傳入三個引數,第一個引數就是要載入的佈局,第二個引數就是這個佈局要加入到這父佈局中,第三個引數指定成 false 表示只讓我們在父佈局中宣告的 layout 屬性生效,其實就是為了測量一下這個生成的佈局,但是不會將這個 View 新增到父佈局中,因為一旦 View 有了父佈局後,就不能再新增到 ListView 中了。現在不理解也沒關係,記住這是標準寫法就行了。
最後我們在 Activity
中將 ListView 與我們自己建立的介面卡繫結就可以了。
// 進行資料加工
initData();
FruitAdapter fruitAdapter = new FruitAdapter(this,R.layout.fuit_item,list);
ListView listView = findViewById(R.id.lv);
listView.setAdapter(fruitAdapter);
複製程式碼
3.5.3 提高 ListView 的執行效率
ListView 還有很多的細節點需要優化,不然執行效率很低。因為在 getView()
方法中每次都將佈局重新載入了一遍,當 ListView 快速滾動的時候,效能就會出現問題。
其實在 getView()
方法中還有一個 converView
引數,這個引數就是用於將之前載入好的佈局進行快取的,以便之後可以進行重複使用。修改 FruitDapter
的程式碼
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Fruit fruit = getItem(position);
View view ;
if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
}else {
view = convertView;
}
TextView textView = view.findViewById(R.id.tv_name);
ImageView imageView = view.findViewById(R.id.iv);
imageView.setImageResource(R.mipmap.ic_launcher);
textView.setText(fruit.getName());
return view;
}
複製程式碼
這樣我們就充分把快取的 conventView 利用起來了。
不過目前還需要優化,雖然不會再去重複的載入佈局了,但是每次 getView()
方法中還是會呼叫 View
的 findViewById()
方法來獲取一次控制元件的例項。我們可以藉助 ViewHolder
來對這部分進行效能的優化。修改程式碼
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
// 獲取當前項的 Fruit 例項
Fruit fruit = getItem(position);
View view ;
ViewHolder viewHolder;
if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.iv = view.findViewById(R.id.iv);
viewHolder.tv = view.findViewById(R.id.tv_name);
view.setTag(viewHolder); // 將 ViewHolder 儲存在 View 中
}else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.iv.setImageResource(R.mipmap.ic_launcher);
viewHolder.tv.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView iv;
TextView tv;
}
複製程式碼
新增加一個 ViewHolder
內部類,用於對控制元件的例項進行快取,當 convertView 為 null 的時候就建立一個 ViewHolder
並將它存放在 View 中。當convertView
不為 null 的時候將 ViewHolder
例項取出來。這樣就沒有必要每次都要通過 findViewById()
來獲取控制元件例項了。
通過以上兩步,ListView 就優化的非常不錯了。
3.5.4 ListView 點選事件
給 ListView 設定點選事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 通過 positon 我們就可以確定是點選的哪一個子項
}
});
複製程式碼