開発を便利にするライブラリ
Java/Androidには、便利で実績の多いライブラリがたくさんあります。本アプリでは、開発を便利にするライブラリとして下記を導入します。
- lombok
- Timber
- Lightweight-Stream-API
- RxJava2
- RxAndroid
執筆時点での最新バージョンを指定したbuild.gradleは以下のようになります。
dependencies {
...略...
compileOnly 'org.projectlombok:lombok:1.16.18'
annotationProcessor 'org.projectlombok:lombok:1.16.18'
compile 'com.jakewharton.timber:timber:4.5.1'
compile 'com.annimon:stream:1.1.9'
compile 'io.reactivex.rxjava2:rxjava:2.1.3'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
...略...
}
簡単に各ライブラリの利便性について記載します。
Lombok
@AllArgsConstructor、@NoArgsConstructor、@Data、@Setter、@Getterなどのアノテーションを付与することでコンストラクタやgetter/setterを記述する手間を省いてくれます。
Timber
フォーマットを扱えるログ出力APIを提供します。また、デバッグビルドのみログを出力したい、リリースビルド時はバグトラッカーに出力したいといった制御を簡単に行うことができるライブラリです。
Lightweight-Stream-API
Optional、Stream API互換API、java.util.functionパッケージ互換インターフェースを提供します。他にも、Java 8標準では存在しないThrowableConsumerインターフェースなどの例外に対応したFunctionalInterfaceであるThrowableXXXシリーズも有用です。
RxJava2
Javaでリアクティブプログラミングを行うためのライブラリです。RxJavaの2.xバージョンではReactive Streamsを実装しています。詳しくはRxJava 2.xで導入されたReactive Streamsもご参照ください。
RxAndroid
RxJava2をAndroidで利用するためにandroid.os.Looperからio.reactivex.Schedulerを生成するAPIと、AndroidのMAINスレッドで動作するSchedulerの取得APIを提供します。
ユーザーリストフラグメントの作成
まずは、チャット相手をリスト表示する画面をFragmentで実装します。この画面の完成図は以下のとおりです。

レイアウト
このFragmentのレイアウトは、下図のようにしています。

「画面上部から引っ張って更新」のUIを実現するためにSwipeRefreshLayoutを配置し、その内部には、ユーザーリストを表示するためのRecyclerViewを配置しています。 最後に配置したAppCompatTextViewは、ユーザーリストが空の場合にメッセージを表示するためのViewです。
リストデータを表示する部分は、ListViewでも同じUIを実現できますが、RecyclerViewにはViewHolderパターンを前提とした扱いやすいAdapterクラス、アニメーションカスタマイズのしやすさ、Material Designを実現するDesign Supportライブラリとの親和性などの利点があり、もはやListViewを選択する理由は限りなく小さくなっていると言えます。
AppCompatTextViewに限らず、サポートライブラリが提供している標準Viewの代替クラスは、OSバージョンの違いを吸収して新機能を利用できる利点があるので、積極的に採用して良いと思います。
これらのView部品を利用するために、以下のライブラリを追加しておきます。
- support-v4
- appcompat-v7
- recyclerview-v7
build.gradleは、こちらです。
dependencies {
...略...
// Android Studioで生成したプロジェクトなら最初から設定されています
implementation 'com.android.support:support-v4:26.1.0'
compile 'com.android.support:appcompat-v7:26.1.0'
compile 'com.android.support:recyclerview-v7:26.1.0'
...略...
}
Viewのバインディング
ユーザーの入力情報を受け取ったり、ユーザーへ情報を表示するためには、Viewからデータを受け取ったり渡したりする必要がありますが、これを実現する方法としては大きく2つの方法が考えられます。
- Viewバインディング:Viewの参照をActivityやFragmentに持たせ、ActivityやFragmentではViewを直接更新する
- Dataバインディング:昨今のjavascript界隈で見られるような、Viewにデータをバインディングし、ActivityやFragmentではデータを更新するといった役割分担を行う
今回は、Viewバインディングを採用します。まずは、参照したいViewへidを設定しておきます。
<FrameLayout ...>
<android.support.v4.widget.SwipeRefreshLayout
...
android:id="@+id/refresh_layout">
<android.support.v7.widget.RecyclerView
...
android:id="@+id/recycler_view" />
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.v7.widget.AppCompatTextView
android:id="@+id/empty_text"
... />
</FrameLayout>
ViewバインディングかDataバインディングか
Androidが提供するData Bindingライブラリを利用すれば、Viewにデータをバインディングする方法を採ることができます。しかし、筆者の個人的な意見ですが、まだData Bindingライブラリがもたらすメリット(ビュー操作の分離によるコード簡略化など)は、そのデメリット(自由度が高く、特にチーム開発ではスパゲティコードを産み出しやすい)を上回っておらず、導入は時期尚早ではないかと思います。
次は、フラグメントクラスにView変数を用意し、onCreateView()メソッドでViewの参照を取得します。この手順は、以前のAndroid開発ではButterKnifeライブラリを使うのが定番の一つでした。しかし、Android 8.0時代(=compileSdkVersionが26以上)に突入した今であれば、Androidの標準的な方法を採用するのが良いと思います。
public class UserListFragment extends Fragment {
private SwipeRefreshLayout mRefreshLayout;
private RecyclerView mRecyclerView;
private AppCompatTextView mEmptyText;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View root = inflater.inflate(R.layout.fragment_user_list, container, false);
// Viewの参照を取得
mRefreshLayout = root.findViewById(R.id.refresh_layout);
mRecyclerView = root.findViewById(R.id.recycler_view);
mEmptyText = root.findViewById(R.id.empty_text);
return root;
}
...
}
Android 8.0では、View#findViewById()メソッドがジェネリクスに対応しました。そのため、下記の例に示すような明示的キャストは、もはや不要です。
mRefreshLayout = (SwipeRefreshLayout) root.findViewById(R.id.refresh_layout);
ButterKnifeを使う場合
compileSdkVersionが更新できない状況や、View変数とバインド対象を識別するidを近い場所に記述したい場合はButterKnifeを使いましょう。
フラグメントのクラスにView変数を用意して、Viewのidを指定した@BindViewアノテーションを付与します。
public class UserListFragment extends Fragment {
@BindView(R.id.refresh_layout)
SwipeRefreshLayout mRefreshLayout;
@BindView(R.id.recycler_view)
RecyclerView mRecyclerView;
@BindView(R.id.empty_text)
AppCompatTextView mEmptyText;
...
}
また、onCreateView()メソッドでViewバインディング、onDestroyView()メソッドでその解除を行います。
public class UserListFragment extends Fragment {
private Unbinder mUnbinder;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View root = inflater.inflate(R.layout.fragment_user_list, container, false);
// Viewバインディング
mUnbinder = ButterKnife.bind(this, root);
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
// Viewバインディング解除
mUnbinder.unbind();
}
...
}
build.gradleを下記のように設定してください。また、ButterKnifeはバージョン8.0.0以降で、ライブラリ自体にProGuardの設定が含まれるようになったため、ButterKnifeのProGuard設定は不要です。
dependencies {
...略...
compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
...略...
}
