Material Components(CardView/FlexboxLayout/TextinputLayout/Chips) (なろうリーダー(仮) 検索画面)

# やりたいこと
- 【大目標】 小説家になろうの小説ダウンロードAndroidアプリの作成。
- 【この記事のゴール】小説検索画面の実装

# 小説検索画面の設計
## 要件
- なろう公式(PC版)とほぼ同等の検索機能を有すること。
- モバイルでの使い勝手を考慮し、以下の実装方針とする。
  - 検索キーワードを直接入力(EditText)および選択肢から選択できること
  - 選択肢からジャンルを選択できること。
  - 読了時間などのフィルタリング条件はスピナーによる選択のみ対応すること
- できるだけMaterial DesignのUIを利用して実装する。具体的には以下のUIを利用してみる。
  - [TextinputLayout](https://material.io/develop/android/components/text-input-layout/)
  - [FlexboxLayout](https://github.com/google/flexbox-layout/blob/master/README.md)
  - [CardView](https://material.io/develop/android/components/material-card-view/)
  - [Chips](https://material.io/design/components/chips.html)
 

## 小説検索画面の実装

## 小説検索画面
- 検索ワードの入力、およびジャンル、詳細設定などを選択するUIを有する。

### TextInputLayout / TextInputEditText
- キーワードを入力する際に、`EditText`ではなく`TextInputLayout`および`TextInputEditText`で実装する。
- `TextInputLayout`は本文(Input text)に加えてラベル(Label text)や補助テキスト(Helper text)などを指定できるマテリアルデザインの部品(詳細は[Material I/O](https://material.io/develop/android/components/text-input-layout/)参照)

![Text.png](https://qiita-image-store.s3.amazonaws.com/0/306047/e1010a6d-1c4a-b944-9edc-5119bda7f79a.png)

``` SearchFragment.xml
<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/text_input_search"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:helperText="空白で区切って複数のキーワードを指定できます"
    app:helperTextEnabled="true"
    android:hint="検索ワード"
    app:hintEnabled="true">
    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/editText_search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout>
```
- 公式だと、`android:hint`は`TextInputEditText`側に付与することになっているが、Android 8(Oreo)の不具合に起因して`NullPointerException`が発生するケースがあるため、`TextInputLayout`側に`android:hint`を移動しています。

### CardView
- 各検索条件を入力する際には、CardVieを利用したUIを構築する。
- 常に全項目を表示するのではなく、タップすることで表示状態を変更できるようにする。
- さくらさんの[伸び縮みするCardViewを作成する（cachapa/ExpandableLayout + RecyclerView + CardViewのサンプル）](http://sakura-bird1.hatenablog.com/entry/2017/04/26/003155)を参考にさせて頂き、入力用レイアウトを作成する。

``` 伸び縮みするCardView.xml

    <androidx.cardview.widget.CardView>
        <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/layout_search">
            <TextView android:text="検索ワードを指定する" /> <!-- タイトル -->
            <net.cachapa.expandablelayout.ExpandableLayout
             android:id="@+id/expandable_layout_search_subtitle"
             app:el_expanded="true"> <!-- サブタイトル 領域: デフォルト表示あり -->
                <TextView android:id="@+id/expand_search_subtitle"/> <!-- サブタイトル -->
            </net.cachapa.expandablelayout.ExpandableLayout>
            <ImageView app:srcCompat="@drawable/expand_arrow"/> <!-- 伸び縮みアイコン -->
            <net.cachapa.expandablelayout.ExpandableLayout
             android:id="@+id/expandable_layout_contents"
             app:el_expanded="false"> <!-- コンテンツ領域: デフォルト表示なし -->
                 <!-- 表示対象コンテンツをここに設定 -->
            </net.cachapa.expandablelayout.ExpandableLayout>
        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>
```
- タイトル領域 = 常に表示
- サブタイトル領域 = 縮小表示時のみ表示
- コンテンツ領域 = 拡大表示時のみ表示

レイアウトがクリックされると、サブタイトル領域とコンテンツ領域の表示を切り替える形です。

```SearchFragment.java
    @BindView(R.id.expandable_layout_search_subtitle) ExpandableLayout layoutSearchSubtitle;
    @BindView(R.id.expandable_layout_contents) ExpandableLayout layoutSearchContents;

    @OnClick(R.id.layout_expand_search)
    void onClickSearchArea(View view) {
        layoutSearchSubtitle.toggle(true);
        layoutSearchContents.toggle(true);
    }
```

### 出来たもの
![device-2018-11-23-231626.png](https://qiita-image-store.s3.amazonaws.com/0/306047/fd9cac1d-80de-2d17-d531-1462c5fa8312.png)　　![device-2018-11-24-001912.png](https://qiita-image-store.s3.amazonaws.com/0/306047/31151001-a40c-f749-8ae6-f943c36463d8.png)


## キーワード選択ダイアログ
- 小説検索画面からキーワード指定ボタン押下で、キーワード選択ダイアログを表示する。
- なろう公式キーワード、なろうおすすめキーワードに対応する。

### Chip
- 画面上でキーワードを選択する際に、キーワードのUIをCheckBoxではなくChipで実装する。
- Chipは以下の特徴をもつマテリアルデザインの部品(詳細は[Material I/O](https://material.io/design/components/chips.html#anatomy)参照)
  - アイコン、テキスト、クローズボタンで構成される角丸ボタン。
  - アイコン、クローズボタンは任意
  - トグル操作で選択状態の切り替えも可能
  - グループを作る場合、ラジオボタン的な使い方となる(グループ内で選択されるのは一つのみ)

![IMG_20181122_073947.jpg](https://qiita-image-store.s3.amazonaws.com/0/306047/d4a8982c-8cd2-e9ff-6e83-d1bc4a49beed.jpeg)

``` chip_sample.xml
<com.google.android.material.chip.Chip
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"/>
```

### FlexboxLayout
- 画面上のキーワードは横画面利用時などを考慮し、LinearLayoutではなく、FlexboxLayoutで実装する(自動で折り返しさせるため)
- FlexboxLayoutはCSS Flexible Box Layoutと似たレイアウトで、Google提供のlibraryとなる。
- コンテンツ幅に応じて自動的に折り返すなどフレキシブルなレイアウト構成が利用可能。
- 以下のケースで利用できる(詳細は[github](https://github.com/google/flexbox-layout/blob/master/README.md)参照)
  - XML上でLinearLayoutやRelativeLayoutの代わりに利用(どちらかと言えばLinearLayout寄り)
  - RecyclerViewで利用

``` build.gradle
dependencies {
    implementation 'com.google.android:flexbox:1.1.0'
}
```

### 出来たもの

![device-2018-11-21-021719.png](https://qiita-image-store.s3.amazonaws.com/0/306047/1e44cc7b-3a86-1089-99ff-7ab77fa56e11.png)


``` RecommendKeywords.xml
<com.google.android.flexbox.FlexboxLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:flexWrap="wrap"
    app:justifyContent="space_around">
    <com.google.android.material.chip.Chip
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/check_keyword_gag"
    android:text="ギャグ" />
    <!-- 以下チップが並列で並ぶ -->
</com.google.android.flexbox.FlexboxLayout>
```

こんな感じになりました。

- `FlexboxLayout`において、「flexWrap="wrap"」「justifyContent="space_around"」を設定する事で、レイアウトが一行に収まらなかったら改行し、行内のコンテンツ間のスペースを均等割当てしています。

## まとめ
- Material Componentsには便利な部品がたくさんそろっていました。
- どの部品をどのようなケースで使えば良いのかを十分に理解しておく必要がありそうです。