質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Q&A

解決済

1回答

2021閲覧

【Android】EditTextを含むListViewをスクロールするとEditTextの内容が元に戻る

inumi

総合スコア13

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

0グッド

0クリップ

投稿2018/12/01 16:48

編集2018/12/01 17:13

前提・実現したいこと

ListViewの表示アイテムにEditTextを含めたいのですが、入力後にENTERキーで確定させないでListViewをスクロールさせるとEditTextへの入力内容が元に戻ってしまいます。

ListViewのアイテムをListItemをいうクラスにして、ListItem型のデータとListViewはAdapterで接続しています。

発生している問題

ListItemのデータを書き換えができれば、表示内容は保持されることは分かりましたが、ENTERキーを押して確定するという動作が必要になってしまっています。文字を入力した段階でデータとして更新する方法が見つかりません。

該当のソースコード

java

1(ListItem.java) 2public class ListItem { 3 private int id; 4 private String value; 5 6 public int getId() { return this.id; } 7 public void setId(int id) { this.id = id; } 8 public String getValue() { return this.value; } 9 public void setValue(String value) { this.value = value; } 10} 11 12(MainActivity.java) 13public class MainActivity extends AppCompatActivity { 14 15 MyListAdapter adapter; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 this.setContentView(layout.activity_main); 21 22 ArrayList<ListItem> data = new ArrayList<>(); 23 for (int i = 0; i < 20; i++) { 24 ListItem item = new ListItem(); 25 item.setValue(Integer.toString(i)); 26 data.add(item); 27 } 28 this.adapter = new MyListAdapter(this, data, layout.list_item); 29 ListView list = (ListView) this.findViewById(id.list); 30 list.setAdapter(this.adapter); 31 } 32} 33 34(MyListAdapter.java) 35public class MyListAdapter extends BaseAdapter { 36 private Context context; 37 private ArrayList<ListItem> data; 38 private int resource; 39 private final LayoutInflater inflater; 40 41 static class ViewHolder { 42 EditText editText; 43 } 44 45 public int getCount() { 46 return this.data.size(); 47 } 48 49 public Object getItem(int position) { 50 return this.data.get(position); 51 } 52 53 public long getItemId(int position) { 54 return this.data.get(position).getId(); 55 } 56 57 public MyListAdapter(Context context, ArrayList<ListItem> data, int resource) 58 { 59 this.context = context; 60 this.data = data; 61 this.resource = resource; 62 inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 63 } 64 65 public View getView(final int position, View convertView, final ViewGroup parent) { 66 Activity activity = (Activity) this.context; 67 final ListItem item = (ListItem) this.getItem(position); 68 final MyListAdapter.ViewHolder holder; 69 70 if (convertView == null) { 71 convertView = activity.getLayoutInflater().inflate(this.resource, null); 72 holder = new MyListAdapter.ViewHolder(); 73 holder.editText = (EditText) convertView.findViewById(id.editText); 74 convertView.setTag(holder); 75 } 76 else{ 77 holder = (MyListAdapter.ViewHolder)convertView.getTag(); 78 } 79 80 holder.editText.addTextChangedListener(new TextWatcher() { 81 @Override 82 public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { 83 } 84 85 @Override 86 public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { 87 } 88 89 @Override 90 public void afterTextChanged(Editable editable) { 91 /* 実験コード1 スクロール範囲外の行も一緒に変わってしまう */ 92 //item.setValue(editable.toString()); 93 } 94 }); 95 96 holder.editText.setOnKeyListener(new View.OnKeyListener() { 97 @Override 98 public boolean onKey(View view, int i, KeyEvent keyEvent) { 99 //実験コード2 ENTERキーで確定すれば希望通りとなる */ 100 String str = holder.editText.getText().toString(); 101 item.setValue(str); 102 return false; 103 } 104 }); 105 ((EditText) holder.editText).setText(item.getValue()); 106 107 return convertView; 108 } 109}

xml

1(list_item.xml) 2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical"> 6 7 <RelativeLayout 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" 10 android:layout_gravity="center_horizontal"> 11 12 <EditText 13 android:id="@+id/editText" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:layout_alignParentEnd="true" 17 android:layout_alignParentTop="true" 18 android:inputType="none" 19 android:digits="0123456789abcdefABCDEF" 20 android:maxLength="2"/> 21 </RelativeLayout> 22</LinearLayout> 23 24(activity_main.xml) 25<?xml version="1.0" encoding="utf-8"?> 26<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 27 xmlns:tools="http://schemas.android.com/tools" 28 android:layout_width="match_parent" 29 android:layout_height="match_parent" 30 android:paddingBottom="@dimen/activity_vertical_margin" 31 android:paddingLeft="@dimen/activity_horizontal_margin" 32 android:paddingRight="@dimen/activity_horizontal_margin" 33 android:paddingTop="@dimen/activity_vertical_margin" 34 tools:context="jp.oops.listmyadapter.MainActivity"> 35 36 <ListView 37 android:layout_width="fill_parent" 38 android:layout_height="fill_parent" 39 android:id="@+id/list" /> 40 41</RelativeLayout>

試したこと

前出の実験コード1~2のいずれを有効にしてみました。
実験コード2の箇所で、EditTextへ入力したデータを取得できれば、望む結果になりそうです。

補足情報(Android Studio 3.1.4 / windows10 Pro 64bit)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ENTERキーが押されなくても、EditText 内のテキストが変わったらそれを保持しておきたいとのことなので、実験コード1で使われている TextWatcher を使うのがよいと思います。実験コード2の方法では、ENTERなどのキーが押されないと、保持しているデータを更新できません。

実験コード1の問題点

実験コード1での「スクロール範囲外の行も一緒に変わってしまう」問題は、ListViewが、画面外に消えた行のViewを、画面内に入ってくる別の行のために再利用していることに起因しています。

TextView.addTextChangedListener() は、"add" という名前が示す通り、新しいリスナー (TextWatcher) を追加登録するためのメソッドです。このメソッドを呼び出しても、EditTextにすでに登録されているリスナーは削除されません。つまり、新しい行のためにViewを再利用するとき、古い行のためのリスナーは EditText に登録されたままです。その結果、EditText のテキストが変更されると、新しい行のためのリスナーと古い行のためのリスナーの両方が呼ばれ、古い行も更新されてしまいます。

実験コード2がうまく動いた理由

実験コード2で利用している View.setOnKeyListener() は、新しいリスナー (View.OnKeyListener) を上書き登録するためのメソッドです。このメソッドを呼び出すと、View に登録されている古いリスナーは削除されます。新しい行のためにViewを再利用するとき、古い行のための View.OnKeyListenerEditText から削除されるので、古い行の ListItem は更新されなくなり、期待通りの動作になります。

TextWatcher を使いながらスクロール範囲外の行は変えないようにする方法

上記をふまえて、実験コード1を修正する方法ですが、大きく以下の2つの方針が考えられます。

  • 方針1: Viewを再利用するときに、古いリスナーを削除する
  • 方針2: Viewを再利用するときに、EditText に登録されている TextWatcher も再利用する

TextView に「登録済みの TextWatcher を全て削除する」というようなメソッドが用意されているなら、方針1が簡単なのですが、残念ながら用意されていないため、今回のケースでは方針2のほうがシンプルに実装できそうです。

方針2で TextWatcher を再利用するというのは、ある TextWatcher が更新対象とする ListItem を、再利用時に変える必要があるということです。実装方法はいくつか考えられますが、TextWatcherViewHolder の中に移動して、TextWatcher が更新対象とする ListItemViewHolder のフィールドとして管理するのがやりやすいと思います。

言葉で言われてもよくわからないと思うので、以下にコード例を示します。MyListAdapter.ViewHolderMyListAdapter.getView() を次のように変更すれば、うまく動くと思います。

java

1 static class ViewHolder { 2 private final EditText editText; 3 private ListItem item; 4 5 private TextWatcher textWatcher = new TextWatcher() { 6 @Override 7 public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { 8 } 9 10 @Override 11 public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { 12 } 13 14 @Override 15 public void afterTextChanged(Editable editable) { 16 if (item != null) { 17 item.setValue(editable.toString()); 18 } 19 } 20 }; 21 22 ViewHolder(EditText editText) { 23 this.editText = editText; 24 editText.addTextChangedListener(textWatcher); 25 } 26 27 void setListItem(ListItem item) { 28 this.item = item; 29 editText.setText(item.getValue()); 30 } 31 }

java

1 public View getView(final int position, View convertView, final ViewGroup parent) { 2 Activity activity = (Activity) this.context; 3 final ListItem item = (ListItem) this.getItem(position); 4 final MyListAdapter.ViewHolder holder; 5 6 if (convertView == null) { 7 convertView = activity.getLayoutInflater().inflate(this.resource, null); 8 holder = new MyListAdapter.ViewHolder(convertView.findViewById(R.id.editText)); 9 convertView.setTag(holder); 10 } else { 11 holder = (MyListAdapter.ViewHolder) convertView.getTag(); 12 } 13 14 holder.setListItem(item); 15 16 return convertView; 17 }

変更ポイントは次の通りです。

  • TextWatcher の実装を ViewHolder の内部に移動しました。getView() が呼び出されるたびに新しい TextWathcer を作成して EditText に設定するのではなく、ViewHolder の作成時に一回だけ TextWatcher を作成して EditText に設定するようにしています
  • ListItemViewHolder.item で保持するようにしました。TextWatcher が更新対象としている ListItem を、ViewHolder.item で保持しています

どうでしょうか。不明な点があれば追加で説明しますので、コメントいただければと思います。

投稿2018/12/03 19:42

kafumi

総合スコア87

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

inumi

2018/12/04 00:57

お示し頂いたコードで実験したところ、意図した動作になりました。 私の環境では以下の行でキャストが必要でした。 holder = new MyListAdapter.ViewHolder(convertView.findViewById(R.id.editText));                        ↓ holder = new MyListAdapter.ViewHolder((EditText)convertView.findViewById(R.id.editText)); ViewHolder側にTextWatcherを保持すること、Viewを再利用していることを意識することの重要性が大変よく分かりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問