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

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

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

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

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

1571閲覧

EditTextのOnEditorActionListener、OnFocusChangeListenerでの入力値チェックについて

退会済みユーザー

退会済みユーザー

総合スコア0

Android

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

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2022/11/03 04:13

前提

https://teratail.com/questions/j24b454qcc9g38
の続きになりますが、質問の観点が違うので質問を新たに作成しました。
同様に、Excelのような表形式のイメージで、EditTextに値を入力し、DB(Room)を更新するようなアプリを作っています。
ここで、業務用券的に、EditTextの入力値に対してチェックが必要になります。例えば数値の範囲が5〜10の範囲であるとか。ある数値以下であること、とか。

EditTextのハンドラがふたつあり、
OnEditorActionListener(キーパッドのIME_ACTION_NEXT、IME_ACTION_DONE等)
OnFocusChangeListener(他のViewにフォーカスが移る場合)

OnEditorActionListenerは戻り値があり、入力値チェックが通らない場合falseを返せば、そのEditTextにフォーカスを留まらせることができます。
それに対し、OnFocusChangeListenerは戻り値がありません。入力値チェックが通っても、通らなくても、フォーカスは他のViewに移ってしまいます。

こういう場合、どうやっているのか、検索してみましたが、OnFocusChangeListenerは引数にフォーカスが当たった(true)、外れた(false)が渡ってくるので、フォーカスが当たった時に前の値を保持して、フォーカスが外れた時に入力値チェックして不正であれば前の値に戻す、というやり方をしているのが多いようです。

kotlin

