Android列表控件ListView詳解


ListView絕對可以稱得上是Android中最常用的控件之一,幾乎所有應用程序都會用到它。

由於手機屏幕空間都比較有限,能夠一次性在屏幕上顯示的內容並不多,當我們的程序中有大量的數據需要展示的時候,就可以借助ListView來實現。

----------------------------------------------ListView簡易用法------------------------------------------------------

創建一個ListViewTest項目,讓Android Studio自動創建好活動。

然后修改activity_main.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">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </ListView>

</LinearLayout>

引入了ListView后,即使你的ListView沒有內容,可視化編輯器preview仍然會這樣顯示:

接下來修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {

    private String[] data = {"肖申克的救贖", "這個殺手不太冷", "霸王別姬", "泰坦尼克號", "瓦力",
            "三傻大鬧寶萊塢", "放牛班的春天", "千與千尋", "鬼子來了", "星際穿越"};

    @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);
    }
}

直接在MainActivity中定義字符串數組雖然可以達到目的,但是不便於管理。

可以在res目錄下的values文件夾下新建一個array.xml文件,內容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="moive">
        <item>肖申克的救贖</item>
        <item>這個殺手不太冷</item>
        <item>霸王別姬</item>
        <item>泰坦尼克號</item>
        <item>瓦力</item>
        <item>三傻大鬧寶萊塢</item>
        <item>放牛班的春天</item>
        <item>千與千尋</item>
        <item>鬼子來了</item>
        <item>星際穿越</item>
    </string-array>
</resources>

通過Resources對象獲得array.xml的內容:

        Resources resourse = this.getResources();
        String[] data = resourse.getStringArray(R.array.moive);    

 最終結果如下:

-----------------------------------------------------適配器的介紹------------------------------------------------------

既然ListView是用於展示大量數據的,這些數據可以是從網上下載的,也可以是從數據庫中讀取的,應該視具體的應用場景來決定。

數組中的數據是無法直接傳遞給ListView的,還需要借助適配器來完成。

Android中提供了很多的適配器的實現類,其中最簡單的就是ArrayAdapter(注意不是SimpleAdapter)。

它可以通過泛型來指定要適配的數據類型,然后在構造函數中把要適配的數據傳入即可。

ArrayAdapter有多個構造函數的重載,根據需要選擇最合適的一種。這里我簡單介紹其中的一種:

new ArrayAdapter<>( Context context , @LayoutRes int resource , String[] objects );

Context:context表示上下文對象,參數:MainActivity.this

@LayoutRes:resource表示ListView子項布局的id,參數:android.R.layout.simple_list_item_1

(android.R.layout.simple_list_item_1這是Android內置的布局文件,里面只有一個TextView,可用於簡單顯示一段文本)

List<String> | String[]:objects中指定要適配的數據。

適配器構建好之后,還需要調用ListView的setAdapter()方法,將構建好的適配器對象傳遞進去,這樣ListView和數據之間的關聯就建立完成了。

--------------------------------------------定制ListView------------------------------------------------------

只能顯示一段文本的ListView實在是太單調了,我們現在就來對ListView的界面進行定制,讓它可以顯示更加豐富的內容。

效果預覽:

素材:

首先定義一個實體類,作為ListView適配器的適配類型。新建類Browser,代碼如下:

public class Browser {
    private String name;
    private int icon;

    public Browser(String name, int icon) {
        this.name = name;
        this.icon = icon;
    }

    public String getName() {
        return name;
    }

    public int getIcon() {
        return icon;
    }
}

為了盡量簡單,Browser類中只有兩個字段,name表示瀏覽器的名字,icon對應瀏覽器的圖標。

然后需要為ListView的子項指定一個自定義的布局,在layout目錄下新建browser_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">

    <ImageView
        android:id="@+id/browser_icon"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_marginLeft="20dp" />

    <TextView
        android:id="@+id/browser_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="120dp"
        android:textSize="24dp" />

</LinearLayout>

在這個布局中,我們定義了一個ImageView用於顯示瀏覽器圖標,又定義了一個TextView用於顯示瀏覽器的名稱。

為了簡潔,我在ImageView和TextView沒有引入內容。其實引入內容並不會對結果造成影響,而且可以在preview中預覽,以便調整布局。

接下來需要創建一個自定義的適配器,這個適配器繼承自ArrayAdapter,並將泛型指定為Browser類。

新建類BrowserAdapter,代碼如下:

//自定義適配器,繼承自ArrayAdapter
public class BrowserAdapter extends ArrayAdapter<Browser>{
    //resourceID指定ListView的布局方式
    private int resourceID;
    //重寫BrowserAdapter的構造器
    public BrowserAdapter(Context context,int textViewResourceID , List<Browser> objects){
        super(context,textViewResourceID,objects);
        resourceID = textViewResourceID;
    }
    //自定義item資源的解析方式
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //獲取當前Browser實例
        Browser browser = getItem(position);
        //使用LayoutInfater為子項加載傳入的布局
        View view = LayoutInflater.from(getContext()).inflate(resourceID,null);
        ImageView browserIcon = (ImageView)view.findViewById(R.id.browser_icon);
        TextView browserName = (TextView)view.findViewById(R.id.browser_name);
        //引入Browser對象的屬性值
        browserIcon.setImageResource(browser.getIcon());
        browserName.setText(browser.getName());
        return view;
    }
}

