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

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

ただいまの
回答率

88.37%

[Android Studio]チェックボックス付ListviewでSearchviewによるフィルターがかからない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,114

68kjo_pzdr

score 8

タイトルの通りです。listviewに並んだ要素それぞれにチェックボックス(複数選択可)があり、チェックを入れたものの配列を保存する、というものに、searchviewによる検索機能を足そうとしてみました。(検索を前方一致以外にするにはadapterのgetFilterとかをいじる、と見たので、そうしました。)その結果、チェックを一つも打っていない状態なら検索が正常に動作するのに、1つでもチェックが入っている状態からは検索ワードを打ち込んでも反応しない、という状況になってしまいました。

public class SelectActivity extends AppCompatActivity {
    private static ArrayList<Card> targetList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_select);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_select);

        Toolbar toolbar = findViewById(R.id.tool_bar);
        setSupportActionBar(toolbar);
        toolbar.setTitle(R.string.choose_cards);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);

      setList();

        searchView = findViewById(R.id.searchView);
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                return false;
            }
            @Override
            public boolean onQueryTextChange(String s) {

                if (TextUtils.isEmpty(s)) {
                    listView.clearTextFilter();
                } else {
                    listView.setFilterText(s.toString());
                }
                return true;
            }
        });

    @Override
    public void onResume(){
        super.onResume();
        checkUpdate();
    }
    @Override
    public void onPause(){
        super.onPause();
        resisterTargetList();
    }

    private void setList(){
        MyArrayAdapter adapter = new MyArrayAdapter(
                    this, android.R.layout.simple_list_item_multiple_choice, new ArrayList<String>(Arrays.asList(MainActivity.cardNames)), targetList);

        listView = findViewById(R.id.listview);
        listView.setAdapter(adapter);
        listView.setTextFilterEnabled(true);
        listView.setDivider(null);
    }

    public static void checkUpdate(){
        for(Card card: targetList){
            int id = Arrays.asList(MainActivity.cardNames).indexOf(card.getName());
            listView.setItemChecked(id, true);
        }
    }

    public static void resisterTargetList(){
        SparseBooleanArray array = listView.getCheckedItemPositions();
        targetList.clear();

        for(int i = 0; i < array.size(); i++){
            int at = array.keyAt(i);
            if(array.get(at)){
                targetList.add(MainActivity.cardList.get(at));
            }
        }
    }
}
   public class MyArrayAdapter extends ArrayAdapter {

    private ArrayList<String> cardNames;
    private ArrayList<Card> targetList;

    private TestFilter _filter;

    public MyArrayAdapter(Context context, int resourceId, ArrayList<String> cardNames, ArrayList<Card> targetList){
        super(context, resourceId, cardNames);
        this.cardNames = (ArrayList<String>)cardNames.clone();
        this.targetList = targetList;
        this.otherList = otherList;
    }

    @Override
    public boolean areAllItemsEnabled(){
        return true;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        View view = super.getView(position, convertView, parent);
        return view;
    }

    @Override
    public Filter getFilter() {
        if(_filter == null) {
            _filter = new TestFilter();
        }
        return _filter;
    }

    public class TestFilter extends Filter {

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {

            SelectActivity.resisterTargetList();

            ArrayList<String> items = new ArrayList<>();
            if (constraint == null || constraint.length() == 0) {
                items = new ArrayList<String>(cardNames);
            } else {
                // getCount及びgetItemはAdapterのメソッド
                for(int i = 0, size = cardNames.size(); i < size; i++) {
                    String data = cardNames.get(i);
                    if(data.contains(constraint)) {
                        items.add(data);
                    }
                }
            }

            FilterResults r = new FilterResults();
            r.count = items.size();
            r.values = items;
            SelectActivity.checkUpdate();
            return r;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, Filter.FilterResults results) {

            // Adapterのメソッドでデータの内容を更新する
            if(results != null && results.count > 0) {

                List<String> items = (List<String>) results.values;
                clear();
                addAll(items);

                notifyDataSetChanged();
            } else {
                notifyDataSetInvalidated();
            }
        }
    }
}

追記
MainActivityにあるcardList(Arraylist<Card>)は、idとnameを持つCardクラスのオブジェクトの集合で、このnameのみのstring配列がcardNamesです。selectActivityから離れるときにチェックを入れていたところのCardオブジェクトの配列をtargetList(Arraylist<Card>)に入れようとしています。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kakajika

    2018/08/08 17:14

    MyArrayAdapterのソースコードも貼っていただいた方が回答がつきやすいかと思います。

    キャンセル

  • 68kjo_pzdr

    2018/08/08 18:06

    ご指摘ありがとうございます。貼り忘れていました。

    キャンセル

  • kakajika

    2018/08/10 16:39

    うーん、色々とツッコミどころがある感じですが、とりあえずMyArrayAdapterのソースを全部載せてください。targetListやcardNamesという変数とその扱いが怪しいですが、今のご質問のソースではそれが何なのか判断するのは厳しいです。

    キャンセル

回答 1

checkベストアンサー

0

Filterクラスの performFiltering メソッドは、メインスレッド以外のスレッド上で呼び出されるので、この中でUIに関する操作をしてはいけません。実際に動かしたことはないので確かなことは言えませんが、UIを操作する順番がおかしくなってしまいフィルターが効いていないように見えるのだと思います。チェックが1つでもされている時にのみ現象が起こるのは、checkUpdate() 内の処理が行われるのがチェックがされている時だからです。

まずはperformFilteringの中で呼んでいる SelectActivity.resisterTargetList(); と SelectActivity.checkUpdate(); を無くす方向で修正してください。publishResultsがフィルターした結果を元にUIを操作するためのメソッドなので、そちらの中に移せば意図する動作になると思います。

余談ですが、外部からアクセスできるようにするために変数やメソッドをstaticで宣言するのはオブジェクト指向の考え方に反するものであり、あまりオススメできません。(いわゆるstaticおじさんというやつです)そのあたりも修正しておいた方が今後バグを起こす可能性を減らせてよいと思います。

補足: staticを使わない方法について

いくらかやり方はありますが、簡単なのはActivityの中でMyArrayAdapterを継承したクラスを作り、そこでFilterを実装するものです。

private void setList(){
    MyArrayAdapter adapter = new MyArrayAdapter(...) {
        private Filter filter = new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                ...
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                // ここならActiviyのメソッドが呼べる
            }
        };

        @NotNull
        @Override
        public Filter getFilter() {
            return filter;
        }
    };

    ...
    listView.setAdapter(adapter);
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/22 20:46

    大変有難うございました!仰る通りpublishResultsの中にUI処理を移したら正常に動きました。
    staticについては、activityクラスのインスタンスは生成出来ないと思うのですが、それ以外で他クラスから参照する方法が思いつきませんでした…。例えばどのように出来るか補足して頂けると大変助かります。

    キャンセル

  • 2018/08/23 00:25

    補足を追記しておきました。

    キャンセル

  • 2018/08/23 07:45

    参考になります!誠にありがとうございました。

    キャンセル

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

  • ただいまの回答率 88.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る