RecyclerViewとRealmを使ったグリッドインタフェースの構築
アンドロイド - 推定所要時間: 25分
このチュートリアルでThorben Primkeは、Realm用に拡張されたRecyclerViewであるRealmRecyclerViewを使ってRealmに格納されたデータをグリッド表示する方法を示します。
アプリケーションにグリッドレイアウトを実装しようとしたことはありませんか?データがRealmに格納されているなら、すぐにこのようなインタフェースを作成することができます。一番の売りは、データを更新するだけでアニメーションと共に自動的に表示が更新されるところです。
それでは始めてみましょう。もし先に完成版を見たい場合は、GitHub上に置いてあります。 ここをクリック
完成すると以下のようなものができあがります。
チュートリアル
ニューヨーク・タイムズのAPIからデータを取得するためにはAPIキーが必要です。ニューヨーク・タイムズ開発者サイトから無料のAPIキーを取得してください。
“Empty Activity”テンプレートを使用して新しいAndroid Studioプロジェクトを作成します。
次に、プロジェクトにRealmSearchViewを追加します。RealmSearchViewはjitpack.ioリポジトリからGradleの依存ライブラリとして利用可能です。
リポジトリ追加するには、プロジェクトのプロジェクトのルートにあるbuild.gradleに次の行を追加します。
maven { url "https://jitpack.io" }
追加を行うと、build.gradleは次のようになります。
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
次に、appモジュールのbuild.gradleファイルに次の行を追加します。
compile 'com.github.thorbenprimke:realm-recycler-view:0.9.4'
このチュートリアルでは、サンプルデータセットと画像読み込みライブラリを使用します。これらをアプリケーションに追加するためにappモジュールのbuild.gradleファイルに次の行を追加します。
compile 'com.github.thorbenprimke:realm-nytimes-data:0.9.2'
compile 'com.github.bumptech.glide:glide:3.6.1'
以上で準備は完了です。再同期(Sync Project with Gradle files)を行い、次に進んでください。
統合
これでベースとなるプロジェクトのセットアップが完了したことになります。それではアプリケーションにRealmRecyclerViewを組み込み、表示をカスタマイズするためにRealmBasedRecyclerViewAdapterのサブクラスを作成しましょう。
MainActivityのレイアウトファイルを開き、以下のように内容を置き換えます。
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView
android:id="@+id/realm_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rrvIsRefreshable="false"
app:rrvLayoutType="Grid"
app:rrvGridLayoutSpanCount="2"
/>
</FrameLayout>
以下の3つのカスタム属性を指定している点に注意してください。
- rrvIsRefreshable - これはRecyclerViewの引っ張って更新(pull to refresh)の機能を無効にします。
- rrvLayoutType -
RealmRecyclerViewはさまざまなレイアウトタイプをサポートしており、このチュートリアルでは、2列の「グリッド」を指定します。 - rrvGridLayoutSpanCount - この属性は’rrvLayoutType’がグリッドに設定されている場合、列数を設定する必要があります。
サンプルデータに、NYTimesStory (ソース)クラがあります。このクラスをストーリーを表示するためのアダプラーのデータ型として使用します。
NYTimesStoryは、ビューを用いて以下のように表示されます。
_------------------_
Image
Date
Title
Abstract
_------------------_
RealmBasedRecyclerViewAdapterのViewHolderで使用されるレイアウトはgrid_item_view.xmlという名前です。layoutフォルダの中に以下の内容のlayout xmlファイルを作成してください。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="5dp"
android:layout_gravity="center_horizontal"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:singleLine="true"
android:ellipsize="end"
android:textStyle="bold"/>
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_gravity="center_horizontal"
android:singleLine="true"
android:ellipsize="end"/>
<TextView
android:id="@+id/story_abstract"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="5dp"
android:gravity="center_horizontal"
android:maxLines="5"
android:ellipsize="end"/>
</LinearLayout>
データモデルは、画面にNYTimesStoryを表示するために、上記のレイアウトにマッピングされます。
データモデルとそれがどのように画面に表示されるかを理解したので、RealmBasedRecyclerViewAdapterクラスのサブクラスを実装します。
public class NYTimesStoryRecyclerViewAdapter extends RealmBasedRecyclerViewAdapter<NYTimesStory,
NYTimesStoryRecyclerViewAdapter.ViewHolder> {
public NYTimesStoryRecyclerViewAdapter(
Context context,
RealmResults<NYTimesStory> realmResults,
boolean automaticUpdate,
boolean animateIdType) {
super(context, realmResults, automaticUpdate, animateIdType);
}
public class ViewHolder extends RealmViewHolder {
public TextView title;
public TextView publishedDate;
public ImageView image;
public TextView storyAbstract;
public ViewHolder(LinearLayout container) {
super(container);
this.title = (TextView) container.findViewById(R.id.title);
this.publishedDate = (TextView) container.findViewById(R.id.date);
this.image = (ImageView) container.findViewById(R.id.image);
this.storyAbstract = (TextView) container.findViewById(R.id.story_abstract);
}
}
@Override
public ViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int viewType) {
View v = inflater.inflate(R.layout.grid_item_view, viewGroup, false);
ViewHolder vh = new ViewHolder((LinearLayout) v);
return vh;
}
@Override
public void onBindRealmViewHolder(ViewHolder viewHolder, int position) {
final NYTimesStory nyTimesStory = realmResults.get(position);
viewHolder.title.setText(nyTimesStory.getTitle());
viewHolder.publishedDate.setText(nyTimesStory.getPublishedDate());
final RealmList<NYTimesMultimedium> multimedia = nyTimesStory.getMultimedia();
if (multimedia != null && !multimedia.isEmpty()) {
Glide.with(GridExampleActivity.this).load(
multimedia.get(0).getUrl()).into(viewHolder.image);
} else {
viewHolder.image.setImageResource(R.drawable.nytimes_logo);
}
viewHolder.storyAbstract.setText(nyTimesStory.getStoryAbstract());
}
}
onCreateRealmViewHolderメソッドはgrid_item_viewレイアウトをインフレートしViewHolderに引き渡します。
onBindRealmViewHolderメソッドで、NYTimesStoryはビューにバインドされます。
ストーリーの画像を表示するために、Glideを使用している点に注意してください。
部品の結合
ここまででほとんど完了しています。残っている作業は、RealmRecyclerViewとNYTimesStoryRecyclerViewAdapterを接続することです。
これらを接続するには、Activityに次のコードを実装してください。
private RealmRecyclerView realmRecyclerView;
private NYTimesStoryRecyclerViewAdapter nyTimesStoryAdapter;
private Realm realm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_grid_layout);
realmRecyclerView = (RealmRecyclerView) findViewById(R.id.realm_recycler_view);
setTitle(getResources().getString(
R.string.activity_layout_name,
getIntent().getStringExtra("Type")));
resetRealm();
Realm.setDefaultConfiguration(getRealmConfig());
realm = Realm.getDefaultInstance();
RealmResults<NYTimesStory> nyTimesStories =
realm.where(NYTimesStory.class).findAllSorted("sortTimeStamp", false);
nyTimesStoryAdapter = new NYTimesStoryRecyclerViewAdapter(this, nyTimesStories, true, true);
realmRecyclerView.setAdapter(nyTimesStoryAdapter);
final NYTimesDataLoader nyTimesDataLoader = new NYTimesDataLoader();
nyTimesDataLoader.loadAllData(realm, "YOUR_NY_TIMES_API_KEY");
}
@Override
protected void onDestroy() {
super.onDestroy();
realm.close();
realm = null;
}
private RealmConfiguration getRealmConfig() {
return new RealmConfiguration
.Builder(this)
.setModules(Realm.getDefaultModule(), new NYTimesModule())
.build();
}
private void resetRealm() {
Realm.deleteRealm(getRealmConfig());
}
ActivityのonCreateメソッドで、Realmに対するクエリの結果としてRealmResultsを取得しNYTimesStoryRecyclerViewAdapterへ渡します。NYTimesStoryRecyclerViewAdapterのコンストラクタは、2つのboolean型のパラメータを持っていることに注意してください。これらは、元のクエリに対する新たな検索結果をアダプタが自動的に表示することと、グリッドアイテムがアニメーションされることを指定しています。
また、getRealmConfig()メソッドが返すRealmのデフォルト設定としてセットしている点にも注意してください。これは、外部のライブラリであるcom.github.thorbenprimke:realm-nytimes-dataが提供するモデルクラスをスキーマに含める必要があるためです。
この時点でアプリケーションを実行することができますが、Realmは空で、何も表示されません。
今、私たちは全ての繋ぎこみを完了させたので、データを準備する必要があります。ニューヨーク・タイムズはすばらしいAPIを提供してくれているので、“トップストーリー”APIを使用して最新のニュースを取得しグリッドに表示できます。
初期セットアップ時、Gradleの依存ライブラリにrealm-nytimes-dataを追加したことを思い出してください。このライブラリは、“トップストーリー”APIから送られてくるデータを表現するNYTimesStory(ソース)とNYTimesMultimedium(ソース)を含んでいます。
このチュートリアルを簡単にするために、APIからデータを取得する方法の詳細や、JSONデータをデシリアライズしてRealmオブジェクトを構築する方法の詳細については踏み込みません。代わりに、あなたがしなければならないことは、ニューヨーク・タイムズ開発者サイトで登録を行いAPIキーを取得することです。
そして、Activity中の以下のYOUR_NY_TIMES_API_KEYの部分をあなたのAPIキーで置き換えてください。
nyTimesDataLoader.loadAllData(realm, "YOUR_NY_TIMES_API_KEY");
loadAllDataメソッドはAPIのレスポンスを取得し、Realmへの書き込みを行います。
データの読み込みが行われるようになったので、アプリケーションを実行してNYTimesのニュース記事の見出しがグリッドで表示されるはずです。
- もしあなたがデータの取得とJSONの解析の処理方法を知りたい場合は、サンプル・データ・プロジェクト内のメソッド
NYTimesDataLoader(ソース)の実装を確認してください。このメソッドは、非同期API要求を実行し、JSONレスポンスを解析してRealmモデルに変換します。