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

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

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

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

Q&A

解決済

1回答

6800閲覧

searchviewを用いたListviewの検索方法について

MyDino

総合スコア7

Android Studio

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

0グッド

0クリップ

投稿2018/04/23 12:35

初めて質問させていただきます。よろしくお願いいたします。

前提・実現したいこと

1行に6個のテキストを持つListViewに検索機能を追加したいです。
6個のテキストそれぞれに対して部分一致検索を行い、一つでも部分一致するものがあれば
その行を表示したいと思っています。

発生している問題・エラーメッセージ

1行に1個のテキストを含むListViewの検索機能について、調べたところ多くの回答がありました。
しかし、複数のテキストを含む場合どうすればいいのか分かりません。

参考までに以下が私の見解です

ほかの回答では
list.setFilterText(~~);
のメソッドで文字列を渡しているように感じました。
なのでsetFilterTextをオーバーライドし、6個のテキストを同時に検索にかけれるのではないかと感じました。しかし実装の仕方も戻り値も全く分かりませんしどうすればいいか分からないです。

完全に独学で勉強していたので、知識の前提からいろいろ間違っているかもしれないのでご指摘いただけると嬉しいです。よろしくお願いいたします。

<補足>
・ArrayAdapterを継承したCustomArrayAdapterを使用しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

最近はもっぱらRecyclerViewを使っていてListViewはしばらく触っていないのですが、どちらでも使える方法を回答します。setFilterTextを使うのも一つの方法ではありますが、こちらの方が一般的で応用が効くと思います。(例えば、アイテムを後から追加・削除したいときなど)

手順としては、

  • 元のデータにフィルターをかけたリストを用意する
  • ArrayAdapterにリストを渡して、更新する

これだけです。
実装例としては以下のような感じになります。

java

1// Adapterの作成時 2// 6個のテキストを持つデータのリスト 3List<String[]> items = ... 4CustomArrayAdapter adapter = ... 5 6... 7 8// 検索ボタン押下時の処理 9@Override 10public boolean onSubmitQuery(String query) { 11 // 元のデータにフィルターをかけたリストを作成 12 final List<String[]> filteredItems = new ArrayList<String[]>(); 13 for (String[] item: items) { 14 for (String text: item) { 15 if (text.contains(query)) { // テキストがqueryを含めば検索にHITさせる 16 filteredItems.add(item); 17 } 18 } 19 } 20 21 // adapterの更新処理 22 adapter.clear(); 23 adapter.addAll(filteredItems); 24 adapter.notifyDataSetChanged(); 25}

投稿2018/04/25 07:01

編集2018/04/29 03:38
kakajika

総合スコア3131

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

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

MyDino

2018/04/28 14:08

