#最常用和最難用的控件----ListView
ListView絕對是稱得上Android中最常用的控件之一。幾乎所有應用程序都會用到它。
ListView允許用戶通過手指上下滑動的方式將屏幕外的數據滾動到屏幕內,同時屏幕上原有的數據則會滾動出屏幕。
不過比起先前學的幾種控件,ListView的用法也相對複雜了很多。
新建項目ListViewTest
一個ListView通常有兩個職責。
(1)將數據填充到佈局。
(2)處理用戶的選擇點擊等操作。
一個ListView的創建需要三個元素
(1)ListView中的每一列的View
(2)填入View的數據或圖片等。
(3)連接數據與ListView的適配器。
也就是說,要使用ListView,首先要了解什麼是適配器。
適配器是一個連接數據和AdapterView(ListView就是一個典型的AdapterView)
的橋樑,通過它能有效地實現數據與AdaterView的分離設置,使AndroidView與數據的綁定更加簡便,修改更加方便。
Android中提供了很多的Adapter
Adapter 含義
ArrayAdapter 用來綁定一個數組,支持泛型操作
SimpleAdapter 用來綁定在xml中定義的控件對應的數據
SimpleCursorAdapter 用來綁定遊標得到的數據
BaseAdapter 通用的基礎適配器
其實適配器還有很多,要注意的是,各種Adapter只不過是轉換的方式和能力不一樣而已。
package net.nyist.lenovo.listviewtest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends AppCompatActivity {
private String[] data = {"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango",
"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView= (ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
}
既然ListView是用於展示大量數據的,那我們就應該先將數據提供好。這些數據可以是從網上下載的,也可以是從數據庫中讀取的,應該視具體的應用程序場景而定。這裏我就使用了一個簡單的data數組來測試,裏面包含了許多水果的名稱。
不過,數組中的數據時無法直接傳遞給ListView的,我們還需要藉助適配器來完成。
ArrayAdpter有多個構造函數的重載,應該根據實際情況選擇合適的一種。
由於我們提供的數據都是字符串,因此將ArrayAdapter的泛型指定爲String,然後在ArrayAdapter的構造函數中一次傳入當前上下文、LitView子項佈局的id,以及要適配的數據。注意,我們使用了android.R.layout.simple_list_item_1作爲ListView子項佈局的id,這是一個Android內置的佈局文件,裏面只有一個TextView,可用於簡單地顯示一段文本。這樣適配器對象就構建好了。
最後,還需要調用ListView的setAdapter()方法,將構建好的適配器對象傳遞進去,這樣ListView和數據之間的關聯就建立完成了。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0I9U22qy-1587024879884)(http://i.imgur.com/xBKRLfV.png)]
#定製ListView的界面
只能顯示一段文本的ListView實在是太單調了,我們現在就來對ListView的界面進行定製,讓它可以顯示更加豐富的內容。
首先需要準備好一組圖片。
接着定義一個實體類,作爲ListView適配器的適配類型。新建Fruit,代碼如下:
package net.nyist.lenovo.listviewtest;
public class Fruit {
private String name;
private int imageId;
public Fruit(String name ,int imageId){
this.name = name;
this.imageId = imageId;
}
public int getImageId() {
return imageId;
}
public String getName() {
return name;
}
}
Fruit類中只有兩個字段,name表示水果的名字,imageId表示水果對應圖片的資源id。
然後需要爲ListView的子項指定一個我們自定義的佈局,在layout目錄下新建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="match_parent">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
/>
</LinearLayout>
在這個佈局中,我們定義了一個ImageView用於顯示水果的圖片,有定義了一個TextView用於顯示水果的名稱,並讓TextView在垂直方向上居中顯示。
接下來需要創建一個自定義的適配器,這個適配器繼承自ArrayAdapter,並將泛型指定爲Fruit類。新建類FruitAdapter,代碼如下所示:
package net.nyist.lenovo.listviewtest;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class FruitAdapter extends ArrayAdapter<Fruit>{
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit>objects){
super(context,textViewResourceId,objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);//獲取當前項的Fruit實例
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
ImageView fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView)view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
}
//加載佈局管理器,將xml佈局轉換爲view對象
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
//利用view對象,找到佈局中的組件
ImageView fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView)view.findViewById(R.id.fruit_name);
FruitAdapter
重寫了父類的一組構造函數,用於將上下文,ListView子項佈局的id和數據都傳遞進來。
另外又重寫了getView()方法這個方法,這個方法在每個子項被滾動到屏幕內的時候會被調用。在getView()方法中,首先通過getItem()方法得到當前項的Fruit實例,然後使用LayoutInflater來爲這個子項加載我們傳入的佈局。
這裏LayoutInflater的inflate()方法接收三個參數,前兩個參數我們已經知道是什麼意思了,第三個參數指定成false,表示只讓我們在父佈局中聲明的layout屬性生效,但不爲這個View添加父佈局,因爲一旦View有了父佈局之後,他就不能再添加到ListView中了。
接下來調用View的findViewById()方法分別獲取到ImageView和TextView的實例。
並分別調用它們的setImageResource()和setText()方法來設置顯示的圖片和文字,,最後將佈局返回,這樣定義的適配器就完成了。
下面修改MainActivity中的代碼,如下所示:
package net.nyist.lenovo.listviewtest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<Fruit>fruitList = new ArrayList<>();
private String[] data = {"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango",
"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();//初始化水果數據
FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView= (ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initFruits(){
for (int i = 0;i < 2;i++){
Fruit apple = new Fruit("Apple",R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana",R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange",R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear",R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape",R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple",R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry",R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry",R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango",R.drawable.mango_pic);
fruitList.add(mango);
}
}
}
這裏添加一個initFruits()方法,用於初始化所有的水果數據。在Fruit類的構造函數中將水果的名字和對應的圖片id傳入,然後把創建好的對象添加到水果列表中,另外我們使用了一個for循環將所有的水果數據添加了兩遍,這是因爲如果只添加一遍的話,數據量還不足以充滿整個屏幕,接着在onCreate()方法中創建了FruitAdpter對象,並將FruitAdapter作爲適配器傳遞給ListView,這樣定製ListView界面的任務就做好了。
#ListView的點擊事件
話說回來,ListView的滾動畢竟只是滿足了我們視覺上的效果,可是如果ListView中的子項不能點擊的話,這個控件就沒有什麼實際的用途了。因此,本小節我們就來學習一下ListView如何才能響應用戶的點擊事件。
修改MainActivity中的代碼,如下所示:
public class MainActivity extends AppCompatActivity {
private List<Fruit>fruitList = new ArrayList<>();
private String[] data = {"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango",
"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();//初始化水果數據
FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView= (ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
}
可以看到,我們使用setOnItemClickListener()方法爲ListView註冊了一個監聽器,當用戶點擊了ListView中的任何一個子項時,就會回調onItemClick()方法。在這個方法中可以通過position參數判斷出用戶點擊的是哪一個子項,然後獲取到響應的水果,並通過Toast將水果的名字顯示出來。