データ項目の編集機能

データ一覧画面での編集機能は、データ項目の編集と、Kii Cloud へのアクセスの 2 つが大きな要素となります。

  • データ項目の編集 は、一般的な Android アプリの開発手法と同様です。DialogFragment を使って項目の値を編集し、結果を BalanceListFragment で受け取ります。フラグメントを使った実装を熟知している方は、読み飛ばしても問題ありません。

  • Kii Cloud へのアクセス は、ダイアログから取得したデータを元に Kii Cloud SDK の API を呼び出して、データを書き換えます。

ソースコードは以下のとおりです。

データ項目の編集

データ一覧画面では、収支のデータを編集できます。画面右下の "+" ボタンのタップではデータの追加を、一覧項目のタップではデータの更新と削除を実行できます。

項目の編集は、BalanceListFragmnet クラスと ItemEditDialogFragment クラスで実行します。以下は編集を行う際の呼び出し関係を表現したものです。

"+" ボタンがタップされたときはリクエストコード REQUEST_ADD でダイアログを呼び出し、追加項目の入力を行います。一覧項目がタップされたときはリクエストコード REQUEST_EDIT でダイアログを呼び出し、更新または削除項目の入力を行います。いずれの場合も、ItemEditDialogFragment クラスへの引数として、ダイアログに初期表示される値が渡されます。

ダイアログで "ADD"、"DELETE"、"UPDATE" のいずれかのボタンがタップされると、対応する ACTION_ADDACTION_DELETEACTION_UPDATE のいずれかが Intent のアクションとなって onActivityResult() メソッドが呼び出されます。

BalanceListFragment での onActivityResult() メソッドでは、リクエストコードと Intent のアクションから以下のような処理を行います。

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (resultCode != Activity.RESULT_OK) { return; }

  switch (requestCode) {
  case REQUEST_ADD: {
    String name = data.getStringExtra(ItemEditDialogFragment.RESULT_NAME);
    int type = data.getIntExtra(ItemEditDialogFragment.RESULT_TYPE, Field.Type.EXPENSE);
    int amount = data.getIntExtra(ItemEditDialogFragment.RESULT_AMOUNT, 0);

    createObject(name, type, amount);
    break;
  }
  case REQUEST_EDIT: {
    String action = data.getAction();
    String objectId = data.getStringExtra(ItemEditDialogFragment.RESULT_OBJECT_ID);
    String name = data.getStringExtra(ItemEditDialogFragment.RESULT_NAME);
    int type = data.getIntExtra(ItemEditDialogFragment.RESULT_TYPE, Field.Type.EXPENSE);
    int amount = data.getIntExtra(ItemEditDialogFragment.RESULT_AMOUNT, 0);

    if (ItemEditDialogFragment.ACTION_UPDATE.equals(action)) {
        updateObjectInList(objectId, name, type, amount);
    } else {
        deleteObject(objectId);
    }
    break;
  }
  }
  super.onActivityResult(requestCode, resultCode, data);
}

追加処理はリクエストコード REQUEST_ADD で通知されるため、createObject() メソッドを呼び出して KiiObject の新規作成を行います。

一覧の項目のタップ(更新または削除)はリクエストコード REQUEST_EDIT に加え、インテントのアクションによって更新と削除を区別します。ACTION_UPDATE の場合は updateObjectInList() メソッドで更新処理を、それ以外(ACTION_DELETE)は deleteObject() メソッドで削除処理を行います。

これ以外の実装の詳細は、ソースコード(BalanceListFragmentItemEditDialogFragment)を直接確認してください。

Kii Cloud へのアクセス

ダイアログでの入力が終わると、onActivityResult() メソッドでは、KiiObject の追加、更新、削除のいずれかの機能が実行されます。

KiiObject の追加

KiiObject の追加処理では、カレントユーザーのスコープの Bucket に KiiObject を作成します。KiiObject の作成方法は、Hello Kii の場合と同様です。

今回は、KiiObject のキーと値のペアとして、以下の内容を作成します。

  • name:収支の名目
  • type:エントリーのタイプ(収入であれば 1、支出であれば 2)
  • amount:収支の額(画面表示された値の 100 倍、セント単位)

これ以外にも、KiiObject が Kii Cloud に保存されるタイミングで、作成日時 _created などの既定フィールドが自動的に作成されます。

以下に作成処理のコードを示します。本質的ではない部分は省略しています。

private void createObject(String name, int type, int amount) {
  KiiUser user = KiiUser.getCurrentUser();
  KiiBucket bucket = user.bucket(Constants.BUCKET_NAME);
  KiiObject object = bucket.object();

  object.set(Field.NAME, name);
  object.set(Field.TYPE, type);
  object.set(Field.AMOUNT, amount);

  object.save(new KiiObjectCallBack() {
    @Override
    public void onSaveCompleted(int token, KiiObject object, Exception e) {
      if (e != null) {
        ViewUtil.showToast(getActivity(), e.getMessage());
        return;
      }

      ViewUtil.showToast(getActivity(), R.string.add_succeeded);
      addObjectToList(object);
    }
  });
}

KiiObject の更新

KiiObject の更新処理では、メソッドの引数の値で指定された値を使って KiiObject を書き換えます。

KiiObject の ID は更新処理メソッドの引数で渡されます。onListItemClick() メソッドでデータ一覧のタップを処理するとき、対応する KiiObject の ID をダイアログに渡しており、その値が更新処理メソッドまで中継されてきます。

