如何使用BaseAdapter自訂ListView

如何使用BaseAdapter自訂ListView

情境

在 5.0 以後多了一個 RecyclerView
功能比 ListView 還強大
重點是用起來跟 ListView 大同小異
目前是全面取代 ListView 的利器
可以參考http://givemepass.blogspot.tw/2015/12/recyclerview.html

如果 ListView 想塞圖、塞 Button 或者塞你想放進去的東西怎麼辦?
利用 BaseAdapter 就可以輕鬆辦到

程式碼下載

你可以在 GitHub 觀看或下載完成程式碼

或者複製下面程式碼貼到對應的檔案
一開始先宣告一個 ListView 在 xml 內
main_activity layout.xml 部分

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

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

MainActivity.java 主程式部分

public class MainActivity extends AppCompatActivity {
    private ListView mListView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.list);
        mListView.setAdapter(new MyAdapter());
    }

    private class MyAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            return 3;
        }

        @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) {
            View v = convertView;
            Holder holder;
            if(v == null){
                v = LayoutInflater.from(getApplicationContext()).inflate(R.layout.list_item, null);
                holder = new Holder();
                holder.image = (ImageView) v.findViewById(R.id.image);
                holder.text = (TextView) v.findViewById(R.id.text);

                v.setTag(holder);
            } else{
                holder = (Holder) v.getTag();
            }
            switch(position) {
                case 0:
                    holder.image.setImageResource(R.drawable.cat);
                    holder.text.setText("cat");
                    break;
                case 1:
                    holder.image.setImageResource(R.drawable.monkey);
                    holder.text.setText("monkey");
                    break;
                case 2:
                    holder.image.setImageResource(R.drawable.panda);
                    holder.text.setText("panda");
                    break;
            }
            return v;
        }
        class Holder{
            ImageView image;
            TextView text;
        }
    }
}

在我們的 BaseAdapter 內必須吃一個 row 的l ayout
因此增加一個 adapter.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/image"
        android:layout_centerVertical="true"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"/>

    <TextView
        android:id="@+id/text"
        android:layout_centerVertical="true"
        android:textSize="18sp"
        android:layout_marginLeft="20dp"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_toRightOf="@+id/image"
        android:textColor="#000000"/>

</RelativeLayout>

由於我們的 Layout 內有吃到一些圖檔
可以從下面的連結下載放到 drawable 資料夾內
https://github.com/givemepassxd999/blog_files/blob/master/drawable.zip

程式碼說明

首先我們了解一下什麼是 BaseAdapter
BaseAdapter 是一個超好用的類別 它可以讓你自己定義許多種 View
例如 Spinner, ListView, GridView
那我們要怎麼去定義一個屬於自己的 ListView 呢?

增加一個新的類別叫做 MyAdapter
並且繼承 BaseAdapter
你會發現需要覆寫四個方法分別是

private class MyAdapter 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;
    }
}

IDE 會自動產生這四個方法
這四個方法我來稍微解釋一下
我們知道 ListView 是由一列一列所組成的
而每一個列 我們可以將它看成是一個 View
所組合起來的就是一整個 ListView
所以 getCount() 就是可以取得到底有多少列的方法

而如果我們要取得某一列的內容 就是使用 getItem() 這個方法
如果你想要取得某一列的 id 就使用 getItemId() 這個方法
接著是我們最重要的一個方法 要做修改某一列 View 的內容
就是利用 getView() 這個方法

首先先想好要將 ListView 改變成怎樣?
假設我們想要先塞一個 Button 然後塞一張圖片
最後在加上文字說明好了

增加一個 adapter.xml
裡面就是放每一列 ListView 的內容
根據我們想要的會放下
由於我的 Layout 設定為 RelativeLayout
因此會多一個關聯的屬性

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/image"
        android:layout_centerVertical="true"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"/>

    <TextView
        android:id="@+id/text"
        android:layout_centerVertical="true"
        android:textSize="18sp"
        android:layout_marginLeft="20dp"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_toRightOf="@+id/image"
        android:textColor="#000000"/>

</RelativeLayout>

接著我們在 MyListView 裡面指定 main.xml 為一開始的畫面
然後設定 MyAdapter 為 ListView 的 Adapter

private class MyAdapter extends BaseAdapter{

    @Override
    public int getCount() {
        return 3;
    }

    @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) {
        View v = convertView;
        Holder holder;
        if(v == null){
            v = LayoutInflater.from(getApplicationContext()).inflate(R.layout.list_item, null);
            holder = new Holder();
            holder.image = (ImageView) v.findViewById(R.id.image);
            holder.text = (TextView) v.findViewById(R.id.text);

            v.setTag(holder);
        } else{
            holder = (Holder) v.getTag();
        }
        switch(position) {
            case 0:
                holder.image.setImageResource(R.drawable.cat);
                holder.text.setText("cat");
                break;
            case 1:
                holder.image.setImageResource(R.drawable.monkey);
                holder.text.setText("monkey");
                break;
            case 2:
                holder.image.setImageResource(R.drawable.panda);
                holder.text.setText("panda");
                break;
        }
        return v;
    }
    class Holder{
        ImageView image;
        TextView text;
    }
}

接著換到 MyAdapter.java
簡單的用三列
因此我們將 return 設定為3

public int getCount() {
   return 3;
}

再來就是要取得 View 的順序
先是 Button 再來是 ImageView 接著是 TextView
所以我們宣告成一個類別讓該列的 setTag 屬性去讀取

class Holder{
    ImageView image;
    TextView text;
}

接著我們在getView 方法裡面去讀取它
可以看到我們利用 Holder 在 getView 重複使用
這邊可以參考 如何在ListView中使用Holder pattern來重用view
再來就是設定三個元件上面的圖或文字

switch(position) {
    case 0:
        holder.image.setImageResource(R.drawable.cat);
        holder.text.setText("cat");
        break;
    case 1:
        holder.image.setImageResource(R.drawable.monkey);
        holder.text.setText("monkey");
        break;
    case 2:
        holder.image.setImageResource(R.drawable.panda);
        holder.text.setText("panda");
        break;
}

這樣一來全部設定好
可以將該列的 View 回傳

return view;

最後在 MainActivity.java 使用這個 Adapter 就可以出現畫面了。

private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mListView = (ListView) findViewById(R.id.list);
    mListView.setAdapter(new MyAdapter());
}





這樣就是一個簡單的客製化 ListView 的範例