Android画像付きリストの設定(ListView)
Androidでの開発で、リストビューに項目毎に画像を置くケースでの対応です。
メモリ使用量を抑える為、スクロールさせて実際に必要になったときだけ表示する対処を行う。
● 読込中と読込完了時の設定
AsyncTaskでバックグラウンド処理を行い、読込中の場合はプログレスバーを表示させる。
● 画像データのキャッシュの使用
AsyncTaskで一度読み込んだ画像をキャッシュに保持し、スクロールにより再表示する時にwebからの読込を省略する事で負荷を軽減する。
● 画像データのキャッシュのクリア
キャッシュを保持し続けるとメモリ使用量が増える為、リスト画面終了時にキャッシュをクリアする処理を呼び出す。
以上の事を踏まえたソースコードを以下に示します。
画面レイアウト設定用XMLファイル
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:padding="0dp" android:layout_margin="0dp" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/TextTitleMain" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="12dp" android:textStyle="bold" android:layout_marginTop="4dp" android:layout_marginBottom="4dp" android:layout_marginLeft="4dp" android:text="スタンプ選択 - 定型文" /> </LinearLayout> <ListView android:id="@+id/listGroup" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="fill" android:background="#ddffffff" android:padding="0dp" android:layout_margin="0dp" > </ListView> </LinearLayout>
リストレイアウト設定用XMLファイル
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" > <!-- 非表示項目(選択時に呼び出し元に渡す画像URL) --> <TextView android:id="@+id/TextUrl" android:layout_width="0dp" android:layout_height="0dp" android:text="a" android:visibility="gone" /> <LinearLayout android:id="@+id/viewWait" android:layout_width="fill_parent" android:layout_height="60dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:gravity="center" > <!-- リスト画像読込中に表示するプログレスバー --> <ProgressBar android:id="@+id/WaitBar" android:layout_width="wrap_content" android:layout_height="wrap_content" style="?android:attr/progressBarStyleSmallInverse" android:layout_gravity="center_vertical|center_horizontal" /> <!-- リスト画像 --> <ImageView android:id="@+id/ImageThumb" android:layout_width="wrap_content" android:layout_height="fill_parent" android:visibility="gone" /> </LinearLayout> </LinearLayout>
リスト表示設定
Activity内にSimpleAdapter 継承クラスを作成する
画像の情報がURLなどの文字情報からデータを取得して表示するようにカスタマイズする必要がある。その時に呼び出されるメソッドがgetViewメソッドで、新しいデータが表示されるタイミングで呼び出される。ListViewなら、スクロールして、画面外から新しいデータが表示されるタイミングである。
private class ListTemplateAdapter extends SimpleAdapter { private Context context; private LayoutInflater inflater; private ArrayList<Map<String, Object>> items; //コンストラクタ public ListTemplateAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) { super(context, data, resource, from, to); this.context = context; this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } private void setListData(ArrayList<Map<String, Object>> data){ //データ内容を保持しておく items = data; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if(v == null){ v = inflater.inflate(R.layout.listview_template, null); } Map<String, Object> settingData = items.get(position); TextView textUrl = (TextView)v.findViewById(R.id.TextUrl); ImageView imageView = (ImageView)v.findViewById(R.id.ImageThumb); ProgressBar waitBar = (ProgressBar)v.findViewById(R.id.WaitBar); //画像を隠し、プログレスバーを表示 waitBar.setVisibility(View.VISIBLE); imageView.setVisibility(View.GONE); //実際に使用する画像のURLを保持 textUrl.setText(settingData.get("url").toString()); //リスト表示用画像のURLを取得 String thumbUrl = settingData.get("viewImage").toString(); //仮の画像設定 imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.blank)); //画像読込 try{ imageView.setTag(thumbUrl); // AsyncTaskは1回しか実行できない為、毎回インスタンスを生成 ImageGetTask task = new ImageGetTask(imageView, waitBar); task.execute(thumbUrl); } catch(Exception e){ imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.x)); waitBar.setVisibility(View.GONE); imageView.setVisibility(View.VISIBLE); } return v; } class ImageGetTask extends AsyncTask<String,Void,Bitmap> { //以下に記載 } }
Image取得用スレッドクラス(AsyncTask)
SimpleAdapter継承クラス内に作成
AsyncTaskによりバックグラウンド処理で画像データの取得をし、
画像以外は先に表示しておく。
class ImageGetTask extends AsyncTask<String,Void,Bitmap> { private ImageView image; private ProgressBar progress; private String tag; public ImageGetTask(ImageView _image, ProgressBar _progress) { //対象の項目を保持しておく image = _image; progress = _progress; tag = image.getTag().toString(); } @Override protected Bitmap doInBackground(String... params) { // ここでHttp経由で画像を取得します。取得後Bitmapで返します。 synchronized (context){ try { //キャッシュより画像データを取得 Bitmap image = ImageCache.getImage(params[0]); if (image == null) { //キャッシュにデータが存在しない場合はwebより画像データを取得 URL imageUrl = new URL(params[0]); InputStream imageIs; imageIs = imageUrl.openStream(); image = BitmapFactory.decodeStream(imageIs); //取得した画像データをキャッシュに保持 ImageCache.setImage(params[0], image); } return image; } catch (MalformedURLException e) { return null; } catch (IOException e) { return null; } } } @Override protected void onPostExecute(Bitmap result) { // Tagが同じものか確認して、同じであれば画像を設定する // (Tagの設定をしないと別の行に画像が表示されてしまう) if(tag.equals(image.getTag())){ if(result!=null){ //画像の設定 image.setImageBitmap(result); } else{ //エラーの場合は×印を表示 image.setImageDrawable(context.getResources().getDrawable(R.drawable.x)); } //プログレスバーを隠し、取得した画像を表示 progress.setVisibility(View.GONE); image.setVisibility(View.VISIBLE); } } }
画像データのキャッシュ
リスト表示用画像はwebから読み込む為、
一度読み込んだ画像をスクロールしてもう一度表示させるときに
再度web接続をするという非効率な操作となってしまう。
その為、キャッシュに一度読み込んだ画像を保持し、
スクロール操作での画像表示の効率化を行う。
public class ImageCache { private static HashMap<String,Bitmap> cache = new HashMap<String,Bitmap>(); //キャッシュより画像データを取得 public static Bitmap getImage(String key) { if (cache.containsKey(key)) { return cache.get(key); } //存在しない場合はNULLを返す return null; } //キャッシュに画像データを設定 public static void setImage(String key, Bitmap image) { cache.put(key, image); } //キャッシュの初期化(リスト選択終了時に呼び出し、キャッシュで使用していたメモリを解放する) public static void clearCache(){ cache = null; cache = new HashMap<String,Bitmap>(); } }
初めはキャッシュのクリア(clearCache)を設置していなかったが、
多くのグループのリストを見ているうちに落ちてしまった為、
リスト画面の終了時にキャッシュをクリアするようにした。
Activityの設定
ArrayList<Map<String, Object>> data; ListTemplateAdapter adapter = null; //独自のアダプタを作成 String[] from_template = {"url","viewImage"}; int[] to_template = {R.id.TextUrl, R.id.ImageThumb}; //画面のリストビュー ListView list_group = (ListView) Activity.findViewById(R.id.listGroup); //テンプレートのリスト取得 data = new ArrayList<Map<String, Object>>(); Map<String, Object> map; //データの設定(仮・データ件数毎の設定) map = new HashMap<String, Object>(); map.put("url", <呼び出し元に渡す画像のURL>); map.put("viewImage", <リスト表示用画像のURL>); data.add(map); adapter = new ListTemplateAdapter(Activity, data, R.layout.listview_template, from_template, to_template); if(data != null && data.size() > 0){ adapter.setListData(data); //テンプレートにデータ内容を保持 list_group.setFocusable(false); list_group.setAdapter(adapter); list_group.setScrollingCacheEnabled(false); list_group.setTextFilterEnabled(true); list_group.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //リスト選択時の処理 ImageCache.clearCache(); //キャッシュのクリア //終了処理 } }); list_group.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { //リスト選択時の処理 ImageCache.clearCache(); //キャッシュのクリア //終了処理 } @Override public void onNothingSelected(AdapterView<?> parent) { } }); }
● 参考文献
Adapterの高速化
ListViewをカスタマイズする
AsyncTaskでユーザビリティを向上させる
AsyncTaskは使い捨て
マンガとアニメとゲームから錬成された宇宙大好きエンジニア。 軌道エレベーターで行ける静止軌道上のコロニーに住まいを移し、ゲームやってマンガ読んでアニメ見て爆睡、ゲームやってマンガ読んでアニメ見て爆睡、という生活を夢見ながら今日もコードを書き続けるのだった。
TAG
- Android
- AWS
- Bitrise
- CodePipeline
- Firebase
- HTML
- iOS
- IoT
- JavaScript
- KPI
- Linux
- Mac
- Memcached
- MGRe
- MGReのゆるガチエンジニアブログ
- MySQL
- PHP
- PICK UP
- PR
- Python
- Ruby
- Ruby on Rails
- SEO
- Swift
- TIPS
- UI/UX
- VirtualBox
- Wantedly
- Windows
- アクセス解析
- イベントレポート
- エンジニアブログ
- ガジェット
- カスタマーサクセス
- サーバ技術
- サービス
- セキュリティ
- セミナー・展示会
- テクノロジー
- デザイン
- プレスリリース
- マーケティング施策
- マネジメント
- ラボ
- リーンスタートアップ
- 企画
- 会社紹介
- 会社紹介資料
- 勉強会
- 実績紹介
- 拡張性
- 採用
- 日常
- 書籍紹介
- 歓迎会
- 社内イベント
- 社員インタビュー
- 社長ブログ
- 視察
- 開発環境