ArrayAdapterを使えば手軽に作れる ListView ですが、画像とテキストのリストなど色々と細かく作りこみたい場合はカスタムでadapterを作成することになります。BaseAdapterを継承してカスタムでadapterを作るケースを試してみます。
2021.2.1
ListView
メンバー11人の写真、スタッフ管理番号、eメールアドレス、電話番号(内線)そして名前をリスト化したアプリを作成してみたいと思います。
メンバー:
![[Android] BaseAdapterで画像とテキストをListView表示 1x1.trans - [Android] BaseAdapterで画像とテキストをListView表示](https://akira-watson.com/wp-content/themes/simplicity2/images/1x1.trans.gif)
最終的にこのようになります。
![[Android] BaseAdapterで画像とテキストをListView表示 1x1.trans - [Android] BaseAdapterで画像とテキストをListView表示](https://akira-watson.com/wp-content/themes/simplicity2/images/1x1.trans.gif)
ListViewを作るためのBaseAdapterを継承したクラスは3つあります。
- SimpleAdapter
- ArrayAdapter
- CursorAdapter
単純なテキストリストはArrayAdapterを使ったほうが簡単です。ただし込み入ったレイアウトにしたい時はそのままBaseAdapterをカスタム化して使ったほうがいいでしょう。
BaseAdapter
カスタムadapterを作成する手順としては:
- BaseAdapterを継承したクラスを作成
- getView() メソッドをオーバライト
- リスト項目のレイアウトをカスタマイズ
基本的なBaseAdapterの構造は
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | // BaseAdapterを継承したクラス private class TestAdapter extends BaseAdapter {     private Context context;     public TestAdapter(Context context) {         super();         this.context = context;     }     @Override     public int getCount() {         return data.length;     }     @Override     public Object getItem(int position) {         return position;     }     @Override     public long getItemId(int position) {         return position;     }     // getViewメソッドをOverride     @Override     public View getView(int position, View convertView, ViewGroup parent) {         // レイアウトを作成         if (convertView == null) {         }else{         }         return convertView;     } } | 
- getCount(): 配列やListの要素数を返す
- getItem(): indexやオブジェクトを返す
- getItemId(): 特別なIDをindexの他に返す
- getView(): setImageBitmap() など、ここで描画させている
サンプルコード
実際に作っていきたいと思います。
 
MainActivity.java
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | //package com.example.testbaseadapter; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.BaseAdapter; import android.widget.ListView; import java.util.Locale; public class MainActivity extends AppCompatActivity {     private static final String[] names = {             "Yuka",             "Kurumi",             "Tomoya",             "Mai",             "Miki",             "Saya",             "Toko",             "Nagi",             "Yuyu",             "Yumiko",             "Katakuriko"     };     // drawableに画像を入れる、R.id.xxx はint型     private static final int[] photos = {             R.drawable.yuka,             R.drawable.kurumi,             R.drawable.tomoya,             R.drawable.mai,             R.drawable.miki,             R.drawable.saya,             R.drawable.toko,             R.drawable.nagi,             R.drawable.yuyu,             R.drawable.yumiko,             R.drawable.katakuriko     };     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         String[] emails = new String[names.length];         // nameからメルアド作成         for(int i=0; i< names.length ;i++ ){             emails[i] = String.format(Locale.US, "%s@mail.co.jp",names[i]);         }         // ListViewのインスタンスを生成         ListView listView = findViewById(R.id.listView);         // BaseAdapter を継承したadapterのインスタンスを生成         // レイアウトファイル list_items.xml を         // activity_main.xml に inflate するためにadapterに引数として渡す         BaseAdapter adapter = new TestAdapter(this.getApplicationContext(),                 R.layout.list_items, names, emails, photos);         // ListViewにadapterをセット         listView.setAdapter(adapter);     } } | 
 
新しいTestAdapterクラスをprojectに作成します。
TestAdapter.java
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | //package com.example.testbaseadapter; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; public class TestAdapter extends BaseAdapter {     private final LayoutInflater inflater;     private final int layoutID;     private final String[] namelist;     private final String[] emaillist;     private final Bitmap[] photolist;     static class ViewHolder {         TextView text;         TextView email;         ImageView img;     }     TestAdapter(Context context, int itemLayoutId,                 String[] names, String[] emails, int[] photos ){         inflater = LayoutInflater.from(context);         layoutID = itemLayoutId;         namelist = names;         emaillist = emails;         // bitmapの配列         photolist = new Bitmap[photos.length];         // drawableのIDからbitmapに変換         for( int i = 0; i< photos.length; i++){             photolist[i] = BitmapFactory.decodeResource(context.getResources(), photos[i]);         }     }     @Override     public View getView(int position, View convertView, ViewGroup parent) {         ViewHolder holder;         if (convertView == null) {             convertView = inflater.inflate(layoutID, null);             holder = new ViewHolder();             holder.img = convertView.findViewById(R.id.img_item);             holder.text = convertView.findViewById(R.id.text_view);             holder.email = convertView.findViewById(R.id.text_mail);             convertView.setTag(holder);         } else {             holder = (ViewHolder) convertView.getTag();         }         holder.img.setImageBitmap(photolist[position]);         String str = "Staff ID.170900"+ (position+1)                 + "\n\nEmail: " + emaillist[position]                 + "\nTel: 020-8931-9933 #340" + (position+1);         holder.email.setText(str);         holder.text.setText(namelist[position]);         return convertView;     }     @Override     public int getCount() {         return namelist.length;     }     @Override     public Object getItem(int position) {         return position;     }     @Override     public long getItemId(int position) {         return position;     } } | 
 
レイアウトファイル
RelativeLayoutを使いますが、constraintlayoutでも可能です。
activity_main.xml
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?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:padding="10dp"     android:background="#44a"     tools:context=".MainActivity">     <ListView         android:id="@+id/listView"         android:divider="@android:color/transparent"         android:dividerHeight="4dp"         android:layout_width="match_parent"         android:layout_height="wrap_content" /> </RelativeLayout> | 
 
新しくLayout XMLファイルをlayout以下に作成します。
list_items.xml
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout      xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="horizontal"     android:background="#afc"     android:layout_width="match_parent"     android:layout_height="match_parent">     <ImageView         android:id="@+id/img_item"         android:scaleType="centerCrop"         android:layout_margin="5dp"         android:contentDescription="@string/description"         android:layout_width="120dp"         android:layout_height="120dp" />     <LinearLayout         android:orientation="vertical"         android:layout_width="wrap_content"         android:layout_height="match_parent">         <TextView             android:id="@+id/text_mail"             android:textColor="#000"             android:layout_marginTop="10dp"             android:layout_marginStart="20dp"             android:layout_width="wrap_content"             android:layout_height="wrap_content" />         <TextView             android:id="@+id/text_view"             android:textColor="#04e"             android:textSize="20sp"             android:layout_marginTop="10dp"             android:layout_marginStart="20dp"             android:layout_width="wrap_content"             android:layout_height="wrap_content" />     </LinearLayout> </LinearLayout> | 
 
strings.xml
| 1 2 3 4 | <resources>     <string name="app_name">Your App Name</string>     <string name="description">pictures</string> </resources> | 
 
サムネイル化(150x150pixel程度)した小さい画像をdrawableに入れます。
(API24以上を想定した場合はdrawable-v24に入れる)
 
![[Android] BaseAdapterで画像とテキストをListView表示 1x1.trans - [Android] BaseAdapterで画像とテキストをListView表示](https://akira-watson.com/wp-content/themes/simplicity2/images/1x1.trans.gif)
画像は適当なものでかまいませんが、ファイル名がコードで呼ばれているので合わせてください。
 
これで実行してみます。
 
convertViewの再利用
リスト数が10個程度だとあまり気にならないのですが、画像を貼り付けてリスト数が数百になると、あるいはリストを増やせる仕様のケースでは、スクロールが重くなり最終的には Out Of Memory で終了…となる可能性があります。
 
ちょっと古いのですがGoogleがListViewについて解説した2010の動画です。今も使える内容で基本的な事が良く分かります。
Google I/O 2010 – The world of ListView
10000のアイテムをそのままリスト表示させていたらメモリがいくらあっても足りない
描画負荷を減らし、高速にするための手法として
| 1 2 3 4 5 6 7 8 9 10 11 | ViewHolder holder; if (convertView == null) { 	convertView = mInflater.inflate(R.layout.list_item_icon_text, null); 	holder = new ViewHolder(); 	holder.img = (ImageView) convertView.findViewById(R.id.img_item); 	convertView.setTag(holder); } else { 	holder = (ViewHolder) convertView.getTag(); } | 
 
if (convertView == null){
…
}else{
…
}
の部分で convetView がnullでなければ再利用する
– Inflate()
– findViewById()
は省略され、ViewHolder の children view が使用されます。
テキストのリストであれば問題ないかもしれませんが、画像を扱うListViewではこれは必須です。
 
- ArrayAdapter
- ListView と ArrayAdapter 簡単なテキストリストの表示
- ArrayAdapterを使ってレイアウトをアレンジ
- ListActivity と ArrayAdapterで画像とテキストをリスト表示
- Basedapter
- BaseAdapterで画像とテキストをリスト表示
- ListViewリストをタップして画面遷移
- ListViewアイテムの移動、削除
- ListView アイテム個々の背景、高さなどを変える
References:
ListView
BaseAdapter
