What I Learned for Android Development (Part I)
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:
- Define the button in a layout XML file with an id.
- Set an Activity to inflate the XML layout.
- Use
findViewByIdto create an object representing the button. - Do things you want with the button object.
Menus
The steps to use a menu in an Activity:
- Define a menu resource in a XML file.
- Load the menu to an Activity by overriding the
onCreateOptionsMenu(Menu menu)function, usinggetMenuInflater().inflate(R.menu.main, menu);. - Override the function
onOptionsItemSelected(MenuItem item)and implement a switch case on the menu item id to treat each menu item individually. - Remember to return true when a menu item is handled in a
casestatement. - 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:
- Prepare an
ArrayList<String>as the data for the rows of theListView. - Extends an
ArrayAdapter<String>to handle the data for theListView. - Set the adapter to use
android.R.layout.simple_list_item_checkedandArrayList<String>as the data. - Set the
ListViewto use the adapter. - Additionally set the choice mode of the
ListViewwithsetChoiceMode. - Inside the adapter, the minimal list of functions to override are the
constructor,
getItemId(int position)andhasStableIds().
The steps to use a customized ListView in Android:
- Prepare an
ArrayAdapter<?>holding of data that will be used to populate aListView. - Prepare a layout in XML representing a single row of the
ListView. - Set the adapter to use the custom
ListViewrow andArrayList<?>as the data. - Set the
ListViewto use the adapter. - Additionally set the choice mode of the
ListViewwithsetChoiceMode. - Inside the adapter, implement minimally the constructor,
getItemId(int position), andgetView(int position, View convertView, ViewGroup parent). - In
getView(int position, View convertView, ViewGroup parent), first see if aconvertViewcan be reused; if not, useinflater.inflate(R.layout.listrow, null);to inflate a new one. Then retrieve the subviews by their id defined inR.layout.listrowand set their properties (as well as listeners if you need to). Lastly return the inflated view.
Using An Activity
Steps to use an Activity:
- Prepare a layout file for an
Activity. - Extend an
Activityclass for your own use. - Override the
onCreate(Bundle savedInstanceState)method to use the layout with firstly call a super constructorsuper.onCreate(savedInstanceState);and thensetContentView(R.layout.activity_myownactivity);. - Find subviews and set their properties (and listeners) pragmatically.
- Remember to define the activity in the
AndroidManifest.xmlfile!
Starting A New Activity
Steps to start another Activity on top of the current one:
- Have the
NewActivitydefined as extendingActivityand registered inAndroidManifest.xml. - Define an
IntentwithIntent i = new Intent(currentActivity, NewActivity.class);. - Call
currentActivity.startActivity(i). - In the
NewActivity, usefinish()to go back to the callingActivity, 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:
- Use
i.putExtra("identifier", data);to putdatato theIntent. - In the
NewActivity, use a corresponding get method to obtain the data from theIntent. For example,getIntent().getExtras().getString("identifier");gets thedatathat’s previously put to theIntent. (In practice, be safe by checking whethergetIntent().getExtras()returnsnull.)
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.
- Let the class
MyDataClassimplementParcelable. - Override
describeContents()(you can do nothing, it is fine). - Override
writeToParcel(Parcel out, int flags). Theoutvariable 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 variousout.write*()methods to “send out” the data. - Implement a method that reconstruct the object that’s put into a parcel.
Note that the order the object fields are written to the
outvariable in the previous step must be obeyed.private MyDataClass(Parcel in) { this.id = in.readLong(); this.startTime = in.readLong(); this.endTime = in.readLong(); } - Implement a CREATOR that can be automatically called when an
Parcelableobject 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]; } }; - When putting objects of
MyDataClassor lists of them in to anIntent, the syntax is exactly the same as putting in simple fields, and everything is taken care of!i.putExtra("data", listOfMyDataClass); - In the
NewActivity, useArrayList<MyDataClass> logs = this.getIntent().getParcelableArrayListExtra("data");to obtain anArrayList<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:
- As usual, define the
NewActivityas usual, declare anIntentholding theNewActivity, and put data to theIntent. - Instead of calling the
startActivitymethod, call thestartActivityForResultmethod with an additionalintparameterREQUEST_NUMBER, representing a particular identifier to distinguish between different returned values. - In
NewActivity, obtain and prepare the result as necessary. Create a new emptyIntent, put the result into thisIntentas usual, set the result for return and finish theNewActivity. 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(); } }); - In the calling
Activity, overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data). TherequrestCodeis theREQUEST_NUMBERthat was used to call theNewActivity, theresultCodeshould beRESULT_OKfor you to know thatNewActivityprepared and returned result normally.dataholds the necessary information that you wanted. - Now perform desired actions based on the information contained in the
dataintent.