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 aListView
.mListAdapter
that is an instance of theObjectAdapter
class providesmListView
with data.The data provided to
mListView
is KiiObjects stored in an array of theArrayList
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.
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 likeMyObject 1
.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 thegetCurrentUser()
method. By executing thebucket()
method against this instance, you can get a bucket in the scope of this user. The variableBUCKET_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.
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" }
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 ofmListAdapter
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.
Clear the list
We clear all the KiiObjects stored in
mListAdapter
to make sure proper listing.Prepare a query
We then prepare a query for getting all data in the bucket.
First, we create a query object
query
with theKiiQuery()
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.
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).Execute the query
We execute the query by passing
query
that was created in Step 2 to thequery()
method ofbucket
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
(themyObjectValue
field of the KiiObject) is set as the title. A string such asMyObject 1
is obtained through thegetString()
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.