Android Application Model I CSE 5236 Mobile Application
Android Application Model I CSE 5236: Mobile Application Development Instructor: Adam C. Champion, Ph. D. Course Coordinator: Dr. Rajiv Ramnath Reading: Big Nerd Ranch Guide, Chapters 3, 5 (Activities); Chapter 13 (menus) 1
Android Framework Support 2
Framework Capabilities and Add-Ons • Built-In Services: – – – GUI OS services (file I/O, threads, device management) Graphics Device access (GPS, camera, media players, sensors), Networking Standard language libraries • Add-ons: – Google Play services (e. g. Google Maps, Games, etc. ) – Database support (SQLite) – Web. Kit/Chromium 3
Tooling Support • • IDE: Android Studio Testing tools: JUnit, Espresso Performance profiling tools: Android Profiler Source code management: Git, Subversion, CVS, etc. • Software emulators: Android emulator, Intel HAXM, Genymotion • Sensor injection via emulator 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 have Activities – a key part of any Android app 6
Activities in Tic-Tac-Toe 7
Specifying Activities – Android. Manifest. xml <activity android: name=". Splash. Screen" android: label="@string/app_name" android: screen. Orientation="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: launch. Mode="single. Instance" android: screen. Orientation="portrait"> <intent-filter> <action android: name="com. wiley. fordummies. androidsdk. Login"/> <category android: name="android. intent. category. DEFAULT"/> </intent-filter> </activity> 8
Implementing Activities Java Kotlin public class Login. Activity extends Single. Fragment. Activity { . . . }. . . public class Game. Session. Activity extends Single. Fragment. Activity { . . . } class Login. Activity : Single. Fragment. Activity() { . . . }. . . class Game. Session. Activity : Single. Fragment. Activity() { . . . } 9 Note: Single. Fragment. Activity extends Fragment. Activity
Activity UI • Widgets • View and View. Group • Package android. view • Specified declaratively in layout files • Always use Fragments… 10
Sample Layout: Login Activity <? xml version="1. 0" encoding="utf-8"? > <Scroll. View 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="20 dip"> <Linear. Layout android: orientation="vertical . . . <Text. View android: text="@string/login_title” /> <Text. View. . . /> <Edit. Text. . . /> <Button. . . /> </Linear. Layout> </Scroll. View> 11 <project>/app/src/main/res/layout/fragment_login. xml
Views, View. Groups, Layouts, Widgets • Many types of views, layouts and widgets: – Scroll. View, Horizontal. Scroll. View – Linear. Layout, Absolute. Layout, Frame. Layout, Relative. Layout – Text. View, Edit. Text, Button, Date. Picker, Spinner • Nested nature of layout – View. Group base class for composite elements – View base class for terminal UI components • Layout files “compiled” into resource R class in res subtree 12
Implement UI Logic: Listener Objects: Java public class Login. Activity extends Single. Fragment. Activity { // Create Login. Fragment in on. Create() method. (See Single. Fragment. Activity) } public class Login. Fragment extends Fragment implements View. On. Click. Listener { private Edit. Text m. Username. Edit. Text; private Edit. Text m. Password. Edit. Text; . . . @Override public View on. Create. View(. . . ) { // The real code has many null checks and screen rotation check (omitted for brevity) View v = inflater. inflate(R. layout. fragment_login, container, false); m. Username. Edit. Text = v. find. View. By. Id(R. id. username_text); m. Password. Edit. Text = v. find. View. By. Id(R. id. password_text); Button login. Button = v. find. View. By. Id(R. id. login_button); . . . login. Button. set. On. Click. Listener( this); Button cancel. Button = v. find. View. By. Id(R. id. cancel_button); . . . cancel. Button. set. On. Click. Listener( this); Button new. User. Button = v. find. View. By. Id(R. id. new_user_button); . . . new. User. Button. set. On. Click. Listener( this); return v; } 13
Implement UI Logic: Listener Objects: Kotlin class Login. Activity : Single. Fragment. Activity() { // Create Login. Fragment in on. Create() method. (See Single. Fragment. Activity) } class Login. Fragment : Fragment(), View. On. Click. Listener { private lateinit var m. Username. Edit. Text: Edit. Text private lateinit var m. Password. Edit. Text: Edit. Text . . . override fun on. Create. View(. . . ): View? { // Similarly, real code has screen rotation check (omitted for brevity) val v = inflater. inflate(R. layout. fragment_login, container, false) m. Username. Edit. Text = v. find. View. By. Id(R. id. username_text) m. Password. Edit. Text = v. find. View. By. Id (R. id. password_text) val login. Button = v. find. View. By. Id(R. id. login_button) login. Button. set. On. Click. Listener(this) val cancel. Button = v. find. View. By. Id(R. id. cancel_button) cancel. Button. set. On. Click. Listener(this) val new. User. Button = v. find. View. By. Id(R. id. new_user_button) new. User. Button. set. On. Click. Listener(this) return v } //. . . } 14
The On. Click Handler Java public void on. Click(View v) { switch (v. get. Id()) { case R. id. login_button: check. Login(); break; case R. id. cancel_button: finish(); break; case R. id. new_user_button: Fragment. Manager fm = get. Fragment. Manager(); Fragment fragment = new Account. Fragment(); fm. begin. Transaction() . replace(/* container */, fragment) . add. To. Back. Stack("account_fragment") . commit(); break; } // Null check code omitted } Kotlin override fun on. Click(view: View) { when (view. id) { R. id. login_button -> check. Login() R. id. cancel_button -> activity? . finish() R. id. new_user_button -> val fm = fragment. Manager fm? . begin. Transaction() ? . replace(/* container */, fragment) ? . add. To. Back. Stack(. . . ) ? . commit() } } Kotlin’s ? . operator is short for: if (object != null) { object. call. Method() 15 }
Embedding a View: Game. Session Activity, Fragment: Java public class Game. Session. Activity extends Single. Fragment. Activity { . . . } public class Game. Session. Fragment extends Fragment { . . . } <? xml version="1. 0" encoding="utf-8"? > <Linear. Layout xmlns: android="http: //schemas. android. com/apk/res/android”. . > <Linear. Layout . . . <com. wiley. fordummies. androidsdk. Board android: id="@+id/board" android: layout_width="fill_parent" android: layout_height="fill_parent"/> </Linear. Layout> <Text. View. . . "/> </Linear. Layout> 16
Embedding a View: Game. Session Activity, Fragment: Kotlin class Game. Session. Activity : Single. Fragment. Activity() { } class Game. Session. Fragment : Fragment() { } <!-- Same XML as before --> 17
Embedding a View: Board Class Java // Board. java public class Board extends View { . . . public boolean on. Touch. Event( Motion. Event event) { . . . switch (action) { case Motion. Event. ACTION_DOWN: . . . break; } return super. on. Touch. Event(event); } } Kotlin // Board. kt class Board : View { . . . override fun on. Touch. Event( event: Motion. Event): Boolean { . . . when (action) { Motion. Event. ACTION_DOWN -> {. . . } return super. on. Touch. Event(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 on. Touch. Event method in Splash. Screen. Fragment class. 19
Menus (Option menus) and Action Bars • Declare the menu items • Define on. Create. Options. Menu() and/or the on. Create. Context. Menu() callback methods in Activity. Automatically called to create the menu (what if it doesn’t exist? ). • Implement on. Options. Item. Selected() and/or on. Context. Item. Selected() 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 21 android: show. As. Action ="if. Room", "never", "with. Text" or "always".
Menu Creation: Java public class Game. Options. Fragment { . . . public View on. Create. View(…) { //. . . set. Has. Options. Menu(true); } . . . public boolean on. Create. Options. Menu(Menu menu) { super. on. Create. Options. Menu(menu, inflater); inflater. inflate(R. menu, menu); } . . . } 22
Menu Creation: Kotlin class Game. Options. Fragment { . . . override fun on. Create. View(…) { //. . . set. Has. Options. Menu(true); } . . . override fun on. Create. Options. Menu( menu: Menu, inflater: Menu. Inflater) { super. on. Create. Options. Menu(menu, inflater) inflater. inflate(R. menu_ingame, menu) } } 23
Menu Handlers: Java public boolean on. Options. Item. Selected(Menu. Item item) { Activity activity = get. Activity(); if (activity != null) { switch (item. get. Item. Id()) { case R. id. menu_settings: start. Activity(new Intent(activity, Settings. Activity. class)); return true; case R. id. menu_help: start. Activity(new Intent(activity, Help. Activity. class)); return true; case R. id. menu_exit: show. Quit. App. Dialog(); return true; case R. id. menu_contacts: start. Activity(new Intent(activity, Contacts. Activity. class)); return true; } } 24 return false; }
Menu Handlers: Kotlin override fun on. Options. Item. Selected(item: Menu. Item? ): Boolean { when (item!!. item. Id) { R. id. menu_settings -> { start. Activity(Intent(activity? . application. Context, Settings. Activity: : class. java)) return true } R. id. menu_help -> { start. Activity(Intent(activity? . application. Context, Help. Activity: : class. java)) return true } R. id. menu_exit -> { show. Quit. App. Dialog() return true } R. id. menu_contacts -> { start. Activity(Intent(activity? . application. Context, Contacts. Activity: : class. java)) return true } } Kotlin’s !! operator asserts that return false } calling object is not null 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 – Fragment. Activity • We will cover it in a later class 26
Special Types of Activities: Preferences: Java public class Settings. Activity extends App. Compat. Activity { . . . protected Fragment create. Fragment() { return new Settings. Fragment(); } . . . @Override protected void on. Create(Bundle saved. Instance. State) { . . . Fragment. Manager fm = get. Support. Fragment. Manager(); Fragment fragment = fm. find. Fragment. By. Id(R. id. fragment_container); . . . Fragment preference. Fragment = create. Fragment(); fm. begin. Transaction(). replace(R. id. fragment_container, preference. Fragment) . commit(); Preference. Manager. set. Default. Values(this, R. xml. settings, false); } }. . . public class Settings. Fragment extends Preference. Fragment. Compat { . . . @Override public void on. Create. Preferences(Bundle saved. Instance. State, String root. Key) { // Load preferences from XML resource. add. Preferences. From. Resource(R. xml. settings); } } 27
Special Types of Activities: Preferences: Kotlin class Settings. Activity : App. Compat. Activity() { . . . protected fun create. Fragment(): Fragment { return Settings. Fragment() } . . . override fun on. Create(saved. Instance. State: Bundle? ) { . . . val fm = support. Fragment. Manager val fragment = fm. find. Fragment. By. Id(R. id. fragment_container) . . . val preference. Fragment = create. Fragment() fm. begin. Transaction(). replace(R. id. fragment_container, preference. Fragment) . commit() } Preference. Manager. set. Default. Values(this, R. xml. settings, false) } }. . . class Settings. Fragment : Preference. Fragment. Compat() { . . . override fun on. Create. Preferences(saved. Instance. State: Bundle? , root. Key: String? ) { // Load preferences from XML resource. add. Preferences. From. Resource(R. xml. settings) } } 28
Layout File for a Preferences Activity <? xml version="1. 0" encoding="utf-8"? > <Preference. Screen xmlns: android=http: //schemas. android. com/apk/res/android: title="Settings" android: background="@color/background"> <Edit. Text. Preference android: key="name" android: title="Player Info" android: summary="Select your name" android: default. Value="Player 1"/> <Check. Box. Preference android: key="human_starts" android: title="Human Plays First" android: summary="Check box to play first" android: default. Value="true" /> </Preference. Screen> 29
Settings Java // Settings. java public class Settings { . . . public static String get. Name( Context context) { return Preference. Manager . get. Default. Shared. Preferences(context) . get. String(OPT_NAME, OPT_NAME_DEF); } public static boolean does. Human. Play. First( Context context) { return Preference. Manager . get. Default. Shared. Preferences(context) . get. Boolean(OPT_PLAY_FIRST, OPT_PLAY_FIRST_DEF); } } Kotlin // Settings. kt object Settings { . . . fun get. Name(context: Context): String { return Preference. Manager . get. Default. Shared. Preferences(context) . get. String(OPT_NAME, OPT_NAME_DEF) } fun does. Human. Play. First(context: Context): Boolean { return Preference. Manager . get. Default. Shared. Preferences(context) . get. Boolean(OPT_PLAY_FIRST, OPT_PLAY_FIRST_DEF) } } 30
Thank You Questions and comments? 31
- Slides: 31