Data Listing Screen Implementation

Finally, we will dive into the implementation of the data listing screen. This screen is implemented in the MainActivity.java file.

The data listing screen is defined in the layout file main.xml. The screen is bound to the implementation in the class as follows:

  • The Java field mListView has a ListView.

  • mListAdapter that is an instance of the ObjectAdapter class provides mListView with data.

  • The data provided to mListView is KiiObjects stored in an array of the ArrayList class.

Data structure

In the data listing screen, we create a KiiObject that has a key-value pair in JSON format and communicate it with Kii Cloud. The data will be stored in the bucket myBucket that is in the scope of the currently logged-in user.

We implement logic for creating, listing, and deleting KiiObjects in the mobile app.

Data

You can handle arbitrary data types and values as key-value pairs in JSON format in Kii Cloud.

In Hello Kii, the code specifies myObjectValue as the key name. The resulting data will be processed as a JSON string like the one in the above figure.

When you code your own program, you can use arbitrary keys and values including nested JSON data, according to the specification of your mobile app.

You do not need to define any value types beforehand with a schema and such. You just need to create a KiiObject in your code, and then you will be able to store and retrieve the data. Values stored at the top level of JSON data will be auto-indexed so that you can query KiiObjects at high speed.

Creating data

First, we will explain the process to create data.

The addItem() method is called when the "Add Item" button is tapped. The following sample code is an excerpt from the code of this method.

String value = "MyObject " + (++mObjectCount);

KiiBucket bucket = KiiUser.getCurrentUser().bucket(BUCKET_NAME);

// Create a new KiiObject instance and set the key-value pair.
KiiObject obj = bucket.object();
obj.set(OBJECT_KEY, value);

// Save the object asynchronously.
obj.save(new KiiObjectCallBack() {
  public void onSaveCompleted(int token, KiiObject o, Exception e) {
    if (e == null) {
      // Insert the object at the beginning of the list adapter.
      MainActivity.this.mListAdapter.insert(o, 0);
    } else {
      showToast("Error creating object: " + e.getLocalizedMessage());
    }
  }
});

mObjectCount, BUCKET_NAME, and OBJECT_KEY in the above sample code are declared as below:

private int mObjectCount = 0;
private static final String BUCKET_NAME = "myBucket";
private static final String OBJECT_KEY = "myObjectValue";

These steps are processed in order.

  1. Create target data

    We create the data value to store in a KiiObject as value. In the code, we create the value with an incrementing number like MyObject 1.

  2. Prepare a target bucket

    We prepare a bucket in the scope of the currently logged-in user.

    As described in the previous topic, you can get a KiiUser instance of the currently logged-in user with the getCurrentUser() method. By executing the bucket() method against this instance, you can get a bucket in the scope of this user. The variable BUCKET_NAME is passed as the argument. This variable stores the name of the bucket to retrieve.

    If the bucket does not exist, it will be created when the data to store in the bucket is created. If the bucket already exists, this bucket will be retrieved.

  3. Prepare a target KiiObject

    You can create a KiiObject in a bucket by executing the object() method.

    The set() method sets a key-value pair in the top level of the JSON data of a KiiObject as below:

    {
      "myObjectValue" : "MyObject 1"
    }
    
  4. Save the KiiObject

    Execute the save() method to save the KiiObject. Step 1 to 3 are executed solely on the device, but this step will access Kii Cloud and save the KiiObject on Kii Cloud. Since this method involves network access, we use the non-blocking API.

    When the KiiObject is saved, the onSaveCompleted() method is called as the callback. The registered KiiObject is added to the top of mListAdapter when there is no error. At the same time, it is reflected on the screen. An error message will be toasted on the screen when there is any error.

Note that the insert() method updates the data on the device while the save() method updates the data on Kii Cloud.

Listing data

Data is listed when the data listing screen is displayed. At the end of the onCreate() method, the loadObjects() method is executed.

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ......
  // Query for any objects created previously.
  this.loadObjects();
}

Retrieving data

The entire list is fetched by querying for all data in the bucket. The following sample code is an excerpt of the loadObjects() method's process.

private void loadObjects() {
  // Empty the adapter.
  mListAdapter.clear();

  // Create an empty KiiQuery. This query will retrieve all results sorted by the creation date.
  KiiQuery query = new KiiQuery(null);
  query.sortByDesc("_created");

  // Define the bucket to query.
  KiiBucket bucket = KiiUser.getCurrentUser().bucket(BUCKET_NAME);

  // Perform the query.
  bucket.query(new KiiQueryCallBack<KiiObject>() {
      public void onQueryCompleted(int token, KiiQueryResult<KiiObject> result, Exception e) {
          ...
      }
  }, query);
}