BrowserAdapter重寫了父類的一組構造函數,用於將上下文、ListView子項布局的id和數據都傳遞進來。另外又重寫了getView()方法,這個方法在每個子項被滾動到屏幕內的時候會被調用。

在getView方法中,首先通過getItem()方法得到當前項的Browser實例,然后使用LayoutInflater來為這個子項加載我們傳入的布局,接着調用View的findViewById()方法分別獲取到ImageView和TextView的實例,並分別調用它們的setImageResource()和setText()方法來設置顯示的圖片和文字,最后將布局返回,自定義適配器就完成了。

下面修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {

    //Browser實體集合
    private List<Browser> browsers = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化數據
        Browser ie = new Browser("IE", R.drawable.ie);
        Browser chrome = new Browser("Chrome", R.drawable.chrome);
        Browser firefox = new Browser("Firefox", R.drawable.firefox);
        Browser saferi = new Browser("Saferi", R.drawable.safari);

        browsers.add(ie);
        browsers.add(chrome);
        browsers.add(firefox);
        browsers.add(saferi);

        //初始化適配器
        BrowserAdapter adapter = new BrowserAdapter(MainActivity.this, R.layout.browser_item, browsers);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);

    }
}

 雖然目前我們定制的界面還是很簡單,但是如果你領悟到了訣竅,只要修改browser_item.xml中的內容,就可以定制出各種復雜的界面了。

--------------------------------------------提升ListView的運行效率----------------------------------------------

之所以說ListView這個控件很難用,就是因為它有很多的細節可以優化,其中運行效率就是很重要的一點。

目前我們的ListView運行效率是很低的,因為在BrowserAdapter的getView()方法中每次都將布局重新加載了一遍,當ListView快速滾動的時候這就會成為性能的瓶頸。

getView()中還有一個convertView參數,這個參數用於將之前加載好的布局進行緩存,以便之后可以進行重用。

修改BrowserAdapter中的setView()代碼,如下所示:

    public View getView(int position, View convertView, ViewGroup parent) {
        Browser browser = getItem(position);
        View view;
        //convertView為空則加載布局,不為空則重用
        if(convertView == null){ view = LayoutInflater.from(getContext()).inflate(resourceID,null); }else{ view = convertView; }
        ImageView browserIcon = (ImageView)view.findViewById(R.id.browser_icon);
        TextView browserName = (TextView)view.findViewById(R.id.browser_name);
        browserIcon.setImageResource(browser.getIcon());
        browserName.setText(browser.getName());
        return view;

    }

現在我們在getView()方法中進行了判斷,如果convertView為空,則使用LayoutInflater去加載布局,如果不為空則直接對convertView進行重用。這樣就大大提高了ListView的運行效率,在快速滾動的時候可以表現出更好的性能。

不過,雖然現在已經不會再去重復加載布局,但是每次在getView()方法中還是會調用View的findViewById()方法來獲取一次控件的實例。

我們可以借助一個ViewHolder內部類來對這部分性能進行優化,修改BrowserAdapter中的setView()方法,如下所示:

//自定義適配器,繼承自ArrayAdapter
public class BrowserAdapter extends ArrayAdapter<Browser>{
    //resourceID指定ListView的布局方式
    private int resourceID;
    //重寫BrowserAdapter的構造器
    public BrowserAdapter(Context context,int textViewResourceID , List<Browser> objects){
        super(context,textViewResourceID,objects);
        resourceID = textViewResourceID;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Browser browser = getItem(position);
        View view;
        ViewHolder viewHolder;
        if(convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceID,null);
            viewHolder = new ViewHolder();
            viewHolder.browserIcon = (ImageView)view.findViewById(R.id.browser_icon);
            viewHolder.browserName = (TextView)view.findViewById(R.id.browser_name);
            //將ViewHolder存儲在View中
            view.setTag(viewHolder);
        }else {
            view = convertView;
            viewHolder = (ViewHolder)view.getTag();
        }
        viewHolder.browserIcon.setImageResource(browser.getIcon());
        viewHolder.browserName.setText(browser.getName());
        return view;
    }

    class ViewHolder{
        ImageView browserIcon;
        TextView browserName;
    }
}

我們新增了一個內部類ViewHolder,用於對控件的實例進行緩存。當convertView為空的時候,創建一個ViewHolder對象,並將控件的實例都存放在ViewHolder里,然后調用View的setTag()方法,將ViewHolder對象存儲在View中。當convertView不為空的時候調用View的getTag()方法,把ViewHolder重新取出。這樣所有的控件的實例都緩存在了ViewHolder里,就沒有必要每次都通過findViewById()方法來獲取控件實例了。

經過這兩步的優化之后,ListView的運行效率完全可以滿足我們的需要了。

----------------------------------------------ListView的點擊事件-----------------------------------------------------

 使用setOnItemClickListener()方法來為ListView注冊一個監聽器,當用戶點擊了ListView中的任何一個子項時都會回調onItemClick()方法,在這個方法中可以通過position參數判斷出用戶點擊的是哪一個子項,然后執行相應的程序。

        BrowserAdapter adapter = new BrowserAdapter(MainActivity.this, R.layout.browser_item, browsers);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Browser browser = browsers.get(position);                 Toast.makeText(MainActivity.this,browser.getName().toString(),Toast.LENGTH_SHORT).show();
            }
        });
        listView.setAdapter(adapter);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM