Android Application Model I
CSE 5236: Mobile Application DevelopmentInstructor: Adam C. Champion, Ph.D.Course Coordinator: Dr. Rajiv RamnathReading: 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)– WebKit/Chromium 3
Tooling Support
• IDE: Android Studio• Testing tools: JUnit, Espresso• Performance profilers: 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: key parts of any Android app
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 LoginActivity extends
SingleFragmentActivity {
/* ... */
}
...
public class GameSessionActivity
extends SingleFragmentActivity {
/*... */
}
Kotlinclass LoginActivity : SingleFragmentActivity()
{
/* ... */
}
...
class GameSessionActivity : SingleFragmentActivity()
{
/* ... */
}9Note: SingleFragmentActivity extends AppCompatActivity
Activity UI
• Widgets• View and ViewGroup• Package android.view• Specified declaratively in layout files• Later: always use Fragments (even for
simple Activities)
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 res subtree
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(. . .) {
// The real code has null checks, screen rotation check (omitted for brevity)View v = inflater.inflate(R.layout.fragment_login, container, false);
mUsernameEditText = v.findViewById(R.id.username_text);mPasswordEditText = v.findViewById(R.id.password_text);
Button loginButton = v.findViewById(R.id.login_button); /* ... */loginButton.setOnClickListener(this);Button cancelButton = v.findViewById(R.id.cancel_button); /* ... */cancelButton.setOnClickListener(this);Button newUserButton = v.findViewById(R.id.new_user_button); /* ... */newUserButton.setOnClickListener(this);
return v;}
13
Impl. UI Logic: Listener Objects: Kotlinclass LoginActivity : SingleFragmentActivity() {
// Create LoginFragment in onCreate() method. (See SingleFragmentActivity)
}
class LoginFragment : Fragment(), View.OnClickListener {
private lateinit var mUsernameEditText: EditText
private lateinit var mPasswordEditText: EditText /* . . . */override fun onCreateView(. . . ): View? {
// Similarly, real code has checks (omitted for brevity)
val v = inflater.inflate(R.layout.fragment_login, container, false)mUsernameEditText = v.findViewById(R.id.username_text)
mPasswordEditText = v.findViewById (R.id.password_text) val loginButton = v.findViewById(R.id.login_button)
loginButton.setOnClickListener(this)val cancelButton = v.findViewById(R.id.cancel_button)
cancelButton.setOnClickListener(this)val newUserButton = v.findViewById(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:FragmentManager fm = getFragmentManager();Fragment fragment = new AccountFragment();
fm.beginTransaction()
.replace(/* container */, fragment)
.addToBackStack("account_fragment")
.commit();
break;} // Null check code omitted
}
Kotlinoverride fun onClick(view: View) {
when (view.id) {R.id.login_button -> checkLogin()R.id.cancel_button ->
activity?.finish()R.id.new_user_button ->
val fm = fragmentManagerfm?.beginTransaction()
?.replace(/* container */, fragment)?.addToBackStack(. . .)?.commit()
}}
15
Kotlin’s operator ?. is short for:if (object != null) {
object.method() }
Embedding View: GameSession Activity, Fragment: Javapublic 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: Kotlinclass GameSessionActivity : SingleFragmentActivity() {
}
class GameSessionFragment : Fragment() {
}
<!-- Same XML as before -->
17
Embedding a View: Board Class
Java
// Board.java
public class Board extends View {
. . .
public boolean onTouchEvent(MotionEvent event) {. . .switch (action) {case MotionEvent.ACTION_DOWN:
/* . . . */break;
}return super.onTouchEvent(event);}
}
Kotlin// Board.kt
class 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
Option Menus and Action Bars
• Declare the menu items
• Define onCreateOptionsMenu() and/or 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>
21
Action Bar: Declare menu item with additional attribute android:showAsAction ="ifRoom", "never", "withText", or "always".
Menu Creation: Javapublic class GameOptionsFragment {
...
public View onCreateView(…) { // . . .
setHasOptionsMenu(true);
}
...
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu, inflater);inflater.inflate(R.menu.menu, menu);
}
...
}22
Menu Creation: Kotlinclass GameOptionsFragment {
. . .
override fun onCreateView(…) { // . . .
setHasOptionsMenu(true);
}
. . .
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {super.onCreateOptionsMenu(menu, inflater)inflater.inflate(R.menu.menu_ingame, menu)
}
}
23
Menu Handlers: Java
24
public boolean onOptionsItemSelected(MenuItem item) {Activity activity = getActivity();if (activity != null) {switch (item.getItemId()) {case R.id.menu_settings:startActivity(new Intent(activity, SettingsActivity.class));return true;
case R.id.menu_help:startActivity(new Intent(activity, HelpActivity.class));return true;
case R.id.menu_exit:showQuitAppDialog();return true;
case R.id.menu_contacts:startActivity(new Intent(activity, ContactsActivity.class));return true;
} }return false;
}
Menu Handlers: Kotlinoverride fun onOptionsItemSelected(item: MenuItem?): Boolean {when (item!!.itemId) {R.id.menu_settings -> {startActivity(Intent(activity?.applicationContext, SettingsActivity::class.java))
return true }R.id.menu_help -> {startActivity(Intent(activity?.applicationContext,HelpActivity::class.java))
return true }R.id.menu_exit -> {showQuitAppDialog()return true }
R.id.menu_contacts -> {startActivity(Intent(activity?.applicationContext,ContactsActivity::class.java))
return true }}
return false
} 25
Kotlin’s !! operator asserts thatcalling object is not null
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 = supportFragmentManager
val 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"?>
<PreferenceScreen
xmlns: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.java
public class Settings {. . .public static String getName(Context context) {return PreferenceManager
.getDefaultSharedPreferences(context)
.getString(OPT_NAME, OPT_NAME_DEF);}
public static boolean doesHumanPlayFirst(Context context) {return PreferenceManager
.getDefaultSharedPreferences(context)
.getBoolean(OPT_PLAY_FIRST,
OPT_PLAY_FIRST_DEF);}
}
Kotlin// Settings.kt
object 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