LoginSignup
10
8

More than 5 years have passed since last update.

Androidアプリで検索機能実装にExpandableLayoutを使ったらとても使いやすくなった

Posted at

bowyer-appです。
今回新しいアプリ弓道のアプリ 採点簿 for チームの中で、スコアや選手の検索を実装しました。
僕のアプリは、タイトル、人数、日付などいろんな条件で検索する必要がありました。
より操作を簡単にするために極力画面遷移を挟まず、その場で検索したかったので何かいい方法は無いかなと探してたら、
AAkiraさんの作ったExpandableLayoutがちょうど良かったので入れてみました。

UI

通常時 詳細検索時
device-2016-05-15-102732.png device-2016-05-15-101844.png

僕のアプリから一部コードの切り出してこちらに公開してますので、よかったら御覧ください
https://github.com/bowyer-app/PlayerManage

必要なときにカスタムな検索機能が出てくるのでとても使いやすくなりました。
気づいたらどの画面でもExpandableLayoutを使っていました。

DB

DBにはsquareさんのsqlbriteを使いました。
細かいところはsql書きたいけど、それ以外のところを書くのはめんどくさかったので、sqliteをラップしてくれているこちらを選定しました。

今回工夫した点

Daoの実装を工夫してみました。
PlayerDao.java

sqlbriteのサンプルでは、検索条件を書くときにSQLを定義し

ItemsFragment.java
private static final String LIST_QUERY = "SELECT * FROM "
      + TodoItem.TABLE
      + " WHERE "
      + TodoItem.LIST_ID
      + " = ? ORDER BY "
      + TodoItem.COMPLETE
      + " ASC";

@Inject BriteDatabase db;

上記で定義したsqlを元にdbに対してqueryを実行しています。

ItemFragment.java
String listId = String.valueOf(getListId());
subscriptions.add(db.createQuery(TodoItem.TABLE, LIST_QUERY, listId)
        .mapToList(TodoItem.MAPPER)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(adapter));

BriteDatabasecreateQueryを見てみると

BriteDatabase.java
public QueryObservable createQuery(@NonNull final String table, @NonNull String sql,
      @NonNull String... args) {

引数にargsを渡しているので、こことsqlの= ?の部分がズレると検索条件があわなくなり、引数足りないよってエラー出たりします。
僕が作ったPlayerDao.javaでは、sqlargsを関連付けたPlayerQueryというオブジェクトを生成できるようにしました。

PlayerDao.java
public static final class SQLBuilder {
    private String sql = "SELECT * FROM " + Player.TABLE + " WHERE ";
    private final List<String> values = new ArrayList<>();

    private String getAndSql() {
      if (!values.isEmpty()) {
        sql = sql + " and ";
      }
      return sql;
    }

    private String getOrSql() {
      if (!values.isEmpty()) {
        sql = sql + " or ";
      }
      return sql;
    }

    public SQLBuilder name(String name) {
      if (TextUtils.isEmpty(name)) {
        return this;
      }
      //全部マッチさせるため
      sql = getAndSql() + Player.LAST_NAME + " like ? ";
      values.add("%" + name + "%");
      sql = getOrSql() + Player.FIRST_NAME + " like ? ";
      values.add("%" + name + "%");
      sql = getOrSql() + Player.LAST_NAME_PHONETIC + " like ? ";
      values.add("%" + name + "%");
      sql = getOrSql() + Player.FIRST_NAME_PHONETIC + " like ? ";
      values.add("%" + name + "%");
      return this;
    }

    public SQLBuilder sex(Sex sex) {
      if (sex == Sex.ALL) {
        return this;
      }
      sql = getAndSql() + Player.SEX + " = ? ";
      values.add(String.valueOf(sex.getSex()));
      return this;
    }

    public SQLBuilder rank(Rank rank) {
      if (rank == Rank.ALL) {
        return this;
      }
      sql = getAndSql() + Player.RANK + " = ? ";
      values.add(rank.getRank());
      return this;
    }

    public SQLBuilder id(long playerId) {
      sql = getAndSql() + Player.ID + " = ? ";
      values.add(String.valueOf(playerId));
      return this;
    }

    public PlayerQuery build() {
      if (values.isEmpty()) {
        sql = sql.replace("WHERE", "");
      }
      return new PlayerQuery(sql, values.toArray(new String[values.size()]));
    }
  }

public static class PlayerQuery {
    public final String sql;
    public final String[] values;

    public PlayerQuery(String sql, String[] values) {
      this.sql = sql + " ORDER BY " + Player.ID + " DESC, " + Player.LAST_NAME + " ASC";
      this.values = values;
    }
  }

Builderパターンを使ってsqlのカラムとargsの値を関連付けて組み立てているので、
足りないとかズレるということが発生しません。

使い方はこちら、MainActivity.java#L182-L192

検索条件が変わる度にPlayerDao.PlayerQueryを生成してPlayerDaoに食わせるだけ。

MainActivity.java
private void doSearch() {
    String name = mSearchQueryText.getText().toString();
    Sex sex = getCheckedSex(mSexGroup.getCheckedRadioButtonId());
    Rank rank = Rank.of(mRankText.getText().toString());
    PlayerDao.PlayerQuery playerQuery =
        new PlayerDao.SQLBuilder().name(name).sex(sex).rank(rank).build();
    mSubscription = (PlayerDao.getPlayerByQuery(mDb, playerQuery)
        .mapToList(Player.MAPPER)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(mAdapter));
  }

sql書くのすごくめんどくさいし、条件が複雑になっていくと検索句のカラム名と値がズレてよくわからなくなるので、こういった工夫が役に立つのではと思います。

https://github.com/bowyer-app/PlayerManage
こちらにソースコードを公開してあるのでよかったら見てください。
もっとこうしたほうが良いよってのがあればPR頂けると嬉しいです。

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8