These steps are processed.

  1. Clear the list

    We clear all the KiiObjects stored in mListAdapter to make sure proper listing.

  2. Prepare a query

    We then prepare a query for getting all data in the bucket.

    First, we create a query object query with the KiiQuery() constructor. An empty query (null) is set because we want to get all KiiObjects.

    Then, we set the condition with the sortByDesc("_created") method to specify the sort order. This condition sorts the data in the descending order of the object creation time that is stored in the _created field. It is appropriate to sort data in the descending order because a new KiiObject is added to the top of the list as previously explained.

    The query API supports various query conditions such as comparing the string and numbers and concatenating multiple conditions with And/Or. The query is not made with the SQL; it is to be made with the condition expression that aligns with the target object structure.

  3. Prepare a target bucket

    We prepare a target bucket to query. Just like when we create the data, the variable BUCKET_NAME is passed as the argument. This variable stores the name of the bucket to retrieve (myBucket in the scope of the logged-in user).

  4. Execute the query

    We execute the query by passing query that was created in Step 2 to the query() method of bucket as the second argument.

    The query will access Kii Cloud via the network, so we use the non-blocking API. The callback method onQueryCompleted() will be explained in detail in the next section.

Processing retrieved data

The process in the callback method onQueryCompleted() displays data in the bucket if the query successfully fetched all data in the bucket.

bucket.query(new KiiQueryCallBack<KiiObject>() {
  public void onQueryCompleted(int token, KiiQueryResult<KiiObject> result, Exception e) {
    if (e == null) {
      // Add the objects to the list view via the adapter.
      List<KiiObject> objLists = result.getResult();
      for (KiiObject obj : objLists) {
        mListAdapter.add(obj);
      }
    } else {
      showToast("Error loading objects: " + e.getLocalizedMessage());
    }
  }
}, query);

The following process is performed if the fetch was successful (or e is null).

The query result is passed as result that is an argument of the onQueryCompleted() method. result contains a list of fetched KiiObjects as an array that you can get with the getResult() method. By fetching each KiiObject and passing it to mListAdapter in the loop, you can show the data on the screen with the help of the ObjectAdapter class.

This implementation does not function correctly when the number of fetched objects is large. When the number is large, the query method returns the results in multiple pages by its pagination feature. To avoid complex implementation, Hello Kii processes only the first page.

Displaying data in the ListView

The logic to display data is implemented in the ObjectAdapter class that is a subclass of the ArrayAdapter class, according to the method to implement the ListView in Android.

The style of each row that is displayed in the Listview is defined in the layout file row.xml. For each row, we define values as illustrated in the next figure.

Here is an excerpt of the process of the ObjectAdapter class.

public class ObjectAdapter extends ArrayAdapter<KiiObject> {
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    KiiObject obj = getItem(position);

    LinearLayout rowView = ...

    // Get the text fields for the row.
    TextView titleText = (TextView) rowView.findViewById(R.id.rowTextTitle);
    TextView subtitleText = (TextView) rowView.findViewById(R.id.rowTextSubtitle);

    // Set the content of the row text.
    titleText.setText(obj.getString(OBJECT_KEY));
    subtitleText.setText(obj.toUri().toString());

    return rowView;
  }
}

We set KiiObject data in views in LinearLayout.

First, the index number (0, 1, 2, ...) of an element that is being processed is passed as an argument position. A KiiObject that is referred to by the index number is fetched in obj.

Next, the values in obj are set in TextViews in LinearLayout. The TextViews defined for the Listview are rowTextTitle and rowTextSubtitle.

  • rowTextTitle

    The value of OBJECT_KEY (the myObjectValue field of the KiiObject) is set as the title. A string such as MyObject 1 is obtained through the getString() method of the KiiObject.

  • rowTextSubtitle

    The URI of the KiiObject is set as the subtitle.

    Kii Cloud uniquely identifies each KiiObject within an application with its URI. Note that you cannot use URI in the REST API.

Deleting data

You can delete an item by tapping it in the list. A confirmation message is displayed and the performDelete() method below is executed when an item is deleted. The argument position receives the index number (0, 1, 2, ...) of an item to be deleted.

void performDelete(int position) {
  // Get the object to delete with the index number of the tapped row.
  final KiiObject o = MainActivity.this.mListAdapter.getItem(position);
  // Delete the object asynchronously.
  o.delete(new KiiObjectCallBack() {
    public void onDeleteCompleted(int token, Exception e) {
      if (e == null) {
        // Remove the object from the list adapter.
        MainActivity.this.mListAdapter.remove(o);
      } else {
        showToast("Error deleting object: " + e.getLocalizedMessage());
      }
    }
  });
}

The implementation is the same as we've seen in other features. The KiiObject is taken out from mListAdapter and deleted on Kii Cloud with the non-blocking API. If there is no error, the item is removed from the screen.


We are now done with walking through the implementation.

We've shown that you can use the API provided by Kii Cloud SDK to manipulate the data on the cloud. We've also presented when you should use the non-blocking API. You should be able to start implementing other features easily by understanding these basics.


What's next?

We will explain how you can use our programming guide to start adding new features.

Go to Modify the Program.

If you want to learn more...

  • See Creating a KiiObject to learn more details on how to create objects. Also, read Querying KiiObjects to learn more about how to query for objects.

  • Object ID and URI covers the relationship between an object and its URI. If you want to cover a scenario like saving your app configuration in an object and later retrieving it, for example, the content covered in Retrieving with URI will be helpful.