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
findViewById
to 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
case
statement. - 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_checked
andArrayList<String>
as the data. - Set the
ListView
to use the adapter. - Additionally set the choice mode of the
ListView
withsetChoiceMode
. - 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
ListView
row andArrayList<?>
as the data. - Set the
ListView
to use the adapter. - Additionally set the choice mode of the
ListView
withsetChoiceMode
. - 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 aconvertView
can 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.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
:
- Prepare a layout file for an
Activity
. - Extend an
Activity
class 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.xml
file!
Starting A New Activity
Steps to start another Activity
on top of the current one:
- Have the
NewActivity
defined as extendingActivity
and registered inAndroidManifest.xml
. - Define an
Intent
withIntent 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 putdata
to theIntent
. - In the
NewActivity
, use a corresponding get method to obtain the data from theIntent
. For example,getIntent().getExtras().getString("identifier");
gets thedata
that’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
MyDataClass
implementParcelable
. - Override
describeContents()
(you can do nothing, it is fine). - Override
writeToParcel(Parcel out, int flags)
. Theout
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 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
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(); }
- 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]; } };
- When putting objects of
MyDataClass
or 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
NewActivity
as usual, declare anIntent
holding theNewActivity
, and put data to theIntent
. - Instead of calling the
startActivity
method, call thestartActivityForResult
method with an additionalint
parameterREQUEST_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 thisIntent
as 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)
. TherequrestCode
is theREQUEST_NUMBER
that was used to call theNewActivity
, theresultCode
should beRESULT_OK
for you to know thatNewActivity
prepared and returned result normally.data
holds the necessary information that you wanted. - Now perform desired actions based on the information contained in the
data
intent.