Android Application Model I
CSE 5236: Mobile Application DevelopmentInstructor: Adam C. Champion, Ph.D.
Course Coordinator: Dr. Rajiv Ramnath
1
Framework Support (e.g. Android)
2
Framework Capabilities and Add-Ons
• Tailored to mobile apps• Built-in services:
– GUI – OS services (file I/O, threads, device management)– Graphics– Device access (GPS, camera, music and video players, sensors)– Web services– Networking– XML processing– Standard language libraries
• Add-ons:– Maps– Database support (SQLite)– WebKit
3
IDE Support
• Eclipse, Android Studio (Beta)• Testing tools (test management, unit
tests)• Performance profiling tools• SCM integration (Git, SVN, CVS)• Software emulators• Sensor injection (GPS, other sensors)
4
Android Studio Project Components
5
Types of Android Programs• Applications– Home app, Home screen– Take over screen
• Services– Run in the background without UI
• App widgets and home screen widgets– View-only interface to a service– Home screen widget if on Home screen
• All apps are composed of Activities – a “cohesive step” in an Android application
6
Activities in Tic-Tac-Toe
7
Specifying Activities – AndroidManifest.xml<activity android:name=".SplashScreen"
android:label="@string/app_name"android:screenOrientation="portrait">
<intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER”/>
</intent-filter> </activity><activity android:name=".Login"
android:label="@string/app_name"android:launchMode="singleInstance"android:screenOrientation="portrait">
<intent-filter><action
android:name="com.wiley.fordummies.androidsdk.Login"/><category android:name="android.intent.category.DEFAULT"/>
</intent-filter></activity>
8
Implementing Activities
Javapublic class LoginActivityextendsSingleFragmentActivity {......
}...public class GameSessionActivity extendsSingleFragmentActivity {
...
...}
Kotlinclass LoginActivity : SingleFragmentActivity() {......
}...class GameSessionActivity : SingleFragmentActivity() {
...
...}
9Note: SingleFragmentActivity extends FragmentActivity
Activity UI
• Widgets• View and ViewGroup• Package android.view• Specified declaratively in layout
files• Always use Fragments…
10
Sample Layout: Login Activity<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="http://schemas.android.com/apk/res/android”
android:background="@color/background"android:orientation="horizontal"android:layout_width="fill_parent"android:layout_height="fill_parent"android:padding="20dip"><LinearLayout android:orientation="vertical. . .
<TextView android:text="@string/login_title” /><TextView . . . /><EditText . . . /><TextView . . . /><EditText . . . /><Button . . . /><Button . . . /><Button . . . />
</LinearLayout></ScrollView>
11<project>/app/src/main/res/layout/fragment_login.xml
Views, ViewGroups, Layouts, Widgets
• Many types of views, layouts and widgets:– ScrollView, HorizontalScrollView– LinearLayout, AbsoluteLayout, FrameLayout, RelativeLayout
– TextView, EditText, Button, DatePicker, Spinner• Nested nature of layout– ViewGroup base class for composite elements– View base class for terminal UI components.
• Layout files “compiled” into resource R class in ressubtree.
12
Implement UI Logic: Listener Objects: Javapublic class LoginActivity extends SingleFragmentActivity {
// Create LoginFragment in onCreate() method. (See SingleFragmentActivity)}
public class LoginFragment extends Fragment implements View.OnClickListener {private EditText mUsernameEditText;private EditText mPasswordEditText;. . .@Overridepublic View onCreateView(. . .) {
View v = inflater.inflate(R.layout.fragment_login, container, false);
mUsernameEditText = (EditText) v.findViewById(R.id.username_text);mPasswordEditText = (EditText) v.findViewById(R.id.password_text);
Button loginButton = (Button) v.findViewById(R.id.login_button);. . .loginButton.setOnClickListener(this);Button cancelButton = (Button) v.findViewById(R.id.cancel_button);. . .cancelButton.setOnClickListener(this);Button newUserButton = (Button) v.findViewById(R.id.new_user_button);. . .newUserButton.setOnClickListener(this);
return v;}
13
Implement UI Logic: Listener Objects: Kotlin
class LoginActivity : SingleFragmentActivity() {// Create LoginFragment in onCreate() method. (See SingleFragmentActivity)
}
class LoginFragment : Fragment(), View.OnClickListener {private lateinit var mUsernameEditText: EditTextprivate lateinit var mPasswordEditText: EditText. . .override fun onCreateView(. . . ): View? {
val v = inflater.inflate(R.layout.fragment_login, container, false)
mUsernameEditText = v.findViewById<EditText>(R.id.username_text)mPasswordEditText = v.findViewById<EditText>(R.id.password_text)
val loginButton = v.findViewById<Button>(R.id.login_button)loginButton.setOnClickListener(this)val cancelButton = v.findViewById<Button>(R.id.cancel_button) cancelButton.setOnClickListener(this)val newUserButton = v.findViewById<Button>(R.id.new_user_button)newUserButton.setOnClickListener(this)
return v}
. . . } 14
The OnClick Handler
Java public void onClick(View v) {
switch (v.getId()) {case R.id.login_button:
checkLogin();break;
case R.id.cancel_button:finish();break;
case R.id.new_user_button:startActivity(new
Intent(this, Account.class));break;
}}
Kotlinoverride fun onClick(view: View) {
when (view.id) {R.id.login_button ->
checkLogin()R.id.cancel_button ->
activity.finish()R.id.new_user_button ->
startActivity(Intent(activity.applicationContext, AccountActivity::class.java))
}}
15
Embedding a View: GameSessionActivity, Fragment: Java
public class GameSessionActivity extends SingleFragmentActivity {. . .
}
public class GameSessionFragment extends Fragment {. . .
}
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android” . . >
<LinearLayout...<com.wiley.fordummies.androidsdk.Board
android:id="@+id/board"android:layout_width="fill_parent"android:layout_height="fill_parent"/>
</LinearLayout><TextView ... "/><TextView ... "/>
</LinearLayout> 16
Embedding a View: GameSessionActivity, Fragment: Kotlin
class GameSessionActivity : SingleFragmentActivity() {
}
class GameSessionFragment : Fragment() {
}
<!-- Same XML as before -->
17
Embedding a View: Board Class
Java// Board.javapublic class Board extends View {
...public boolean onTouchEvent(
MotionEvent event) {. . .switch (action) {
case MotionEvent.ACTION_DOWN:. . .break;
}return super.onTouchEvent(event);}
}
Kotlin// Board.ktclass Board : View {
. . . override fun onTouchEvent(
event: MotionEvent): Boolean {. . . when (action) {
MotionEvent.ACTION_DOWN -> { . . . }
}return super.onTouchEvent(event)
}}
18
Handling UI in the Activity
• Activity also a View• Can handle UI without any widgets. Why? – Handle non-widget-specific events (touch)– Handle user interaction outside the boundaries of
any UI components – See onTouchEvent method in SplashScreenFragment class.
19
Menus (Option menus) and Action Bars
• Declare the menu items• Define onCreateOptionsMenu() and/or the onCreateContextMenu() callback methods in Activity. Automatically called to create the menu (what if it doesn’t exist?).
• Implement onOptionsItemSelected()and/or onContextItemSelected() in activity.
20
Menu Layout File<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="Settings" android:id="@+id/menu_settings"android:icon="@android:drawable/ic_menu_preferences"
/><item android:title="Help"
android:id="@+id/menu_help"android:icon="@android:drawable/ic_menu_info_details"
/> <item android:title="Exit"
android:id="@+id/menu_exit"android:icon="@android:drawable/ic_menu_close_clear_cancel"
/><item android:title="Contacts"
android:id="@+id/menu_contacts"android:icon="@android:drawable/ic_menu_view"
/></menu>
Action Bar: Declare menu item with additional attribute android:showAsAction ="ifRoom", "never", "withText" or "always".
21
Menu Creation: Javapublic class GameOptionsFragment {
...public View onCreateView(…) {
setHasOptionsMenu(true);}public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.menu, menu);return true;
}...
}
22
Menu Creation: Kotlinclass GameOptionsFragment {
. . .fun onCreateView(…) {
setHasOptionsMenu(true);}. . .fun onCreateOptionsMenu(menu: Menu?,
inflater: MenuInflater?) {super.onCreateOptionsMenu(menu,inflater)inflater!!.inflate(R.menu.menu, menu)
}
23
Menu Handlers: Java
24
public boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {
case R.id.menu_settings:startActivity(new Intent(getActivity(),SettingsActivity.class));
return true;case R.id.menu_help:
startActivity(new Intent(getActivity(),HelpActivity.class));
return true;case R.id.menu_exit:
showQuitAppDialog();return true;
case R.id.menu_contacts:startActivity(new Intent(getActivity(),ContactsActivity.class));
return true; } return false; }
Menu Handlers: Kotlinfun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {R.id.menu_settings -> {
startActivity(Intent(activity, SettingsActivity::class.java))
return true }R.id.menu_help -> {
startActivity(Intent(activity,HelpActivity::class.java))
return true }R.id.menu_exit -> {
showQuitAppDialog()return true }
R.id.menu_contacts -> {startActivity(Intent(activity,ContactsActivity::class.java))
return true }}return false }
25
UI for Larger Screens - Fragments
• In Android 3.0 and up with compatibility library (ACL) for earlier versions
• Further decouples UI interactions from activity lifecycle– Standard concept in frameworks
• Allows reuse of UI components• Specialized activity class – FragmentActivity• We will cover it in a later class
26
Special Types of Activities: Preferences: Javapublic class SettingsActivity extends AppCompatActivity {
. . .protected Fragment createFragment() { return new SettingsFragment(); }. . .@Overrideprotected void onCreate(Bundle savedInstanceState) {
. . .FragmentManager fm = getSupportFragmentManager();Fragment fragment = fm.findFragmentById(R.id.fragment_container);. . .Fragment preferenceFragment = createFragment();fm.beginTransaction().replace(R.id.fragment_container, preferenceFragment)
.commit(); }PreferenceManager.setDefaultValues(this, R.xml.settings, false);
}. . .public class SettingsFragment extends PreferenceFragmentCompat {
. . .@Overridepublic void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
// Load preferences from XML resource.addPreferencesFromResource(R.xml.settings);
}}
27
Special Types of Activities: Preferences: Kotlinclass SettingsActivity : AppCompatActivity() {
. . . protected fun createFragment(): Fragment { return SettingsFragment() }. . . override fun onCreate(savedInstanceState: Bundle?) {
. . .val fm = supportFragmentManagerval fragment = fm.findFragmentById(R.id.fragment_container). . .val preferenceFragment = createFragment()fm.beginTransaction().replace(R.id.fragment_container, preferenceFragment)
.commit()}
PreferenceManager.setDefaultValues(this, R.xml.settings, false)}
}. . .class SettingsFragment : PreferenceFragmentCompat() {
. . .override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
// Load preferences from XML resource.addPreferencesFromResource(R.xml.settings)
}
}28
Layout File for a Preferences Activity
<?xml version="1.0" encoding="utf-8"?><PreferenceScreenxmlns:android=http://schemas.android.com/apk/res/android
android:title="Settings"android:background="@color/background">
<EditTextPreference android:key="name"android:title="Player Info"android:summary="Select your name"android:defaultValue="Player 1"/>
<CheckBoxPreference android:key="human_starts"android:title="Human Plays First"android:summary="Check box to play first"android:defaultValue="true"
/></PreferenceScreen>
29
Settings
Java// Settings.javapublic class Settings {
. . .public static String getName(
Context context) {return PreferenceManager
.getDefaultSharedPreferences(context)
.getString(OPT_NAME, OPT_NAME_DEF);}
public static booleandoesHumanPlayFirst(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(OPT_PLAY_FIRST,
OPT_PLAY_FIRST_DEF);}
}
Kotlin// Settings.ktobject Settings {
. . .fun getName(context: Context): String {
return PreferenceManager.getDefaultSharedPreferences(context).getString(OPT_NAME, OPT_NAME_DEF)
}
fun doesHumanPlayFirst(context: Context): Boolean {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(OPT_PLAY_FIRST,
OPT_PLAY_FIRST_DEF)}
}
30
Thank You
Questions and comments?
31