やりたいこと
- 【大目標】 小説家になろうの小説ダウンロードAndroidアプリの作成。
- 【この記事のゴール】小説検索画面の実装
小説検索画面の設計
要件
- なろう公式(PC版)とほぼ同等の検索機能を有すること。
- モバイルでの使い勝手を考慮し、以下の実装方針とする。
- 検索キーワードを直接入力(EditText)および選択肢から選択できること
- 選択肢からジャンルを選択できること。
- 読了時間などのフィルタリング条件はスピナーによる選択のみ対応すること
- できるだけMaterial DesignのUIを利用して実装する。具体的には以下のUIを利用してみる。
小説検索画面の実装
小説検索画面
- 検索ワードの入力、およびジャンル、詳細設定などを選択するUIを有する。
TextInputLayout / TextInputEditText
- キーワードを入力する際に、
EditText
ではなくTextInputLayout
およびTextInputEditText
で実装する。 -
TextInputLayout
は本文(Input text)に加えてラベル(Label text)や補助テキスト(Helper text)などを指定できるマテリアルデザインの部品(詳細はMaterial I/O参照)
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のサンプル)を参考にさせて頂き、入力用レイアウトを作成する。
伸び縮みする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);
}
出来たもの
キーワード選択ダイアログ
- 小説検索画面からキーワード指定ボタン押下で、キーワード選択ダイアログを表示する。
- なろう公式キーワード、なろうおすすめキーワードに対応する。
Chip
- 画面上でキーワードを選択する際に、キーワードのUIをCheckBoxではなくChipで実装する。
- Chipは以下の特徴をもつマテリアルデザインの部品(詳細はMaterial I/O参照)
- アイコン、テキスト、クローズボタンで構成される角丸ボタン。
- アイコン、クローズボタンは任意
- トグル操作で選択状態の切り替えも可能
- グループを作る場合、ラジオボタン的な使い方となる(グループ内で選択されるのは一つのみ)
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参照)
- XML上でLinearLayoutやRelativeLayoutの代わりに利用(どちらかと言えばLinearLayout寄り)
- RecyclerViewで利用
build.gradle
dependencies {
implementation 'com.google.android:flexbox:1.1.0'
}
出来たもの
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には便利な部品がたくさんそろっていました。
- どの部品をどのようなケースで使えば良いのかを十分に理解しておく必要がありそうです。