回答有り難うございます。返信遅れて申し訳ございません。 多少の不明点がございました。以下が不明部分のコードです public boolean onQueryTextSubmit(String query) { // 元のデータにフィルターをかけたリストを作成 final List<String[]> filteredItems = new ArrayList<String[]>(); //すべてのリストビューの要素(繰り返し)、リストビュー内のすべてのテキスト(繰り返し) for (int i = 0;i < partyList.size();i++) { for (int t = 0; t<= 5; t++) { if (partyList[i].contains(query)) { // テキストがqueryを含めば検索にHITさせる filteredItems.add(partyList[i]); } } } // adapterの更新処理 partyAdapter.clear(); partyAdapter.addAll(filteredItems); partyAdapter.notifyDataSetChanged(); } 上記のように書き換えましたがエラーが出ます。kakajika様のご回答を自分なりに噛み砕き 「繰り返しの2回は[①リストのすべての要素について②その要素内の6個のTextViewについて]」と解釈し、Kotolinのループについても調べ上記のように記述しましたが、どうしてもエラーが出てしまいます。 エラー内容は入れ子ループ内のifとaddについて引数の型が間違っていると出ました。 しかし、私自身kakajika様のおっしゃっている内容の解釈が間違っていると根本的な解決につながらないため質問させていただきました。 上記の解釈であっていたのかどうかご回答いただければ幸いです。長文になってしまい申し訳ありませんがよろしくお願いいたします。
kakajika

2018/04/29 03:43

すみません、for-eachループの部分がKotlin形式になってしまっていましたね。。Javaの書き方になるように回答を修正しました。 なお、ご質問には特にコードが含まれていませんでしたので List<String[]> などの型は私の推測で書いたものです。お手元のコードに合わせて適当に変更してください。
MyDino

2018/04/29 09:30

早速のご回答ありがとうございます。コードを書いていなかった私のミスでしたが、以上のご回答が実現できない理由がいくつか出てきました。 ①「foreach not applicable to type」というエラーが入れ子ループの条件内で発生したこと ②adapterのaddAllメソッドが見つからないこと です。1つ目についてはList<Party>という自分で作ったPartyという型で宣言したため、for文に型が合わなくなったと考えられます。2つ目は調べたのですが原因が分かりません。 以下、コードを載せておきます。改善点等ありましたらご助言いただけると嬉しいです。 よろしくお願いいたします。 public class OthersListActivity extends AppCompatActivity implements SimpleCursorAdapter.ViewBinder,SearchView.OnQueryTextListener{ ValueEventListener partyListener; private TextView tv; private ListView othersListView; private PartyAdapter partyAdapter; SearchView mSearchView; List<Party> partyList; //Firebaseコンポーネントを宣言 private FirebaseDatabase mDatabase; private DatabaseReference ptRef; private ChildEventListener mChildEventListener; @Override public boolean setViewValue(View view, Cursor cursor, int i) { // XMLで定義したアニメーションを読み込む Animation anim = AnimationUtils.loadAnimation(this, R.anim.listview_motion_item); // リストアイテムのアニメーションを開始 view.startAnimation(anim); return false; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_others_list); mDatabase =FirebaseDatabase.getInstance(); ptRef = mDatabase.getReference().child("parties"); othersListView =(ListView)findViewById( R.id.others_listview) ; //アダプターを設定 partyList = new ArrayList<>(); partyAdapter = new PartyAdapter(this,R.layout.row,partyList); othersListView.setAdapter(partyAdapter); othersListView.setTextFilterEnabled(true); mSearchView = (SearchView)findViewById(R.id.others_searchview); mSearchView.setOnQueryTextListener(this); mSearchView.setSubmitButtonEnabled(true); mChildEventListener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { //追加データ込みのデータベースの状態を取得し、アダプターに追加 Party partyTitle = dataSnapshot.getValue(Party.class); partyAdapter.add(partyTitle); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }; ptRef.addChildEventListener(mChildEventListener); //リストビューにリスナーを設定 othersListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //クリックしたパーティの詳細のページに遷移 //パーティのプッシュキーを取得 Party mKey = (Party)parent.getItemAtPosition(position); String key = mKey.getKey(); //keyを遷移先アクティビティに渡し、ページ遷移する Intent i = new Intent(getApplicationContext(), OthersPartyActivity.class); i.putExtra("others_party_title_key", key); startActivity(i); //取得したキーからNoSQLで一致するパーティを探す } }); } public boolean onQueryTextChange(String newText) { if (TextUtils.isEmpty(newText)) { othersListView.clearTextFilter(); } else { othersListView.setFilterText(newText.toString()); } return true; } public boolean onQueryTextSubmit(String query) { // 元のデータにフィルターをかけたリストを作成 final List<String[]> filteredItems = new ArrayList<String[]>(); //すべてのリストビューの要素(繰り返し)、リストビュー内のすべてのテキスト(繰り返し) for (Party item : partyList) { for (Party text : item) { if (partyList.contains(query)) { // テキストがqueryを含めば検索にHITさせる filteredItems.add(item); } } } // adapterの更新処理 partyAdapter.clear(); partyAdapter.addAll(filteredItems); partyAdapter.notifyDataSetChanged(); } }
MyDino

2018/04/29 12:11

入れ子ループを次のように書き換え、無事に検索機能を実装することが出来ました。 public boolean onQueryTextSubmit(String query) { // 元のデータにフィルターをかけたリストを作成 final List<Party> filteredItems = new ArrayList<Party>(); //すべてのリストビューの要素(繰り返し)、リストビュー内のすべてのテキスト(繰り返し) for (Party item : partyList) { for (int i =1;i <=6; i++) { try { method = item.getClass().getMethod("getName" + i); text = method.invoke(item).toString(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } if (text.contains(query)) { // テキストがqueryを含めば検索にHITさせる filteredItems.add(item); } } } // adapterの更新処理 partyAdapter.clear(); partyAdapter.addAll(filteredItems); partyAdapter.notifyDataSetChanged(); return true; } kakajikaさんの助力がなければ完成できませんでした。ご丁寧な回答ありがとうございました。
kakajika

2018/04/29 15:25

いえいえ、解決されたならよかったです。 余談ですが、アイテムの形式がコメントにあるようにPartyという独自クラスであるなら、その中に検索マッチ用のメソッドを用意してしまうとよいと思います。リフレクションを使う必要もなくなるはずです。 // 実装イメージ class Party { ... public boolean matchEitherName(String query) { return getName1().contains(query) || getName2().contains(query) || getName3().contains(query) || ...; } }
MyDino

2018/04/30 11:18

アプリ開発とは実に多くの実装方法があるのですね。まだまだ素人ですがこれからも続けていきたいと思います。ためになるご教授ありがとうございました。また機会がありましたらよろしくお願いいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問