同時に、ダイアログで編集された収支の名目、エントリーのタイプ、収支の額が引数で渡されるため、これらを使って KiiObject を更新します。

以下に更新処理のコードを示します。

private void updateObjectInList(final String objectId, String name, int type, int amount) {
  KiiUser user = KiiUser.getCurrentUser();
  KiiBucket bucket = user.bucket(Constants.BUCKET_NAME);
  KiiObject object = bucket.object(objectId);

  object.set(Field.NAME, name);
  object.set(Field.TYPE, type);
  object.set(Field.AMOUNT, amount);

  object.save(new KiiObjectCallBack() {
    @Override
    public void onSaveCompleted(int token, KiiObject object, Exception e) {
      if (e != null) {
        ViewUtil.showToast(getActivity(), e.getMessage());
        return;
      }
      ViewUtil.showToast(getActivity(), R.string.update_succeeded);
      updateObjectInList(object, objectId);
    }
  });
}

更新処理では、ID 指定で KiiObject を作成して save() メソッドを呼び出します。対象の KiiObject は、ログインユーザーのスコープの Bucket から、ID を引数に object() メソッドを呼び出すことで作成します。

ここで、save() メソッドを実行する KiiObject の Java インスタンスは、一覧表示のために KiiObjectAdapter で管理しているインスタンスとは異なりますが、更新処理は正しく実行されます。更新処理の際、REST API の URL を構成する ID や対象 Bucket の文字列が同じであれば、Kii Cloud 上の KiiObject を正しく更新することができます。

Kii Cloud での更新が成功すると、updateObjectInList() メソッドによって、KiiObjectAdapterKiiObject インスタンスを新しいものに差し替えます。

なお、KiiObjectAdapter に格納されている KiiObject インスタンスを取得し、そのフィールドを書き換えてから save() メソッドを呼び出す実装でも問題ありません。ただし、この実装では、エラー発生時にクライアント側の KiiObject を巻き戻す処理(保存しておいた変更前の値に戻す処理など)が必要になります。

更新機能の設計

Kii Cloud での更新処理には、以下の 3 通りがあります。今回は、部分アップデート(更新チェックなし)の方式を実行しています。

  • フルアップデート(更新チェックなし)

    クライアントから送信したキーと値のペアで、サーバーの KiiObject を完全に置き換える更新です。それまでにサーバーに保存されていた値は残りません。

    更新の際、他のクライアントから書き換えられたかどうかをチェックしません。

  • 部分アップデート(更新チェックなし)

    クライアントから送信したキーと値のペアと、サーバーのキーと値のペアがマージされる更新です。結果として、サーバーに保存されていたキーのうち、クライアントが送信していないものは存在し続けます。

    更新の際、他のクライアントから書き換えられたかどうかをチェックしません。

  • フルアップデート(更新チェックあり)

    クライアントから送信したキーと値のペアで、サーバーの KiiObject を完全に置き換える更新です。それまでにサーバーに保存されていた値は残りません。

    更新の際、前回その KiiObject がダウンロードされてから更新されるまでの間に、他のクライアントが KiiObject を更新しているとエラーになります。

これらの動作イメージは、KiiObject の更新 で詳細を説明しています。

Kii Balance では、クライアントから送信する KiiObject のキーのセットは、サーバー上のキーと同じであるため、フルアップデートと部分アップデートのどちらを選んでも問題ありません。

また、今回の更新対象はユーザースコープであるため、上書きによる事故は起きないものとして更新チェックなしとしています。もし、グループスコープのように複数のユーザーが同時に書き込むような場合には、更新チェックありを採用した方が、より安全といえます。

実際のモバイルアプリを設計する際は、差分更新かどうか、同時アクセスが発生するかどうかなどの条件を考慮して更新方法を検討してください。

KiiObject の削除

KiiObject の削除処理では、指定された ID の KiiObject を削除します。

以下に削除処理を示します。

private void deleteObject(final String objectId) {
  KiiUser user = KiiUser.getCurrentUser();
  KiiBucket bucket = user.bucket(Constants.BUCKET_NAME);
  KiiObject object = bucket.object(objectId);

  object.delete(new KiiObjectCallBack() {
    @Override
    public void onDeleteCompleted(int token, Exception e) {
      if (e != null) {
        ViewUtil.showToast(getActivity(), e.getMessage());
        return;
      }
      ViewUtil.showToast(getActivity(), R.string.delete_succeeded);
      removeObjectFromList(objectId);
    }
  });
}

削除処理も更新の場合と同様です。ID を指定して削除用の KiiObject インスタンスを新しく生成し、delete() メソッドによって Kii Cloud 上の KiiObject を削除します。

削除の完了後、removeObjectFromList() メソッドによって、KiiObjectAdapter の KiiObject を ID 指定で削除します。


Android のチュートリアルは以上で完了です。さらに解析を進める場合は、クラス構成 の情報をヒントに、ソースコードを読み解いてください。


次は...

iOS のモバイルアプリの実装について説明します。

iOS モバイルアプリの解説 に移動してください。

iOS 版が不要な場合、チュートリアルの最後に、データ設計や実装上の注意点を説明します。より実用的なモバイルアプリを設計するためのヒントとしてご覧ください。

アプリケーション設計の最適化 に移動してください。