Android 4.0 系統定義了一系列的高效導航方式 (Effective Navigation), 主要包括標籤、下拉式清單、以及向上和返回等, 本文介紹如何用 Mono for Android 實現這些的導航方式。
準備 Android 4.0 ICS 項目建立 Android ICS 項目
開啟 MonoDevelop , 建立一個 Mono for Android 項目, 並在項目的屬性頁面將 Target Framework 設定為 Android 4.0.3 (Ice Cream Sandwich)
, 如所示:
添加 Mono.Android.Support.v4 引用項
在解決方案視窗, 選中項目的引用節點, 右擊選擇編輯引用, 添加對 Mono.Android.Support.v4.dll
的引用, :
在項目中建立一個目錄 SupportLib , 並添加對 android-support-v4.jar 檔案(位於 android-sdk/extras/android/support/v4 目錄, 如果沒有, 需要用 SDK Manager 安裝)的引用, 並將 jar 檔案的編譯動作 (BuildAction) 設定為 AndroidJavaLibrary , 如所示:
本文提到的導航都是根據 Android 4.0 設計規範中推薦的 ActionBar 實現的, 因此整個應用程式啟用帶 ActionBar 的主題, 如果使用 Java 的話, 需要手工編輯 AppManifest.xml 檔案的設定, 而用 Mono for Android 的話, 基本上不需要手工編輯這個檔案。
Mono for Android 的做法是, 建立一個 App 類, 繼承自 Android.App.Application
類, 並添加 Android.App.ApplicationAttribute
標記, 在編譯時間, Mono for Android 會根據這些標記自動產生一個 AppManifest.xml 檔案並打包到最終的 apk 檔案中。
App 類的代碼如下:
[Application(Label = "@string/AppName", Icon = "@drawable/ic_launcher", Theme = "@android:style/Theme.Holo.Light.DarkActionBar")]public class App : Application {public App(IntPtr javaReference, JniHandleOwnership transfer): base(javaReference, transfer) {}}
添加這個類之後, 項目中的每個 Activity 將預設都是用這個主題, 如果有 Activity 要使用其它的主題, 才需要添加自己的主題屬性。
標籤導航
Android 的標籤用 ActionBar 實現, 使用者既可以點擊標籤切換視圖, 也可以水平滑動切換視圖, 如所示:
使用者既可以點擊上面的 ‘SECTION 0’、 ‘SECTION 1’、 ‘SECTION 2’ 標籤切換視圖, 也可以在視圖上水平拖動切換視圖, 同時標籤選中項也要同步選中, 實現的代碼如下:
[Activity (Label = "@string/AppName", Icon = "@drawable/ic_launcher", MainLauncher = true)]public class MainActivity : FragmentActivity {/// <summary>/// AppSectionsPagerAdapter 提供要顯示的視圖, 繼承自/// Mono.Android.Support.V4.View.PagerAdapter, 所有載入過視圖都儲存在記憶體中, /// 如果視圖佔用記憶體過多, 考慮替換成 FragmentStatePagerAdapter 。/// </summary>AppSectionsPagerAdapter _appSectionsPagerAdapter;/// <summary>/// 用 ViewPager 來顯示視圖三個主視圖, 每次只顯示一個。/// </summary>ViewPager _viewPager;protected override void OnCreate(Bundle bundle) {base.OnCreate(bundle);this.SetContentView(Resource.Layout.MainActivity);// 建立 Adapterthis._appSectionsPagerAdapter = new AppSectionsPagerAdapter(this.SupportFragmentManager);// 設定 ActionBarvar actionBar = this.ActionBar;// 首頁不需要向上的 Home 按鈕actionBar.SetHomeButtonEnabled(false);// 設定標籤導航模式actionBar.NavigationMode = ActionBarNavigationMode.Tabs;// 設定 ViewPager 的 Adapter , 這樣使用者就可以水平滑動切換視圖了this._viewPager = this.FindViewById<ViewPager>(Resource.Id.Pager);this._viewPager.Adapter = this._appSectionsPagerAdapter;// 當水平滑動切換視圖時, 設定選中的標籤this._viewPager.PageSelected += delegate(object sender, ViewPager.PageSelectedEventArgs e) {actionBar.SetSelectedNavigationItem(e.P0);};// 依次添加三個標籤, 並添加標籤的選中事件處理函數, 設定當前的視圖。for (var i = 0; i < this._appSectionsPagerAdapter.Count; i++) {var tab = actionBar.NewTab().SetText(this._appSectionsPagerAdapter.GetPageTitle(i));tab.TabSelected += delegate(object sender, Android.App.ActionBar.TabEventArgs e) {this._viewPager.CurrentItem = tab.Position;};actionBar.AddTab(tab);}}}
左右導航
標籤導航並不適合所有的情境, 有時僅僅需要顯示視圖的標題即可, 但是同樣可以水平滑動切換視圖, 如所示:
這種導航方式相當於標籤式導航的簡化版, 使用者只可以左右滑動切換視圖, 實現的代碼如下:
protected override void OnCreate(Bundle bundle) {base.OnCreate(bundle);this.SetContentView(Resource.Layout.CollectionDemoActivity);// 建立 Adapterthis._demoCollectionPagerAdapter = new DemoCollectionPagerAdapter(this.SupportFragmentManager);// 設定 ViewPager 的 Adapterthis._viewPager = this.FindViewById<ViewPager>(Resource.Id.Pager);this._viewPager.Adapter = this.mDemoCollectionPagerAdapter;}
因為要顯示標題, 所以這個 Activity 的 Layout 添加了一個 PagerTitleStrip , Layout 原始碼如下:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/Pager"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><!--PaterTitleStrip 即可顯示選中頁面的標題, 也顯示臨近選中的幾個視圖的標題--><android.support.v4.view.PagerTitleStrip android:id="@+id/PagerTitleStrip"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="top"android:background="#33b5e5"android:textColor="#fff"android:paddingTop="4dp"android:paddingBottom="4dp" /></android.support.v4.view.ViewPager>
下拉式清單
下拉式清單導航是在 ActionBar 中顯示一個下拉式清單 (Spinner), 就像一個菜單, 只顯示選中的功能表項目對應的視圖, 如所示:
將 ActionBar 設定為下拉式清單導航時, 一般不顯示 Activity 自身的標題, 因此需要將 Activity 的 Label 標記為空白字串, 並且 Activity 需要實現介面 ActionBar.IOnNavigationListener
, ListNavigationActivity 的部分實現代碼如下:
[Activity (Label = "")]public class ListNavigationActivity: FragmentActivity, ActionBar.IOnNavigationListener {ListNavSectionsPagerAdapter _navSectionsPagerAdapter;protected override void OnCreate(Bundle bundle) {base.OnCreate(bundle);/* 其他代碼省略 … */// 設定 ActionBarvar actionBar = this.ActionBar;// 將 Home 設定為向上actionBar.SetDisplayHomeAsUpEnabled(true);// 設定 ActionBar 的導航模式為下拉式清單actionBar.NavigationMode = ActionBarNavigationMode.List;var titles = new string[this._navSectionsPagerAdapter.Count];for (var i = 0; i < titles.Length; i++) {titles[i] = this._navSectionsPagerAdapter.GetPageTitle(i);}// 設定列表導航的回調參數actionBar.SetListNavigationCallbacks(new ArrayAdapter(actionBar.ThemedContext,Resource.Layout.ListNavigationActivityActionbarListItem,Android.Resource.Id.Text1,titles),this);// 設定 ViewPagerthis._viewPager = this.FindViewById<ViewPager>(Resource.Id.Pager);this._viewPager.Adapter = this._navSectionsPagerAdapter;// 當 ViewPager 的選中頁切換時, 同步 actionBar 的選中項。this._viewPager.PageSelected += delegate(object sender, ViewPager.PageSelectedEventArgs e) {actionBar.SetSelectedNavigationItem(e.P0);};}// ActionBar.IOnNavigationListenerpublic bool OnNavigationItemSelected(int itemPosition, long itemId) {this._viewPager.CurrentItem = itemPosition;return true;}}
向上導航
所謂的向上導航, 就是在 Activity 的表徵圖上顯示一個向左的箭頭, 點擊表徵圖返回應用程式的上一級 Activity , 注意是上一級 Activity , 不是上一個 Activity , 關於向上與返回的區別, 可以看看 Android SDK 中的 Providing Ancestral and Temporal Navigation 一文, 將向上和返回講解的非常清楚, 在這裡只討論 Mono for Android 的實現方式。
要顯示向上導航的按鈕, 需要在 OnCreate
方法中對 ActionBar 做如下設定:
// 設定 ActionBarvar actionBar = this.ActionBar;// 將 Home 按鈕顯示為向上, 提示使用者點擊這個按鈕可以返回應用程式的上一級。actionBar.SetDisplayHomeAsUpEnabled(true);
同時還需要重寫 OnOptionsItemSelected
方法, 當使用者點擊 Home 按鈕時, 做相應的處理, 實現向上導航的代碼如下:
public override bool OnOptionsItemSelected(Android.Views.IMenuItem item) {// 作為樣本, 只處理使用者點擊 Home 按鈕的情況。if (item.ItemId == Android.Resource.Id.Home) {// 當 Home 按鈕被點擊時會調用到這裡// 建立啟動上級 Activity 的 Intentvar upIntent = new Intent(this, typeof(MainActivity));// 使用 Suport Package 中的 NavUtils 來正確處理向上導航if (NavUtils.ShouldUpRecreateTask(this, upIntent)) {// 上級 Activity 沒有起動過, 需要建立一個新的導航棧道TaskStackBuilder.Create(this)// If there are ancestor activities, they should be added here..AddNextIntent(upIntent).StartActivities();this.Finish();}else {// 上級 Activity 已經建立過了, 直接導航就行。NavUtils.NavigateUpTo(this, upIntent);}return true;}return base.OnOptionsItemSelected(item);}
總結
Android 系統的導航與 iOS 相比複雜很多, 實現起來也相對麻煩一些, 好在有 Google 的 Support Package 已經多大部分操作提供了比較好的封裝, 還是比較容易掌握的。 文中的完整的原始碼已經提交的 Github 上, 地址是 https://github.com/beginor/MonoDroid/tree/master/EffectiveNavigation 。