ListViewのリスト表示のitem要素の位置を移動したり、削除するにはどうすればいいでしょう。
また、更新を画面に反映させるためには notifyDataSetChanged() を使います。
2021.2.1
ListView
リスト表示させると表示位置を変更したり、追加、削除したいという要望が出てきます。やり方としては、
BaseAdapter が持っている、remove()、add() を使う
1 |
BaseAdapter adapter = new ListViewAdapter(this, R.layout.list_layout); |
要素自体の位置変更、追加、削除をする。data は配列や ArrayList<T> など
1 |
BaseAdapter adapter = new ListViewAdapter(this, R.layout.list_layout, data); |
が考えられます。
今回は2番目のArrayList<>のケースで作ってみました。
ArrayList
この例ではListを使っていますが、Listはインターフェースのためインスタンスを生成できないのでArrayList<>を使っています。
要素の移動や削除:
ArrayList<> の要素がListViewのCellに対応しているので、そのArrayの順番を変えたり、その要素の属性を変更して再描画させます。ArrayListの使い方
上に移動する場合:
arrayの要素の順番を入れ替える
1 2 3 |
temp = items.get(position-1); items.set(position-1, items.get(position)); items.set(position, temp); |
削除する場合:
1 |
items.remove(position); |
削除、移動は、ArrayList の位置にある要素を削除、移動しますが、最後に
notifyDataSetChanged() でListView を更新しています。これをしないとリストに反映されません。
AlertDialog:
移動や削除などのユーザー入力を受け付けるためにAlertDialogを使って見ます。
AlertDialogはDialogFragmentを継承したクラスを呼び、その中でonCreateDialog()を使って作成します。
AlertDialog
AlertDialogは要素のタップしたタイミングで呼び出します。
ListViewリストをタップして画面遷移 と同様にonItemClickを使います。
サンプルコード
画像をリスト表示して、移動、削除などができるアプリはこのようになります。
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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
//package com.example.testlistviewitemchange; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AlertDialog; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { private BaseAdapter adapter; // 要素の削除、順番変更のためArrayListを定義 private List<String> itemNames; private List<Integer> itemImages; // タップされたitemの位置 private int tappedPosition = 0; // Isle of Wight in U.K. private static final String[] scenes = { // Scenes of Isle of Wight "Ventnor", "Wroxall", "Whitewell", "Ryde", "StLawrence", "Lake", "Sandown", "Shanklin" }; // それぞれの画像ファイルをdarawableに入れます // ArrayListにコピーするためintからInteger型にしました private static final Integer[] photos = { R.drawable.ventnor, R.drawable.wroxall, R.drawable.whitewell, R.drawable.ryde, R.drawable.stlawrence, R.drawable.lake, R.drawable.sandown, R.drawable.shanklin }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 配列をArrayListにコピー itemNames = new ArrayList<>(Arrays.asList(scenes)); itemImages = new ArrayList<>(Arrays.asList(photos)); // ListViewのインスタンスを生成 ListView listView = findViewById(R.id.list_view); // BaseAdapter を継承したadapterのインスタンスを生成 // レイアウトファイル list.xml を activity_main.xml に inflate するためにadapterに引数として渡す adapter = new ListViewAdapter(this.getApplicationContext(), R.layout.list, itemNames, itemImages); // ListViewにadapterをセット listView.setAdapter(adapter); listView.setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { String item = itemNames.get(position); // 選択された位置を保持する setPosition(position); // アラートダイアログ alertCheck(item); } private void setPosition(int position) { tappedPosition = position; } private int getPosition() { return tappedPosition; } private void alertCheck(String item) { String[] alert_menu = {"上に移動", "下に移動", "削除", "cancel"}; AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle(item); // lambda alert.setItems(alert_menu, (dialog, idx) -> { // リストアイテムを選択したときの処理 // 上に移動 if (idx == 0) { moveAbove(); } // 下に移動 else if (idx == 1) { moveBelow(); } // アイテムの削除 else if (idx == 2) { deleteCheck(); } // cancel else { Log.d("debug", "cancel"); } }); alert.show(); } private void deleteCheck() { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); // AlertDialogのタイトル設定します alertDialogBuilder.setTitle("削除"); // AlertDialogのメッセージ設定 alertDialogBuilder.setMessage("本当に削除しますか?"); // AlertDialogのYesボタンのコールバックリスナーを登録 alertDialogBuilder.setPositiveButton("Yes", (dialog, which) -> deleteItem()); // AlertDialogのNoボタンのコールバックリスナーを登録 alertDialogBuilder.setNeutralButton("No", (dialog, which) -> { }); // AlertDialogのキャンセルができるように設定 alertDialogBuilder.setCancelable(true); AlertDialog alertDialog = alertDialogBuilder.create(); // AlertDialogの表示 alertDialog.show(); } private void moveAbove() { int position = getPosition(); if (position > 0) { String str = itemNames.get(position - 1); itemNames.set(position - 1, itemNames.get(position)); itemNames.set(position, str); int temp = itemImages.get(position - 1); itemImages.set(position - 1, itemImages.get(position)); itemImages.set(position, temp); } else { Log.d("debug", "error: moveAbove()"); } // ListView の更新 adapter.notifyDataSetChanged(); } private void moveBelow() { int position = getPosition(); if (position < itemNames.size() - 1) { String str = itemNames.get(position + 1); itemNames.set(position + 1, itemNames.get(position)); itemNames.set(position, str); int temp = itemImages.get(position + 1); itemImages.set(position + 1, itemImages.get(position)); itemImages.set(position, temp); } else { Log.d("debug", "error: moveBelow()"); } // ListView の更新 adapter.notifyDataSetChanged(); } private void deleteItem() { int position = getPosition(); // それぞれの要素を削除 itemNames.remove(position); itemImages.remove(position); // ListView の更新 adapter.notifyDataSetChanged(); } } |
アダプターのクラスです。BaseAdapterを継承したクラスとMainActivity.javaの側に作成してコーディングします
ListViewAdapter.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 |
//package com.example.testlistviewitemchange; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; public class ListViewAdapter extends BaseAdapter { static class ViewHolder { TextView textView; ImageView imageView; } private final LayoutInflater inflater; private final int itemLayoutId; private final List<String> titles; private final List<Integer> ids; ListViewAdapter(Context context, int itemLayoutId, List<String> itemNames, List<Integer> itemImages) { super(); this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.itemLayoutId = itemLayoutId; this.titles = itemNames; this.ids = itemImages; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; // 最初だけ View を inflate して、それを再利用する if (convertView == null) { // activity_main.xml に list.xml を inflate して convertView とする convertView = inflater.inflate(itemLayoutId, parent, false); // ViewHolder を生成 holder = new ViewHolder(); holder.textView = convertView.findViewById(R.id.textView); holder.imageView = convertView.findViewById(R.id.imageView); convertView.setTag(holder); } // holder を使って再利用 else { holder = (ViewHolder) convertView.getTag(); } // holder の imageView にセット holder.imageView.setImageResource(ids.get(position)); // 現在の position にあるファイル名リストを holder の textView にセット holder.textView.setText(titles.get(position)); return convertView; } @Override public int getCount() { // texts 配列の要素数 return titles.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } } |
好みの画像をdrawable以下に入れてレイアウトを決めます。
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://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=".MainActivity"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
inflateするレイアウトファイル
list.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 |
<?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"> <ImageView android:id="@+id/imageView" android:scaleType="centerCrop" android:layout_margin="5dp" android:contentDescription="@string/description" android:layout_width="120dp" android:layout_height="80dp" /> <TextView android:id="@+id/textView" android:layout_margin="29dp" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:textColor="#000" /> </LinearLayout> |
一応リソースファイルも
strings.xml
1 2 3 4 |
<resources> <string name="app_name">your app name</string> <string name="description">picture</string> </resources> |
実は、RecyclerViewを使うともっとインタラクティブなセル移動が可能です。難易度はあがりますが
- ArrayAdapter
- ListView と ArrayAdapter 簡単なテキストリストの表示
- ArrayAdapterを使ってレイアウトをアレンジ
- ListActivity と ArrayAdapterで画像とテキストをリスト表示
- Basedapter
- BaseAdapterで画像とテキストをリスト表示
- ListViewリストをタップして画面遷移
- ListViewアイテムの移動、削除
- ListView アイテム個々の背景、高さなどを変える
References:
ListView
BaseAdapter