Mono for Android 實現(xiàn)高效的導(dǎo)航(Effective Navigation)
Android 4.0 系統(tǒng)定義了一系列的高效導(dǎo)航方式 (Effective Navigation), 主要包括標(biāo)簽、下拉列表、以及向上和返回等, 本文介紹如何用 Mono for Android 實現(xiàn)這些的導(dǎo)航方式。
準(zhǔn)備 Android 4.0 ICS 項目
新建 Android ICS 項目
打開 MonoDevelop , 新建一個 Mono for Android 項目, 并在項目的屬性頁將 Target Framework 設(shè)置為 Android 4.0.3 (Ice Cream Sandwich)
, 如下圖所示:
添加 Mono.Android.Support.v4 引用項
在解決方案窗口, 選中項目的引用節(jié)點(diǎn), 右擊選擇編輯引用, 添加對 Mono.Android.Support.v4.dll
的引用, 如圖所示:
在項目中新建一個目錄 SupportLib , 并添加對 android-support-v4.jar 文件(位于 android-sdk/extras/android/support/v4 目錄, 如果沒有, 需要用 SDK Manager 安裝)的引用, 并將 jar 文件的編譯動作 (BuildAction) 設(shè)置為 AndroidJavaLibrary , 如下圖所示:
本文提到的導(dǎo)航都是根據(jù) Android 4.0 設(shè)計規(guī)范中推薦的 ActionBar 實現(xiàn)的, 因此整個應(yīng)用程序啟用帶 ActionBar 的主題, 如果使用 Java 的話, 需要手工編輯 AppManifest.xml 文件的設(shè)置, 而用 Mono for Android 的話, 基本上不需要手工編輯這個文件。
Mono for Android 的做法是, 新建一個 App 類, 繼承自 Android.App.Application
類, 并添加 Android.App.ApplicationAttribute
標(biāo)記, 在編譯時, Mono for Android 會根據(jù)這些標(biāo)記自動生成一個 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 將默認(rèn)都是用這個主題, 如果有 Activity 要使用其它的主題, 才需要添加自己的主題屬性。
標(biāo)簽導(dǎo)航
Android 的標(biāo)簽用 ActionBar 實現(xiàn), 用戶既可以點(diǎn)擊標(biāo)簽切換視圖, 也可以水平滑動切換視圖, 如下圖所示:
用戶既可以點(diǎn)擊上面的 ‘SECTION 0'、 ‘SECTION 1'、 ‘SECTION 2' 標(biāo)簽切換視圖, 也可以在視圖上水平拖動切換視圖, 同時標(biāo)簽選中項也要同步選中, 實現(xiàn)的代碼如下:
[Activity (Label = "@string/AppName", Icon = "@drawable/ic_launcher", MainLauncher = true)]
public class MainActivity : FragmentActivity {
/// <summary>
/// AppSectionsPagerAdapter 提供要顯示的視圖, 繼承自
/// Mono.Android.Support.V4.View.PagerAdapter, 所有加載過視圖都保存在內(nèi)存中,
/// 如果視圖占用內(nèi)存過多, 考慮替換成 FragmentStatePagerAdapter 。
/// </summary>
AppSectionsPagerAdapter _appSectionsPagerAdapter;
/// <summary>
/// 用 ViewPager 來顯示視圖三個主視圖, 每次只顯示一個。
/// </summary>
ViewPager _viewPager;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
this.SetContentView(Resource.Layout.MainActivity);
// 創(chuàng)建 Adapter
this._appSectionsPagerAdapter = new AppSectionsPagerAdapter(this.SupportFragmentManager);
// 設(shè)置 ActionBar
var actionBar = this.ActionBar;
// 首頁不需要向上的 Home 按鈕
actionBar.SetHomeButtonEnabled(false);
// 設(shè)置標(biāo)簽導(dǎo)航模式
actionBar.NavigationMode = ActionBarNavigationMode.Tabs;
// 設(shè)置 ViewPager 的 Adapter , 這樣用戶就可以水平滑動切換視圖了
this._viewPager = this.FindViewById<ViewPager>(Resource.Id.Pager);
this._viewPager.Adapter = this._appSectionsPagerAdapter;
// 當(dāng)水平滑動切換視圖時, 設(shè)置選中的標(biāo)簽
this._viewPager.PageSelected += delegate(object sender, ViewPager.PageSelectedEventArgs e) {
actionBar.SetSelectedNavigationItem(e.P0);
};
// 依次添加三個標(biāo)簽, 并添加標(biāo)簽的選中事件處理函數(shù), 設(shè)置當(dāng)前的視圖。
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);
}
}
}
左右導(dǎo)航
標(biāo)簽導(dǎo)航并不適合所有的場景, 有時僅僅需要顯示視圖的標(biāo)題即可, 但是同樣可以水平滑動切換視圖, 如下圖所示:
這種導(dǎo)航方式相當(dāng)于標(biāo)簽式導(dǎo)航的簡化版, 用戶只可以左右滑動切換視圖, 實現(xiàn)的代碼如下:
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
this.SetContentView(Resource.Layout.CollectionDemoActivity);
// 創(chuàng)建 Adapter
this._demoCollectionPagerAdapter = new DemoCollectionPagerAdapter(this.SupportFragmentManager);
// 設(shè)置 ViewPager 的 Adapter
this._viewPager = this.FindViewById<ViewPager>(Resource.Id.Pager);
this._viewPager.Adapter = this.mDemoCollectionPagerAdapter;
}
因為要顯示標(biāo)題, 所以這個 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 即可顯示選中頁面的標(biāo)題, 也顯示臨近選中的幾個視圖的標(biāo)題
-->
<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>
下拉列表
下拉列表導(dǎo)航是在 ActionBar 中顯示一個下拉列表 (Spinner), 就像一個菜單, 只顯示選中的菜單項對應(yīng)的視圖, 如下圖所示:
將 ActionBar 設(shè)置為下拉列表導(dǎo)航時, 一般不顯示 Activity 自身的標(biāo)題, 因此需要將 Activity 的 Label 標(biāo)記為空字符串, 并且 Activity 需要實現(xiàn)接口 ActionBar.IOnNavigationListener
, ListNavigationActivity 的部分實現(xiàn)代碼如下:
[Activity (Label = "")]
public class ListNavigationActivity
: FragmentActivity, ActionBar.IOnNavigationListener {
ListNavSectionsPagerAdapter _navSectionsPagerAdapter;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
/* 其他代碼省略 … */
// 設(shè)置 ActionBar
var actionBar = this.ActionBar;
// 將 Home 設(shè)置為向上
actionBar.SetDisplayHomeAsUpEnabled(true);
// 設(shè)置 ActionBar 的導(dǎo)航模式為下拉列表
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);
}
// 設(shè)置列表導(dǎo)航的回調(diào)參數(shù)
actionBar.SetListNavigationCallbacks(
new ArrayAdapter(
actionBar.ThemedContext,
Resource.Layout.ListNavigationActivityActionbarListItem,
Android.Resource.Id.Text1,
titles
),
this
);
// 設(shè)置 ViewPager
this._viewPager = this.FindViewById<ViewPager>(Resource.Id.Pager);
this._viewPager.Adapter = this._navSectionsPagerAdapter;
// 當(dāng) ViewPager 的選中頁切換時, 同步 actionBar 的選中項。
this._viewPager.PageSelected += delegate(object sender, ViewPager.PageSelectedEventArgs e) {
actionBar.SetSelectedNavigationItem(e.P0);
};
}
// ActionBar.IOnNavigationListener
public bool OnNavigationItemSelected(int itemPosition, long itemId) {
this._viewPager.CurrentItem = itemPosition;
return true;
}
}
向上導(dǎo)航
所謂的向上導(dǎo)航, 就是在 Activity 的圖標(biāo)上顯示一個向左的箭頭, 點(diǎn)擊圖標(biāo)返回應(yīng)用程序的上一級 Activity , 注意是上一級 Activity , 不是上一個 Activity , 關(guān)于向上與返回的區(qū)別, 可以看看 Android SDK 中的 Providing Ancestral and Temporal Navigation 一文, 將向上和返回講解的非常清楚, 在這里只討論 Mono for Android 的實現(xiàn)方式。
要顯示向上導(dǎo)航的按鈕, 需要在 OnCreate
方法中對 ActionBar 做如下設(shè)置:
// 設(shè)置 ActionBar
var actionBar = this.ActionBar;
// 將 Home 按鈕顯示為向上, 提示用戶點(diǎn)擊這個按鈕可以返回應(yīng)用程序的上一級。
actionBar.SetDisplayHomeAsUpEnabled(true);同時還需要重寫 OnOptionsItemSelected 方法, 當(dāng)用戶點(diǎn)擊 Home 按鈕時, 做相應(yīng)的處理, 實現(xiàn)向上導(dǎo)航的代碼如下:
public override bool OnOptionsItemSelected(Android.Views.IMenuItem item) {
// 作為示例, 只處理用戶點(diǎn)擊 Home 按鈕的情況。
if (item.ItemId == Android.Resource.Id.Home) {
// 當(dāng) Home 按鈕被點(diǎn)擊時會調(diào)用到這里
// 創(chuàng)建啟動上級 Activity 的 Intent
var upIntent = new Intent(this, typeof(MainActivity));
// 使用 Suport Package 中的 NavUtils 來正確處理向上導(dǎo)航
if (NavUtils.ShouldUpRecreateTask(this, upIntent)) {
// 上級 Activity 沒有起動過, 需要創(chuàng)建一個新的導(dǎo)航棧道
TaskStackBuilder.Create(this)
// If there are ancestor activities, they should be added here.
.AddNextIntent(upIntent)
.StartActivities();
this.Finish();
}
else {
// 上級 Activity 已經(jīng)創(chuàng)建過了, 直接導(dǎo)航就行。
NavUtils.NavigateUpTo(this, upIntent);
}
return true;
}
return base.OnOptionsItemSelected(item);
}
總結(jié)
Android 系統(tǒng)的導(dǎo)航與 iOS 相比復(fù)雜很多, 實現(xiàn)起來也相對麻煩一些, 好在有 Google 的 Support Package 已經(jīng)多大部分操作提供了比較好的封裝, 還是比較容易掌握的。 文中的完整的源代碼已經(jīng)提交的 Github 上, 地址是 https://github.com/beginor/MonoDroid/tree/master/EffectiveNavigation 。
相關(guān)文章
Android LayoutInflater加載布局詳解及實例代碼
這篇文章主要介紹了Android LayoutInflater加載布局詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02Java Base64位編碼與String字符串的相互轉(zhuǎn)換,Base64與Bitmap的相互轉(zhuǎn)換實例代碼
這篇文章主要介紹了Java Base64位編碼與String字符串的相互轉(zhuǎn)換,Base64與Bitmap的相互轉(zhuǎn)換實例代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-03-03Android實現(xiàn)自動點(diǎn)擊無障礙服務(wù)功能的實例代碼
這篇文章主要介紹了Android實現(xiàn)自動點(diǎn)擊無障礙服務(wù)功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04Handler實現(xiàn)線程之間的通信下載文件動態(tài)更新進(jìn)度條
每一個線程對應(yīng)一個消息隊列MessageQueue,實現(xiàn)線程之間的通信,可通過Handler對象將數(shù)據(jù)裝進(jìn)Message中,再將消息加入消息隊列,而后線程會依次處理消息隊列中的消息。這篇文章主要介紹了Handler實現(xiàn)線程之間的通信下載文件動態(tài)更新進(jìn)度條,需要的朋友可以參考下2017-08-08