前回の続きです。
https://qiita.com/KIRIN3qiita/items/4573d26187b8c9e53fc6
今回はRealtime Databaseの実践編としてゲームクリアタイムのランキングを作成します。
私が昔作成したお釣り支払いゲームのクリアタイムランキング画像です。
自作のしょぼいゲームアプリでも、クリアタイムランキングがあると少しは形になります。(インストール数100ぐらいですが・・・)
至高の支払い(https://play.google.com/store/apps/details?id=jp.kirin3.changegame&hl=ja)
これを応用すればスコアランキングなども作れます。
ルール作成
$ Variablesを利用して、ユーザーごとのデータを保存するルールを作成します。
$ Variablesについて
---------------------------
$ 接頭辞を持つキャプチャ変数を宣言することにより、読み取りや書き込みのパスの一部をキャプチャできます。これはワイルドカードとして機能し、ルール宣言の内部で使用するために該当のキーの値を保存します
---------------------------
ユーザーごとの名前、クリア時間、クリア日時、ユーザーIDを保存するルールを作成しました。
{
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
"rules": {
"info": {
".read": true,
".write": true,
"$user_id": {
"name": {
".validate": "newData.isString() &&
newData.val().length < 9"
},
"time": {
".validate": "newData.isNumber()"
},
"date": {
".validate": "newData.isString()"
},
"user_id": {
".validate": "newData.isString()"
}
}
}
}
}
"$user_id"の項目の中に"user_id"があるのは気持ちが悪いですが、同じ階層のデータを引き抜くイメージなのであると便利です。
データ登録
まずデータ保存、取得に利用するクラスを作成。
public static class User {
public int rankingNo;
public String name;
public Double time;
public String date;
public String userId;
// 空のコンストラクタの宣言が必須
public User() {
}
public User(String _name, Double _time,String _date, String _userId) {
name = _name;
time = _time;
date = _date;
userId = _userId;
}
public void setRankingNo( int _rankingNo ){
rankingNo = _rankingNo;
}
public Integer getRankingNo(){
return rankingNo;
}
public String getName(){
return name;
}
public Double getTime(){
return time;
}
public String getDate(){
return date;
}
public String getUserId(){
return userId;
}
}
// とりあえずUserIdはUUIDで作成。(その後、毎回変わらないようにプリファランスに保存するなどが必要)
String sUserId = UUID.randomUUID().toString();
User user = new User( "UMEHARA",5.3,"2019-10-21 09:00:27" ,sUserId);
// インスタンスの取得
FirebaseDatabase database = FirebaseDatabase.getInstance();
// ファイルパスを指定してリファレンスを取得
DatabaseReference ref = database.getReference("info");
// データを登録
ref.child(sUserId).setValue(user);
データ取得
取得前に一度だけ終端データを登録しておきます。(なぜ必要かは後で説明)
public void SaveTerminal(){
String userId = TARMINAL;
String userName = "ターミナル";
Double time = 99999.9;
String date = "1999-01-01 00:00:00";
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("info");
User user = new User( userName,time,date,userId );
ref.child(userId).setValue(user);
}
// ユーザーデータ配列
public static ArrayList<User> sUsers;
// ランキングの最大取得数
final int OUTPUT_RANKING_NUM = 100;
FirebaseDatabase database = FirebaseDatabase.getInstance();
// ファイルパスを指定してリファレンスを取得
final DatabaseReference refUser = database.getReference("info");
// クリア時間でソート、最大取得数を設定
refUser.orderByChild("time").limitToFirst(OUTPUT_RANKING_NUM + 1).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
User user = dataSnapshot.getValue(User.class);
// ランキング順位もオブジェクトに保存
sRankNo++;
user.setRankingNo(sRankNo);
// 終端データを受け取ったら、アダプターに渡し、リストビューに表示
if( dataSnapshot.getKey().equals(TARMINAL) || sRankNo == OUTPUT_RANKING_NUM + 1 ){
UserAdapter adapter = new UserAdapter(getApplicationContext(), 0, sUsers);
sListView.setAdapter(adapter);
}
// 配列に保存
else {
sUsers.add(user);
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
取得したデータを配列に入れて、アダプターに渡し、ListViewで表示します。
データはクリア時間でソートされ、最大100件の取得に設定しています。
データの取得完了が判断できないので終端データ(必ず最後に取得するデータ)を登録してデータの終わりを判断しています。ルール文で"$user_id"の項目の中に"user_id"があるなど、もっとよい書き方があるかもしれませんが、一応これでも動くということでご参考にして頂けたらと思います。
Realtime Databaseは癖がすごくて使いこなすのは大変ですね。