ListView允許用戶通過手指上下滑動的方式將屏幕外的數據滾動到屏幕內,同時屏幕上原有數據將會滾動出屏幕。
1、ListView簡單用法
如何將ListView將你要顯示的大量內容關聯起來呢?這是個很重要的問題。
1、首先我們必須先將數據提供好,因為你的目的是要用ListView來展示數據嘛。我們先用一個數組來保存數據。
2、那么數組中的數據怎么才能傳遞給ListView呢?我們通過適配器來完成,Android中有很多適配器的實現類,比較好用的就是ArrayAdapter。它可以通過泛型來指定更適配的數據類型,然后在構造函數中把要適配的數據傳入。ArrayAdapter有多個構造函數的重載,可以選擇合適的一個。
3、要調用ListView地setAdapter()方法,將構建好的適配器對象傳遞進去、這樣ListView和數據之間地關聯就建立好了。
詳細步驟:
新建一個ListViewTest項目,讓AS自動創建好默認活動。假如目前活動已經創建好。
修改activity_main.xml中的代碼,(在布局中添加ListView控件),如下:
schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.listviewtest.MainActivity"> <ListViewandroid:id="@+id/list_view"android:layout_width="match_parent"android:layout_height="match_parent"></ListView></LinearLayout>
修改MainActivity中的代碼,如下:
package com.example.listviewtest;import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;import org.w3c.dom.Text;import java.util.List;public class MainActivity extends AppCompatActivity {private String[] data = {"A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","A11","A12","A13","A14","A15","A16","A17","A18","A19","A20","A21","A22","A23","A24"};@Overrideprotected 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的,我們需要借助適配器來完成。Android 中提供了很多適配器的實現類,比較好用的就是ArrayAdapter。它可以通過泛型來指定要適配的數據類型,然后在構造函數中把要適配的數據傳入。ArrayAdapter有多個構造函數的重載,我們選擇比較適合的一種。這里由于我們提供的數據都是字符串,因此將ArrayAdapter的泛型指定為String,然后在ArrayAdapter的構造函數中依次傳入當前的上下文,ListView子項布局的id,以及要適配的數據。
注意:這里我們使用了android.R.layout.simple_list_item_1作為ListView子項布局的id,這是一個Android內置的布局文件,里面只有一個TextView,可用于簡單地顯示一段文本。
最后,我們還需要調用ListView地setAdapter()方法,將構建好的適配器對象傳遞進去、這樣ListView和數據之間地關聯就建立好了。
運行程序如下:
2、定制ListView的界面
只能夠顯示一段文本的ListView實在是太單調了,我們對ListView的界面進行定制,讓它可以顯示更加豐富的內容。
我們可以讓這些名稱的旁邊顯示一個圖樣。
定義一個實體類Fruit,作為ListView適配器的適配類型,新建Fruit,代碼如下:
package com.example.listviewtest;public class Fruit {private String name;private int imageId;public Fruit(String name, int imageId) {this.name = name;this.imageId = imageId;}public String getName() {return name;}public int getImageId() {return imageId;}}
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:orientation="horizontal"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/fruit_image"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid: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 com.example.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;/*** Created by ZHJ on 2018/3/3.*/public class FruitAdapter extends ArrayAdapter<Fruit> {private int resourceId; public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){super(context,textViewResourceId,objects);resourceId = textViewResourceId;}@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {Fruit fruit = getItem(position);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;}
}
FruitAdapter重寫了父類的一組構造函數,用于將上下文、ListView子項布局的id和數據都傳遞進來。另外又重寫了getView()方法,這個方法在每個子項被滾動到屏幕內的時候被調用。
在getView()方法中,
? ? 1、首先通過getItem()方法得到當前項的Fruit實例,
????2、然后使用LayoutInflater來為這個子項加載我們傳入的布局。LayoutInflater的inflate()方法接收3個參數,第一個參數是要加載的布局文件的id,第二個參數是給加載好的布局再添加一個父布局,第三個參數,我們指定為false,表示只讓我們在父布局中聲明的layout屬性生效,但不會為這個View添加父布局。
調用View的findViewById()方法分別獲取ImageView和TextView的實例,并分別調用它們的setImageResource()和setText()方法來設置顯示的圖片和文字。
接著修改MainActivity中的代碼,如下:
package com.example.listviewtest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<Fruit>();//新建對象實例
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); initFruits(); // 初始化水果數據FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);//傳入上下文、子項布局的id、數據ListView listView = (ListView) findViewById(R.id.list_view);listView.setAdapter(adapter);listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic 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();}});}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()方法中創建了FruiteAdapter對象,并將FruitAdapter作為適配器傳遞給ListView,這樣ListView界面就完成了。
提升ListView的運行效率
之所以說ListView這個控件很難運用,因為它有很多細節可以優化,其中運行效率就是很重要的一點。目前,我們的運行效率是很低的,因為在FruitAdapter的getView()方法中,每次都將布局重新加載了一遍,當ListView快速滾動的時候,這就會成為性能的瓶頸。
仔細觀察會發現,getView()方法中還有一個convertView參數,這個參數用于將之前加載好的布局進行緩存,一遍日后重用,修改FruitAdapter中的代碼,如下:
package com.example.listviewtest;import android.content.Context;
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;}//構造函數@Overridepublic View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); // 獲取當前項的Fruit實例View view;ViewHolder viewHolder;if (convertView == null) {view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);viewHolder = new ViewHolder();viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);view.setTag(viewHolder); // 將ViewHolder存儲在View中 } else {view = convertView;viewHolder = (ViewHolder) view.getTag(); // 重新獲取ViewHolder}viewHolder.fruitImage.setImageResource(fruit.getImageId());viewHolder.fruitName.setText(fruit.getName());return view;} class ViewHolder {ImageView fruitImage;TextView fruitName;}}