1 var sv = "" 2 editText.setOnFocusChangeListener { _, hasFocus -> 3 if (hasFocus) { 4 // フォーカスが当たった 5 sv = editText.text.toString() 6 } else { 7 // フォーカスが外れた 8 if (isValid(editText.text.toString())) { 9 // DB更新 10 } else { 11 // TODO 元の値に戻していやるしかない 12 editText.setText(sv) 13 } 14 } 15 }

これでも、一応要件的には満たせますが、UI的にはイマイチです。結局、フォーカスは他のViewに移ってしまうし、警告メッセージを出したところで、ユーザをそれを見逃してしまうかもしれません。(フォーカスが移った時に、元の値に戻されえているということに気が付かない)

実現したいこと

OnEditorActionListenerのように、入力値チェックが不正だった場合、フォーカスを移動せずに、同じEditTextに留まらせるということをやりたい(入力値チェックが通るまで、他に移動するな!!)んですが、なにか方策はあるんのでしょうか?

フォーカスはRecyclerViewの外のView(例えば、送信ボタン、戻るボタン)に移るのはいいけど、RecyclerViewの他の行のEditTextには移れないようにするとかはできるんでしょうか?

補足情報(FW/ツールのバージョンなど)

AndroidStudio Dolphine 2021.3.1 patch1
Kotlin 1.7.20

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

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

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

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

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

jimbe

2022/11/06 05:00

>フォーカスはRecyclerViewの外のView(例えば、送信ボタン、戻るボタン)に移るのはいいけど 通常モードでは Button にフォーカスは移動しません。
guest

回答1

0

ベストアンサー

UI そのものの動作の再検討も含めて入力検査・再入力の方法はいろいろで、これしか無いというのは無さそうに思います。
フォーカスが移動してしまうのがダメならフォーカスも戻すとか、逆に移動してもどこが問題なのかがはっきり分かるようにどうにかするというのも方法でしょう。


「TextWatcher 等でバリデートチェックして EditText#setError でエラーを表示するようにしフォーカスは自由に動かせる」というイメージで仕立ててみました。
DB の更新は考慮していませんが、エラーがあったら SEND ボタンを disable にしています。

MainActivity.java

java

1import androidx.annotation.NonNull; 2import androidx.appcompat.app.AppCompatActivity; 3import androidx.recyclerview.widget.RecyclerView; 4 5import android.os.*; 6import android.text.*; 7import android.util.Log; 8import android.view.*; 9import android.widget.*; 10 11import java.util.*; 12 13public class MainActivity extends AppCompatActivity { 14 @Override 15 protected void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_main); 18 19 Adapter adapter = new Adapter(); 20 RecyclerView recyclerView = findViewById(R.id.recyclerView); 21 recyclerView.setAdapter(adapter); 22 23 Button cancelButton = findViewById(R.id.cancelButton); 24 cancelButton.setOnClickListener(v -> { 25 Log.d("MainActivity **", "cancel."); 26 }); 27 28 Button sendButton = findViewById(R.id.sendButton); 29 sendButton.setOnClickListener(v -> { 30 Log.d("MainActivity **", "send."); 31 }); 32 sendButton.setEnabled(false); 33 34 adapter.setEventListener(a -> { 35 sendButton.setEnabled(a.getErrorCount() == 0); 36 }); 37 } 38 39 private static class Item { 40 final long id; 41 final String text; 42 Item(long id, String text) { 43 this.id = id; 44 this.text = text; 45 } 46 @Override 47 public String toString() { 48 return super.toString() + "[id=" + id + ", text='" + text + "']"; 49 } 50 } 51 52 private static class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> { 53 interface AdapterListener extends EventListener { 54 void changeState(Adapter adapter); 55 } 56 57 private static class ViewHolder extends RecyclerView.ViewHolder { 58 final EditText editText; 59 public ViewHolder(@NonNull View itemView) { 60 super(itemView); 61 editText = itemView.findViewById(R.id.editText); 62 } 63 } 64 65 private List<Item> itemList = new ArrayList<>(); 66 private Set<Long> errorSet = new HashSet<>(); 67 private AdapterListener listener; 68 69 Adapter() { 70 //テストデータ 71 itemList.add(new Item(1, "ABC")); 72 itemList.add(new Item(2, "DEF")); 73 itemList.add(new Item(3, "GHI")); 74 itemList.add(new Item(4, "JKL")); 75 } 76 77 void setEventListener(AdapterListener listener) { 78 this.listener = listener; 79 if(listener != null) listener.changeState(this); //初期表示用 80 } 81 82 int getErrorCount() { 83 return errorSet.size(); 84 } 85 86 @NonNull 87 @Override 88 public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 89 return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false)); 90 } 91 92 private class EditValidator implements TextWatcher { 93 private EditText editText; 94 private Item item; 95 96 EditValidator(EditText editText, Item item) { 97 this.editText = editText; 98 this.item = item; 99 } 100 101 private boolean isValid(String text) { 102 return text.length() < 5; //テスト用でテキトウ 103 } 104 105 @Override 106 public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 107 @Override 108 public void onTextChanged(CharSequence s, int start, int before, int count) {} 109 @Override 110 public void afterTextChanged(Editable s) { 111 boolean changeState; 112 if(isValid(s.toString())) { 113 editText.setError(null); 114 changeState = errorSet.remove(item.id); 115 } else { 116 editText.setError("length Error"); 117 changeState = errorSet.add(item.id); 118 } 119 if(changeState && listener != null) listener.changeState(Adapter.this); 120 } 121 } 122 123 @Override 124 public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 125 Item item = itemList.get(position); 126 holder.editText.setText(item.text); 127 128 TextWatcher validator = (TextWatcher)holder.editText.getTag(); 129 if(validator != null) holder.editText.removeTextChangedListener(validator); 130 validator = new EditValidator(holder.editText, item); 131 holder.editText.setTag(validator); 132 holder.editText.addTextChangedListener(validator); 133 } 134 135 @Override 136 public int getItemCount() { 137 return itemList.size(); 138 } 139 } 140}

res/layout/activity_main.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 xmlns:app="http://schemas.android.com/apk/res-auto" 6 android:layout_width="match_parent" 7 android:layout_height="match_parent" 8 tools:context=".MainActivity"> 9 10 <androidx.recyclerview.widget.RecyclerView 11 android:id="@+id/recyclerView" 12 android:layout_width="0dp" 13 android:layout_height="0dp" 14 app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" 15 app:layout_constraintBottom_toTopOf="@id/topOfButtons" 16 app:layout_constraintEnd_toEndOf="parent" 17 app:layout_constraintStart_toStartOf="parent" 18 app:layout_constraintTop_toTopOf="parent" /> 19 <androidx.constraintlayout.widget.Barrier 20 android:id="@+id/topOfButtons" 21 android:layout_width="0dp" 22 android:layout_height="0dp" 23 app:barrierDirection="top" 24 app:constraint_referenced_ids="cancelButton,sendButton" /> 25 <Button 26 android:id="@+id/cancelButton" 27 android:layout_width="wrap_content" 28 android:layout_height="wrap_content" 29 android:text="CANCEL" 30 app:layout_constraintBottom_toBottomOf="parent" 31 app:layout_constraintEnd_toStartOf="@id/sendButton" 32 app:layout_constraintStart_toStartOf="parent" /> 33 <Button 34 android:id="@+id/sendButton" 35 android:layout_width="wrap_content" 36 android:layout_height="wrap_content" 37 android:text="SEND" 38 app:layout_constraintBottom_toBottomOf="parent" 39 app:layout_constraintEnd_toEndOf="parent" 40 app:layout_constraintStart_toEndOf="@id/cancelButton" /> 41</androidx.constraintlayout.widget.ConstraintLayout>

res/layout/item.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<EditText 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:id="@+id/editText" 5 android:layout_width="match_parent" 6 android:layout_height="wrap_content" />

スクリーンショット:エラー無し
スクリーンショット:エラー有り

投稿2022/11/03 06:56

編集2022/11/06 12:41
jimbe

総合スコア12634

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

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

退会済みユーザー

退会済みユーザー

2022/11/03 10:56 編集

> フォーカスが移動してしまうのがダメならフォーカスも戻すとか 無理やりそのEditTextにフォーカスを当ててやるってことですかね? OnEditorActionListener#onFocusChangeの引数にViewが渡ってくるので、 var sv = "" editText.setOnFocusChangeListener { view, hasFocus -> if (hasFocus) { // フォーカスが当たった sv = editText.text.toString() } else { // フォーカスが外れた if (isValid(editText.text.toString())) { // DB更新 } else { // 無理やりフォーカスを当ててやる view.requestFocus() } } } もやってみましたが、期待通りにはなりませんでした。フォーカスが元のEditTextと移動先のEditTextの両方に当たっているような変な動きをして全然ダメでした。
jimbe

2022/11/03 18:10

EditView が RecyclerView に入っていますので、そちらの行の選択/フォーカスの管理と齟齬が出ているのではないでしょうか。 一旦 RecyclerView を外して、例えば EditView を2つ3つ LinerLayout でそれっぽく並べて、フォーカス移動等によるバリデートのテストをしてみては如何でしょう。それでうまくいったら、 RecyclerView に戻してどうなるかを見れば、うまくいなかったら RecyclerView との兼ね合いと判断してその部分の調査に注力できると思います。
jimbe

2022/11/06 04:57

340347 は OnEditorActionListener で requestFocus なので、 OnFocusChangeListener ではフォーカスの取り合いとかでうまく行かない場合があるのかもしれません。 requestFocus を MainLooper の Handler に post する形で行えば大丈夫かも・・・。 また、全体の構造として「問題がある EditText からフォーカスを動かさない」のではなく、TextWatcher 等でバリデートチェックして EditText#setError でエラーを表示するようにしフォーカスは自由に動かせるようにするというのは如何でしょうか。 送信等エラーがあるとダメな機能は、エラー数を管理してエラーがあったら使用不可とかで対応できると思います。
退会済みユーザー

退会済みユーザー

2022/11/06 06:35

> また、全体の構造として「問題がある EditText からフォーカスを動かさない」のではなく、TextWatcher 等でバリデートチェックして EditText#setError でエラーを表示するようにしフォーカスは自由に動かせるようにするというのは如何でしょうか。 なるほど、それもひとつの手ではありますね。 このAndroidアプリはAndroid内にDB(roomでSQLite)をもっていて、observerパターンです。 この方法だと、OnFocusChangeListenerでフォーカスが外れたときでチェック不正でもDBは更新されてしまうのが、ちょっと気持ち的に引っかかるところですが・・・ このアプリは既存のシステムのフロントエンドで、既存のシステムにDBの内容をCSV(今時?と思われると思いますが、既存のシステムのI/Fがソレなので・・・)で、送信することによって成り立ちます。 つまり、OnFocusChangeListenerでフォーカスが外れたとき、チェック不正で、そのままDB更新されても、最終的に既存のシステへの「送信」ボタンの時にチェックできればいいと考えます。 とりあえずは、今の通りで、ユーザからオペレーション的に不満が出てくれば考えることとします。
jimbe

2022/11/06 09:00

チェック不正だったら DB を更新しなければよいだけのような気がしますけれども。エラーがある状態でアプリが終わっても再実行したらエラー状態まで再現する必要があるのであれば、更新は必要ですが。 room のオブジェクトをそのまま recyclerView の要素としてしまっているためにエラーデータを保持してしまうとDBにも反映されてしまってちょっと・・・ということでしたら、 room と recyclerView の要素を分けては如何でしょう。なんなら room は使わずに SQL で書き込むようにしても良いでしょうし。 送信するのなら、SQLite の DB 丸ごとというのは意味無いでしょうから、 CSV でというのは妥当に思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問