I have recently finished a project where I programmed a lot of Android. This is the first time I did a serious piece of programming in Android, so there are quite a few things worth remembering.

The Android MVC Model

According to a StackOverflow post, the Android development framework does not really have a clear model, view, controller distinction.

Models hold application data. However, in Android development, there is not a conventional place where application data are stored. This provides convenience as well as reasonable mess. The scale of my project is luckily not intractable yet. Convenience functions must be provided to easily access the data from different activities. Views and controllers in Android are relatively easy to understand. Controllers written in callback functions are interspersed everywhere in the code, requiring some getting-used-to.

Programming Android UI

Buttons

The steps to use a button in an Activity:

  1. Define the button in a layout XML file with an id.
  2. Set an Activity to inflate the XML layout.
  3. Use findViewById to create an object representing the button.
  4. Do things you want with the button object.

The steps to use a menu in an Activity:

  1. Define a menu resource in a XML file.
  2. Load the menu to an Activity by overriding the onCreateOptionsMenu(Menu menu) function, using getMenuInflater().inflate(R.menu.main, menu);.
  3. Override the function onOptionsItemSelected(MenuItem item) and implement a switch case on the menu item id to treat each menu item individually.
  4. Remember to return true when a menu item is handled in a case statement.
  5. By default, return super.onOptionsItemSelected(item);.

Callbacks

Defining callbacks for different user activities are mostly done by extending a corresponding listener class. For example, setOnClickListener for button clicks, setOnCheckedChangeListener for list view selection changes, etc.

ListView

Using a ListView is tricky. The ones that I used are android.R.layout.simple_list_item_checked (with only strings as contents in rows) as well as a customized one.

The steps to use an existing ListView in Android:

  1. Prepare an ArrayList<String> as the data for the rows of the ListView.
  2. Extends an ArrayAdapter<String> to handle the data for the ListView.
  3. Set the adapter to use android.R.layout.simple_list_item_checked and ArrayList<String> as the data.
  4. Set the ListView to use the adapter.
  5. Additionally set the choice mode of the ListView with setChoiceMode.
  6. Inside the adapter, the minimal list of functions to override are the constructor, getItemId(int position) and hasStableIds().

The steps to use a customized ListView in Android:

  1. Prepare an ArrayAdapter<?> holding of data that will be used to populate a ListView.
  2. Prepare a layout in XML representing a single row of the ListView.
  3. Set the adapter to use the custom ListView row and ArrayList<?> as the data.
  4. Set the ListView to use the adapter.
  5. Additionally set the choice mode of the ListView with setChoiceMode.
  6. Inside the adapter, implement minimally the constructor, getItemId(int position), and getView(int position, View convertView, ViewGroup parent).
  7. In getView(int position, View convertView, ViewGroup parent), first see if a convertView can be reused; if not, use inflater.inflate(R.layout.listrow, null); to inflate a new one. Then retrieve the subviews by their id defined in R.layout.listrow and set their properties (as well as listeners if you need to). Lastly return the inflated view.

Using An Activity

Steps to use an Activity:

  1. Prepare a layout file for an Activity.
  2. Extend an Activity class for your own use.
  3. Override the onCreate(Bundle savedInstanceState) method to use the layout with firstly call a super constructor super.onCreate(savedInstanceState); and then setContentView(R.layout.activity_myownactivity);.
  4. Find subviews and set their properties (and listeners) pragmatically.
  5. Remember to define the activity in the AndroidManifest.xml file!

Starting A New Activity

Steps to start another Activity on top of the current one:

  1. Have the NewActivity defined as extending Activity and registered in AndroidManifest.xml.
  2. Define an Intent with Intent i = new Intent(currentActivity, NewActivity.class);.
  3. Call currentActivity.startActivity(i).
  4. In the NewActivity, use finish() to go back to the calling Activity, or wait for the user to click the system wide back button to go back.

Transferring Simple Data From Current Activity To New Activity

For simple data, including primitive types as well as ArrayList of some primitive types, Android provides a bunch of overloaded putExtra functions for the intent holding the NewActivity. Steps to use them:

  1. Use i.putExtra("identifier", data); to put data to the Intent.
  2. In the NewActivity, use a corresponding get method to obtain the data from the Intent. For example, getIntent().getExtras().getString("identifier"); gets the data that’s previously put to the Intent. (In practice, be safe by checking whether getIntent().getExtras() returns null.)

Transferring Complex Data From Current Activity To New Activity

For passing complex data, i.e., objects of user defined classes, the data object class must implement the Parcelable interface.

  1. Let the class MyDataClass implement Parcelable.
  2. Override describeContents() (you can do nothing, it is fine).
  3. Override writeToParcel(Parcel out, int flags). The out variable can be viewed as a FIFO channel that transmit the data along the parcel so that recipients of the parcel can reconstruct the object. Use the various out.write*() methods to “send out” the data.
  4. Implement a method that reconstruct the object that’s put into a parcel. Note that the order the object fields are written to the out variable in the previous step must be obeyed.
     private MyDataClass(Parcel in) {
         this.id = in.readLong();
         this.startTime = in.readLong();
         this.endTime = in.readLong();
     }
    
  5. Implement a CREATOR that can be automatically called when an Parcelable object or a list of them must be recreated.
     public static final Parcelable.Creator<MyDataClass> CREATOR = new Parcelable.Creator<MyDataClass>() {
         public MyDataClass createFromParcel(Parcel in) {
             return new MyDataClass(in);
         }
         public MyDataClass[] newArray(int size) {
             return new MyDataClass[size];
         }
     };
    
  6. When putting objects of MyDataClass or lists of them in to an Intent, the syntax is exactly the same as putting in simple fields, and everything is taken care of! i.putExtra("data", listOfMyDataClass);
  7. In the NewActivity, use ArrayList<MyDataClass> logs = this.getIntent().getParcelableArrayListExtra("data"); to obtain an ArrayList<MyDataClass> object holding the necessary data.

Getting Feedback From New Activity To Use in Calling Activity

It is often necessary to show an Activity, let the user do something, and obtain information based on user actions. The steps to obtain feedback from another Activity:

  1. As usual, define the NewActivity as usual, declare an Intent holding the NewActivity, and put data to the Intent.
  2. Instead of calling the startActivity method, call the startActivityForResult method with an additional int parameter REQUEST_NUMBER, representing a particular identifier to distinguish between different returned values.
  3. In NewActivity, obtain and prepare the result as necessary. Create a new empty Intent, put the result into this Intent as usual, set the result for return and finish the NewActivity. The code is as follows.
     selectButton.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
             ArrayList<Integer> checked = getSelections();
             Intent res = new Intent();
             res.putIntegerArrayListExtra("selected", checked);
             setResult(Activity.RESULT_OK, res);
             finish();
         }
     });
    
  4. In the calling Activity, override protected void onActivityResult(int requestCode, int resultCode, Intent data). The requrestCode is the REQUEST_NUMBER that was used to call the NewActivity, the resultCode should be RESULT_OK for you to know that NewActivity prepared and returned result normally. data holds the necessary information that you wanted.
  5. Now perform desired actions based on the information contained in the data intent.