In this chapter, we will cover the following recipes:
Creating Xamarin.Android projects
Creating user interface layouts
Creating an option menu
Supporting previous Android versions
Adding an action bar
Navigating with the action bar
Adding action bar action items
Creating contextual action mode menu
Sharing code with other platforms
Xamarin.Android allows us to create native Android applications using the same UI controls we would use in Java, with the flexibility of C#, the power of the .NET Base Class Library, and two first-class IDEs.
Android development with Xamarin can be done on either Mac OS X or Windows, using either Visual Studio or Xamarin Studio. This variety provides us with our choice of how we want to work to create awesome apps.
This book will enable us, as developers, to create amazing, professional apps for the Android ecosystem. This knowledge can be used on so many platforms, from TVs and smartphones to watches, wearables, and many other Android-powered devices.
This chapter covers some of the most common tasks and steps to getting our app ready for development. We will learn how to create a new app, add support for the old versions of Android, and get started with the user interface used in all Android apps. We will also look at just how powerful the Xamarin.Android platform is by looking how we can share code with many other platforms, including Windows Phone, iOS, Windows, and Mac.
Before any apps can be created, the development environment has to be set up and the software downloaded and installed.
Before we start creating any Android apps, we need to get our tools in place using the installer from Xamarin.
Go to http://xamarin.com/download:
Enter your details for registration.
Click on the Download Xamarin for Windows or Download Xamarin for OS X links, depending on the operating system you are using.
Once the download has completed, launch the installer, following the on-screen instructions. Setup will continue to download and install all the required components:
Once the installer has finished, you should have a working installation of Xamarin Studio, the IDE designed for cross-platform development.
Creating Xamarin.Android projects is very simple!
Open Xamarin Studio.
Select File, then New, and then Solution…:
Select C#, then Android, and then Android Application:
Enter a name for your app, for example
XamarinCookbook
.Click on OK.
We now have a fully functional Xamarin.Android app, which can be deployed to a device or an emulator.
In the target device dropdown, select either an emulator or your device (if you have attached an Android device).
Finally, click on Run and the app will install and launch.
Xamarin.Android allows us to write native Android apps using .NET and C# or F#. Xamarin.Android does not abstract or emulate any Android features. Rather, it is an alternate programming language available for the development of Android apps.
Some of the benefits of using Xamarin.Android are found in the small things. For example, if we are using Android Studio or Eclipse, we will have to make changes in AndroidManifest.xml
. If we are using Xamarin.Android, we can do much of this work by using the familiar attributes.
Tip
Various attributes can be used to provide the same functionality that modifying the AndroidManifest.xml
file would bring.
To add the <activity>
element into the manifest with Xamarin.Android, we add the [Activity]
attribute on an activity as follows:
[Activity( Label = "My App", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { }
This will create a section in ApplicationManifest.xml
at compile time, as highlighted in the following code:
<activity android:label="My App" android:icon="@drawable/icon" android:name="mynamespace.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
If we want to add permissions to our app, all we need to do is add this:
[assembly: UsesPermission(Manifest.Permission.Camera)]
There are many other attributes that help us build the manifest file, such as the [Service]
and [IntentFilter]
attributes.
All apps require some form of user interface for the user to input data or view the output of information.
Creating an interface for our apps is very easy. There are two ways to create user interfaces, with code or with XML:
If we are using code to create a button on the screen, we would do something similar to this:
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); LinearLayout layout = new LinearLayout(this); layout.Orientation = Orientation.Vertical; Button button = new Button(this); button.Text = "Hello World!"; layout.AddView( button, ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); SetContentView(layout); }
Both XML and code can be used to create equivalent UIs, but using XML, we have additional capabilities:
The equivalent interface in XML would be created and stored in the
layout
sub-folder of theResources
folder and reads as follows:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Hello World!" /> </LinearLayout>
Once we have created the interface in XML, we have to indicate to the activity which layout file is to be used. This is done by invoking the
SetContentView()
method in theOnCreate()
method of the activity. For example, say we named our layout fileMain.axml
:protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); // note the name "Main" SetContentView(Resource.Layout.Main); }
Regardless of whether the layout was created in code or through XML files, we are able to access the various controls similarly:
In order to access the control at runtime, we make use of the
FindViewById
method on the activity or a view (use theView
property of a fragment):Button btn = FindViewById<Button>(Resource.Id.buttonId);
Separating the UI from the code allows us to easily make changes for updates as well as to support different screen configurations. The benefit of this is that it allows us to modify the UI without updating the code. And part of this is the fact that the Android system can switch the entire layout at runtime. Different layouts for different screen configurations can be selected simply by changing the suffix of the layout
folder name.
Tip
Fragments can be used in addition to layouts to create advanced interfaces consisting of self-contained, reusable regions.
For example, if we want our Main
layout to have the LinearLayout
method to be vertical in portrait orientation and horizontal in landscape orientation, all we have to do is create two layout files with the same name in different folders as follows:
<project-root>/Resources/layout/<layout-file-name>.axml <project-root>/Resources/layout-land/<layout-file-name>.axml
There are many uses for the suffixes, and there are many different suffixes. Each of the resource subfolders can have suffixes, including values
, layout
, menu
, and drawable
. Each of the folders can have combinations of the suffixes for language or culture, screen orientation, screen density, screen size, and platform version. More information can be found online at https://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources.
The Android layout structure usually follows the structure of the type, with the element name matching the type name and property names matching the attribute names. For example, the Button
type has a Text
property; thus, the XML will have a <Button android:text="..." />
element.
Although we can nest one or more layouts within another layout, we should strive to keep our layout hierarchy as shallow as possible. The layout will be drawn faster if it has fewer nested layouts.
One of the most important attributes in layouts is the id
attribute. This attribute is used to uniquely identify a view within a tree. An ID need not be unique throughout the entire tree, but it should be unique within the part of the tree that is being searched.
Layout files are an easy way to create the UI separate from the code, and in the same way, resource files can be used to separate the localizable text from the layout. This is achieved by placing the strings into a resource file and then, referencing each the string from the layout. Say we have a button that has some text:
<Button android:text="Hello World!" />
This value can be extracted and placed into a file under the values
folder of the project resources (<project-root>/Resources/values/<file-name>.xml
):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="buttonText">Hello World!</string>
</resources>
The layout file can then be updated to reference the value:
<Button android:text="@string/buttonText" />
Using this pattern, we are able to not only extract strings but any value for any attribute, including layout information. An example would be to extract an element's padding and use a larger padding for larger screens. These types of resources are placed in the values
folder with a suffix, such as -large
for large screens.
Chapter 2 , Showing Views and Handling Fragments, Creating and using fragments
Providing Resources | Android Developers, https://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources
Android provides the user with the ability to display a special type of menu that contains a set of items that pertains to the entire app, instead of the current activity.
Adding an options menu to our app is very simple, and only two things are required: a menu structure and code to connect the menu with the activity. In order to use a menu layout file, a resource file needs to be added:
First, we create a new XML file with the name of the menu, for example
Options.xml
, in themenu
folder under theResources
folder.Then, we create the menu structure in this file, for example, create three menu items:
refresh
,settings
, andabout
.<?xml version="1.0" encoding="utf-8" ?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_refresh" android:icon="@drawable/ic_action_refresh" android:title="@string/action_refresh" /> <item android:id="@+id/action_settings" android:title="@string/action_settings" /> <item android:id="@+id/action_about" android:title="@string/action_about"/> </menu>
Once we have the structure, we override the
OnCreateOptionsMenu()
method and inflate the resource:public override bool OnCreateOptionsMenu(IMenu menu) { MenuInflater.Inflate(Resource.Menu.Options, menu); return true; }
If we want to respond to items being selected in that menu, all we need to do is override the
OnOptionsItemSelected()
method:public override bool OnOptionsItemSelected(IMenuItem item) { if (item.ItemId == Resource.Id.action_refresh) { // do something here... return true; // we handled the event } return base.OnOptionsItemSelected(item); }
Menus, especially the options menu, are both simple and important to Android apps. The Options menu contains items that are relevant to the current activity. They are important, but they are often not commonly used and so don't have a dedicated space in the layout.

An Android screen with an options menu at the bottom
As with traditional layout files, using resource files for menus allows greater flexibility for the many screen configurations as well as for simplifying customizations to menus.
Each menu item contains a unique ID, which allows the system to recognize the item when the user selects it, and a title, which is used to present the item to the user. There are also additional properties, the most commonly used of these being the icon. When using action bars, this icon is used to display an image alongside, or in place of, the title.
The MenuInflater
instance creates the menu structure from the resource file and inflates it into the IMenu
instance. All the menu items in the resource will be added as children to the menu.
Note
The OnCreateOptionsMenu()
method should return true
if the menu is to be displayed. Returning false
will result in the menu not being displayed.
When we handle the menu item selections, the menu item that was selected is passed into the OnOptionsItemSelected()
method. If the event was handled, true
should be returned; otherwise, the system will keep on processing the event.
We can use any of the properties on the menu item, but one of the more commonly used ones is ItemId
, which contains the ID that was used in the resource file. This ID can be used to determine which item was selected.
As the Android operating system evolves, many new features are added and older devices are often left behind.
In order to add the new features of the later versions of Android to the older versions of Android, all we need to do is add a small package:
An Android app has three platform versions to be set. The first is the API features that are available to code against. We set this to always be the latest in the Target Framework dropdown of the project options.
The next version to set (via Minimum Android version) is the lowest version of the OS that the app can be installed on. When using the support libraries, we can usually target versions down to version 2.3.
Lastly, the Target Android version dropdown specifies how the app should behave when installed on a later version of the OS. Typically, this should always be the latest version so the app will always function as the user expects.
If we want to add support for the new UI paradigm that uses fragments and action bars, we need to install two of the Android support packages:
Create or open a project in Xamarin Studio.
Right-click on the project folder in the Solution Explorer list.
Select Add and then Add Packages….
In the Add Packages dialog that is displayed, search for Xamarin.Android.Support.
Select both Xamarin Support Library v4 and Xamarin Support Library v7 AppCompat.
Click on Add Package.
There are several support library packages, each adding other types of forward compatibility, but these two are the most commonly used.
Once the packages are installed, our activities can now inherit from the
AppCompatActivity
type instead of the usualActivity
type:public class MyActivity : AppCompatActivity { }
Finally, we specify that the activity theme be one of the
AppCompat
derivatives using theTheme
property in the[Activity]
attribute:[Activity(..., Theme = "@style/Theme.AppCompat", ...)]
As Android is developed, new features are being added and designs change. We want to always provide the latest features to our users, but some users either haven't or can't upgrade to the latest version of Android. By including the Android Support Libraries in our app, we can make use of the new features, but still support the old versions.
Note
Types from the Android Support Library are available to almost all versions of Android currently in use.
Xamarin.Android provides three version numbers to specify what and how types can be used. The target framework version specifies what types are available for consumption as well as what toolset to use during compilation. This should be the latest as we always want to use the latest tools.
However, this will make some types and members available to apps even if they aren't actually available on the Android version that the user is using. For example, it will make the ActionBar
type available to apps running on Android version 2.3. If the user were to run the app, it would probably crash.
In these instances, we can set the minimum Android version to be a version that supports these types and members. But, this will then reduce the number of devices that we can install our app on. This is why we use the support libraries; they allow the types to be used on most versions of Android.
Tip
Setting the minimum Android version for an app will prevent the app from being installed on devices with earlier versions of the OS.
The Android support libraries provide us with a type that we know we can use everywhere, and then that base type manages the features to make sure they function as expected. For example, we can use the ActionBar
type on most versions of Android because the support library made it available through the AppCompatActivity
type.
Because the AppCompatActivity
type is an adaptive extension for the traditional Activity
type, we have to use a different theme. This theme adjusts so that the new look and feel of the UI gets carried all the way back to the old Android versions.
Note
When using the AppCompatActivity
type, the activity theme must be one of the AppCompat
theme variations.
There are a few differences when using the support library. With native support for the action bar, the AppCompatActivity
type has a property named ActionBar
; however, in the support library, the property is named SupportActionBar
. This is just a property name change, but the functionality is the same.
Sometimes, features have to be added to the existing types that are not in the support libraries. In these cases, static methods are provided. The native support for custom views in menu items includes a method named SetActionView
:
menuItem.SetActionView(someView);
This method does not exist on the IMenuItem
type for the older versions of Android, so we make use of the static method on the MenuItemCompat
type:
MenuItemCompat.SetActionView(menuItem, someView);
Besides using the Android Support Libraries to handle different versions, there is another way to handle different versions at runtime. Android provides us with the version number of the current operating system through the Build.VERSION
type.
This type has a property, SdkInt
, which we can use to detect the current version. It represents the current API level of the version. Each version of Android has received a series of updates and new features. For example, Android 4 has received numerous updates since its initial release, new features being added each time.
Sometimes the support library cannot cover all the cases, and we will have to write specific code for particular versions:
int apiLevel = (int)Build.VERSION.SdkInt; if (Build.VERSION.SdkInt >= BuildVersionCodes.IceCreamSandwich) { // Android version 4.0 and above } else { // Android versions below version 4.0 }
Although this can be done, it introduces spaghetti code and should be avoided. In addition to different code, the app may behave differently on different versions, even if the support library could have handled it. We will now have to manage these differences ourselves each time a new version of Android is released.
Almost all apps require some form of commanding, usually being frequently used. As a result, these commands should be presented in an easily consumed region of the screen, regardless of differences in screen configuration.
Adding an action bar is very simple and does not need many changes to our app, even if they are to run on the old versions of Android:
By default, on Android 4.0, apps will have an action bar. To access this, we can use the
ActionBar
property on theActivity
type:ActionBar.Title = "Xamarin Cookbook";
To provide an action bar to previous versions of Android, we use the Android Support Libraries:
First, we need to install the Xamarin Support Library v7 AppCompat Component or NuGet.
Then, we need to ensure our activities inherit from the
AppCompatActivity
type instead of the usualActivity
type:public class MyActivity : AppCompatActivity { }
Next, we add the
Theme
property to the[Activity]
attribute:[Activity(..., Theme = "@style/Theme.AppCompat")]
Finally, if we need to access the
ActionBar
instance, it is available via theSupportActionBar
property on the activity:SupportActionBar.Title = "Xamarin Cookbook";
Certain commands are used very frequently in an app. These commands are often the main set of actions available to the current app screen. Because these commands are so important, they have a dedicated area in the app, often at the top of the screen. In a to-do list app, this might be the action to add a new task. In a shopping app, this might be the option to search for a product.

An Android screen with an action bar at the top
While adding an action bar on older Android versions, it is important to inherit it from the AppCompatActivity
type. This type includes all the logic required for including an action bar in the app. It also provides many different methods and properties for accessing and configuring the action bar. In newer versions of Android, all the features are included in the Activity
type.
Although the functionality is the same, we do have to access the various pieces using the support members when using the support libraries. An example would be to use the SupportActionBar
property instead of the ActionBar
property. If we use the ActionBar
property, the app will crash on devices that don't natively support the ActionBar
property.
In order to render the action bar, the activity needs to use a theme that contains a style for the action bar or one that inherits from such a theme. For the older versions of Android, we can use the AppCompat
themes, such as Theme.AppCompat
.
With the release of Android version 5.0, Google introduced a new style of action bar. The new Toolbar
type performs the same function as the action bar but can be placed anywhere on the screen. The action bar is always placed at the top of the screen, but toolbar is not restricted to that location and can even be placed inside other layouts.
To make use of the Toolbar
type, we can either use the native type, or we can use the type found in the support libraries. Like any Android View, we can add the ToolBar
type to the layout:
<android.support.v7.widget.Toolbar android:id="@+id/my_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" android:theme="@style/ThemeOverlay.AppCompat.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
The difference is in how the activity is set up. First, as we are not going to be using the default ActionBar
property, we can use the Theme.AppCompat.NoActionBar
theme. Then, we have to let the activity know which view is the Toolbar
type:
var toolbar = FindViewById<Toolbar>(Resource.Id.toolbar); SetSupportActionBar(toolbar);
The action bar is used to allow the user to navigate to a parent activity, as well as show the user where they are in the app.
Navigation with the action bar is an upward navigation, rather than a backward navigation. This navigation is very simple to add and involves only two steps. If we support versions of Android versions below 4.1, we will make use of the support library.
First, we need to ensure that our source activity is accessible using a known name by adding a
[Register]
attribute:[Register("com.xamarincookbook.MainActivity")] public class MainActivity : AppCompatActivity { }
Next, we let the system know which activity we want to navigate up to using a
[MetaData]
attribute:[MetaData( "android.support.PARENT_ACTIVITY", Value = "com.xamarincookbook.MainActivity")] public class RecipeDetailsActivity : AppCompatActivity { }
Then in the child activity, we let the action bar know that we want to allow upward navigation:
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
If the Android version is 4.1 and above, we use the native types and members:
First, we set the
ParentActivity
property on the[Activity]
attribute:[Activity (ParentActivity = typeof(MainActivity))] public class RecipeDetailsActivity : Activity { }
Then, we let the child activity's action bar know that we want to allow upward navigation:
ActionBar.SetDisplayHomeAsUpEnabled(true);
The action bar can facilitate direct navigation in two ways: navigating up to the parent activity and navigating down to a child activity. Navigating down is often done by adding action items to the action bar.
Action bar automatically navigates up to the parent activity when the user taps the icon, which is different from the traditional back navigation. The up navigation within an app is based on the hierarchical relationships between activities, that is, navigation to the parent activity. The back navigation is navigation back through the history of activities, in reverse chronological order.
Tip
If an activity is the topmost one in an app and it does not have a parent activity, it should not present an up button.
Sometimes the back navigation is the same as the up navigation. This happens when the previously viewed screen is also the hierarchical parent of the current screen. However the up navigation will keep the user in the app, but back navigation may return the user to the home screen or another app.
When adding the [MetaData]
attribute to the activity, we need to reference the final compiled name of the parent activity. Xamarin.Android mangles the final name of the types to avoid possible conflicts, so we have to let the compiler know exactly what name to use. We do this using a [Register]
attribute on the parent activity, and we then use the same value for the value component of the metadata.
The action bar lets the user know where they are in the app by using the action bar's title, which is usually the current activity's label. This can be customized by assigning a new string
value to the Title
property on the ActionBar
instance.
Sometimes the up navigation will take the user to different parent activities, depending on how the user arrived at the current activity. In these cases, we override several members in our activity. If our app is not going to have the activity instantiated on any other apps, we only need to override the SupportParentActivityIntent
or ParentActivityIntent
properties:
public override Intent SupportParentActivityIntent { get {return new Intent(this, typeof(MainActivity));} }
If our activity is going to be used by other apps, we also need to override the OnCreateNavigateUpTaskStack()
or OnCreateSupportNavigateUpTaskStack()
method.
The fundamental purpose of an action bar, besides navigation, is to present the user with a set of actions that can be performed.
By simply using the action bar, all the action items are added to the overflow:
The XML for ActionBar items is exactly the same as the options menu:
<menu ... > <item android:id="@+id/action_refresh" android:icon="@drawable/ic_action_refresh" android:title="@string/action_refresh"/> </menu>
However, we can customize what items are displayed, and how they are displayed:
To add action items with images to the actual
ActionBar
property, as well as more complex items, all that is needed is an attribute in the XML,showAsAction
:<menu ... xmlns:app="http://schemas.android.com/apk/res-auto"> <item ... app:showAsAction="ifRoom"/> </menu>
If we wish to add custom views, such as a search box, to the action bar, we make use of the
actionViewClass
attribute:<menu ... xmlns:app="http://schemas.android.com/apk/res-auto"> <item ... app:actionViewClass="android.support.v7.widget.SearchView"/> </menu>
If the view is in a layout resource file, we use the
actionLayout
attribute:<menu ... xmlns:app="http://schemas.android.com/apk/res-auto"> <item ... app:actionLayout="@layout/action_rating"/> </menu>
Sometimes, we may wish to only display the icon initially and then, when the user taps the icon, expand the item to display the action view:
<menu ... xmlns:app="http://schemas.android.com/apk/res-auto"> <item ... app:showAsAction="ifRoom|collapseActionView"/> </menu>
Action item buttons are just traditional options menu items but are optionally always visible on the action bar.
The underlying logic to handle item selections is the same as that for the traditional options menu. No change is required to existing code inside the OnOptionsItemSelected()
method.
The value of the showAsAction
attribute can be ifRoom
, never
, or always
. This value can optionally be combined, using a pipe, with withText
and/or collapseActionView
.
Some controls or regions in the user interface allow for additional actions to be performed. However, due to limited screen space, these actions need to be hidden until the user requests them.
The first thing that needs to be done is to let the activity or fragment know that we want to display a popup menu when the user long-taps on a view:
this.RegisterForContextMenu(someView);
Then, following the pattern of the options menu, we create or inflate the menu items in the
OnCreateContextMenu()
method:public override void OnCreateContextMenu( IContextMenu menu, View view, IContextMenuContextMenuInfo menuInfo) { base.OnCreateContextMenu(menu, view, menuInfo); MenuInflater.Inflate(Resource.Menu.Main_Options, menu); }
Lastly, we can respond to item selections, as we did with the options menu, in the
OnContextItemSelected()
method:public override bool OnContextItemSelected(IMenuItem item) { if (item.ItemId == Resource.Id.action_refresh) { // do something here... return true; } return base.OnContextItemSelected(item); }
We can provide a context menu for any view, but they are most often used for items in a list, grid, or other view collection. One way to show a contextual menu is to use a floating or pop-up menu, and it is the recommended way for apps supporting versions of Android below version 3.0.
Tip
If the views or list view is not registered with its activity or fragment, the context menu will not be displayed, even if the methods are implemented.
When the user long-taps on a view that has been registered for a context menu, the activity or fragment attempts to display a menu that is created or inflated in the OnCreateContextMenu()
method.
After the user selects an item from the contextual menu, the OnContextItemSelected()
method on the activity or fragment is invoked. In this method, we initiate the desired operation that was selected. We can identify the selected item using the ItemId
property.
Using the IContextMenu
instance that is passed into the OnCreateContextMenu()
method, we can change or remove the header of the popup menu. The header could be a combination of an icon and/or text or a separate custom view:
menu.SetHeaderTitle("My Context Menu Heading");
The action bar provides the user with a set of actions; however, these actions are usually just the most commonly used. Sometimes, the context of the app changes, so the presented actions need to adjust to what is commonly used in the new context.
There are a few steps to implementing a contextual action bar:
The first step is to implement the
ActionMode.ICallback
interface. This interface can either be implemented in a new, separate class or on the actual activity or fragment:public class MainActivity : AppCompatActivity, ActionMode.ICallback { public bool OnCreateActionMode( ActionMode mode, IMenu menu) { } public bool OnPrepareActionMode( ActionMode mode, IMenu menu) { } public bool OnActionItemClicked( ActionMode mode,IMenuItem item) { } public void OnDestroyActionMode(ActionMode mode) { } }
In the
OnCreateActionMode()
method, we create the menu as we would any options menu:public bool OnCreateActionMode(ActionMode mode, IMenu menu) { mode.MenuInflater.Inflate(Resource.Menu.options, menu); return true; }
Because we are not going to be updating the action mode once displayed, we can return
false
in theOnPrepareActionMode()
method:public bool OnPrepareActionMode( ActionMode mode, IMenu menu) { return false; }
We handle any item selections in the
OnActionItemClicked()
method:public bool OnActionItemClicked( ActionMode mode,IMenuItem item) { if (item.ItemId == Resource.Id.action_refresh) { // do something here... return true; } return false; }
We don't need to do anything when we leave action mode, so we leave the
OnDestroyActionMode()
method empty:public void OnDestroyActionMode(ActionMode mode) { }
An instance of
ActionMode.ICallback
is passed to theStartSupportActionMode()
orStartActionMode()
methods:someView.LongClick += (sender, e) => { if (actionMode == null) { // start the action mode actionMode = StartSupportActionMode(this); someView.Selected = true; } };
The contextual action mode menu is actually a separate action bar-like UI element that overlays but does not replace the actual action bar.
Tip
Contextual menu items do not need to have the showAsAction
attribute set in order to be displayed (it is ignored); by default, everything is visible.
This menu provides a set of commands that can be displayed based on some context, usually after a selection of an item on the screen. Selections are usually done after a long-tap, similar to traditional context menus; however, instead of a popup, the action bar is transformed. This provides a consistent interface without disrupting the flow of the task.
In order to enter action mode, we invoke the StartSupportActionMode()
method. If we are not using the support libraries, we invoke the StartActionMode()
method. This will return an ActionMode
instance, which can then be used to customize the appearance of the action mode overlay.
When entering action mode, the OnCreateActionMode()
method is invoked. Here we create the various action items that will be presented on the actions bar.
The OnPrepareActionMode()
method is invoked whenever the action mode changes or is invalidated. This method allows us to optionally modify the UI. We must return true
if any changes were made and false
if nothing was modified.
When the user selects an action item, the OnActionItemClicked()
method will be invoked. The current action mode and the selected item are provided so that we can perform the task.
If we are using lists and supporting Android 3.0 and above, there is an extra feature that we can make use of: multiple selections. There is currently no native support for a similar functionality on older versions of Android; however, there is no reason why we can't use this feature on newer Android versions.
Implementing this requires a few extra steps but is actually an extension of the normal contextual action mode. Instead of implementing the ActionMode.ICallback
interface, implement the AbsListView.IMultiChoiceModeListener
interface (which actually derives from ActionMode.ICallback
). This adds one extra method:
public void OnItemCheckedStateChanged(
ActionMode mode, int position, long id, bool isChecked) {
// handle item selections and deselections
}
And finally, we let the list view know about the multiselect availability by passing the instance. This is done instead of registering the context menu for floating menus:
listView.ChoiceMode = ChoiceMode.MultipleModal; listView.SetMultiChoiceModeListener(this);
One of the major reasons for using Xamarin.Android is the use of C# as the programming language. But this is not the only benefit as that same C# code can be shared with other platforms, such as iOS, Windows, or Mac OS.
First, we are going to create the project structure that we are going to use to create our cross-platform app. In this example, we will only target Xamarin.Android and the Console, but extra platforms can be added.
Open Xamarin Studio or Visual Studio and create an empty solution. For example, we are going to call this one
XamarinCodeSharing
.In this solution, create three projects:
An Android project named
XamarinCodeSharing.Droid
A console application named
XamarinCodeSharing.Desktop
A portable class library named
XamarinCodeSharing.Core
Open the project settings for
XamarinCodeSharing.Core
. If you are using Xamarin Studio, navigate to Build | General. Or if you are using Visual Studio, navigate to Library and then click on Change under the Targeting section.Note the various platform options available, including iOS, Android, Windows, and several others, some with version options. Ensure that the .NET4.5 and the Xamarin.Android boxes are selected as these are the platforms we are going to need.
To make things easier, we are going to use a
NuGet
package,Microsoft.Net.Http
, which simplifies the process of using HTTP and REST services. Install this package into each of the three projects.Add a project reference from
XamarinCodeSharing.Droid
toXamarinCodeSharing.Core
and a project reference fromXamarinCodeSharing.Desktop
toXamarinCodeSharing.Core
.Now that we have our project structure in place, we are going to write some code that will access the web service, and then see how that code is reused without modification or even recompilation. What we are going to do next all takes place in the
XamarinCodeSharing.Core
project.To make things easy, we can just delete the file that the IDE created. Xamarin Studio created a file named
MyClass.cs
, and Visual Studio created a file namedClass1.cs
.We can now create a new class named
BlogItem
, which will represent the actual blog entries. This bit is very simple and is just a set of properties:using System; namespace XamarinCodeSharing.Core { public class BlogItem { public string Title { get; set; } public string Link { get; set; } public DateTime PublishDate { get; set; } public string Description { get; set; } } }
In the same project, create another new class named
XamarinBlog
, which both represents the blog as well as provides a means to download the blog:using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Xml.Linq; using System.Net; namespace XamarinCodeSharing.Core { public class XamarinBlog { private const string BlogUrl = "http://blog.xamarin.com/feed"; // blog metadata properties public string Title { get; set; } public string Link { get; set; } public List<BlogItem> Items { get; private set; } // Download the feed, parse it and return a blog object public static async Task<XamarinBlog> Download() { HttpClient client = new HttpClient(); HttpResponseMessage response = await client.GetAsync(BlogUrl); // if all went well, read the feed, otherwise fail if(response.IsSuccessStatusCode) { return await ParseResponse(response.Content); } else { throw new Exception("There was a problem."); } } // Read the response out of the content and // create objects private static async Task<XamarinBlog>ParseResponse( HttpContent content) { XamarinBlog blog = new XamarinBlog(); using(Stream stream = await content.ReadAsStreamAsync()) { XDocument doc = XDocument.Load(stream); XElement channel = doc.Root.Element("channel"); // load the blog metadata out of the xml blog.Title = WebUtility.HtmlDecode( channel.Element("title").Value); blog.Link = WebUtility.HtmlDecode( channel.Element("link").Value); // load the blog items out of the xml var items = from item in channel.Elements("item") select new BlogItem { Title = WebUtility.HtmlDecode( item.Element("title").Value), Link = WebUtility.HtmlDecode( item.Element("link").Value), PublishDate = DateTime.Parse( WebUtility.HtmlDecode( item.Element("pubDate").Value)), Description = WebUtility.HtmlDecode( item.Element("description").Value), }; blog.Items = items.ToList(); } return blog; } } }
There are several important points to note here. We are using the
async
andawait
keywords to make asynchronous code easier to read, something which is not available in Java. Another feature is LINQ to XML to make working with XML easier to parse, another feature not available to Java. And finally,WebUtility.HtmlDecode
is used as all the data is HTML encoded inside the XML.
Now that we have created the main logic of the app, we can look at those native implementations. First, we will create the Console app that will run on almost any desktop operating system, such as Windows, Mac OS, and most of the flavors of Linux:
To do this, we can just replace the code in the
Program.cs
file with the following:using System; using XamarinCodeSharing.Core; using System.Threading.Tasks; namespace XamarinCodeSharing.Desktop { class MainClass { public static void Main(string[] args) { GetBlogItems().Wait(); Console.WriteLine("Press any key to quit."); Console.ReadKey(); } private static async Task GetBlogItems() { Console.WriteLine("Downloading blog..."); XamarinBlog blog = await XamarinBlog.Download(); Console.WriteLine("Download finished."); Console.WriteLine(); Console.WriteLine("{0} ({1} items)", blog.Title, blog.Items.Count); foreach (var item in blog.Items) { Console.WriteLine(item.Title); Console.WriteLine( item.PublishDate.ToString("d MMMM yyyy")); Console.WriteLine(item.Description); Console.WriteLine(); } } } }
If we run the desktop project now, the console will start up, download the blog feed, and then display each entry along with a short summary. Last but not least, we want to get that mobile version going. And what better platform to support first than Android?
Switching to the Xamarin.Android project, we can replace the code in the
MainActivity.cs
file with the following:using System.Collections.Generic; using System.Linq; using Android.App; using Android.OS; using Android.Runtime; using Android.Widget; using XamarinCodeSharing.Core; namespace XamarinCodeSharing.Droid { [Activity( Label = "XamarinCodeSharing.Droid", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : ListActivity { private const string TitleKey = "title"; private const string SubtitleKey = "subtitle"; protected async override void OnCreate(Bundle bundle) { base.OnCreate(bundle); var progress = new ProgressDialog(this); progress.SetTitle("Downloading blog..."); progress.SetMessage( "Please wait while we download the Xamarin blog..."); progress.Show(); XamarinBlog blog = await XamarinBlog.Download(); var items = from item in blog.Items select new JavaDictionary<string, object> { { TitleKey, item.Title }, { SubtitleKey, item.Description } }; ListAdapter = new SimpleAdapter( this, items.Cast<IDictionary<string, object>>().ToList(), Android.Resource.Layout.SimpleListItem2, new []{ TitleKey, SubtitleKey }, new []{ Android.Resource.Id.Text1, Android.Resource.Id.Text2 }); progress.Dismiss(); progress.Dispose(); } } }
If we run this Android app now, we shall get that app that downloads the latest Xamarin blog posts and displays them in a neat list.
Portable class libraries is one of the most important aspects of code reuse. Although there are many different ways to reuse code, such as file linking or shared projects, none can match the reliability and ease of simply reusing the compiled assembly. This assembly can be tested and verified and then used and reused without fear of problems arising on different platforms.
Why? It is because each platform in each portable class library profile promises to support all the features, for all platforms, in that profile, and all those features function exactly the same.
Using Xamarin.Android, we now have all the features of the .NET runtime available, the same runtime that runs on almost all devices. Along with that same runtime, we can now build libraries that run on that runtime, and those libraries will run on all platforms that have the same runtime. Those platforms include iOS, Android, Windows Phone, Mac OS, Windows, Linux, and even more, such as PlayStation!
Although we can never achieve 100 percent code reuse, as each platform has differences (especially in the user interface), much of the code lives below the surface and can be reused. And using the commonality of the runtime and languages, we can create abstractions that can work around the platform differences, resulting in even more code reuse.
Although we only covered two platforms in this example, we can already see that it takes more code to work with the data than it does to display it. Adding extra platforms, such as Windows Phone and iOS, will result in this ultra-simple example app being able to support the major mobile operating systems, with the only difference being the UI.