1. 程式人生 > >Xamarin.Android之Fragment Walkthrough

Xamarin.Android之Fragment Walkthrough

利用Fragment設計能夠相容不同螢幕的應用

這裡我們先圍觀下最後的成果圖,給讀者打打氣:

普通手機上顯示的結果:

在平板上顯示的結果:

筆者要鄭重宣告下,雖然看似是兩種不同的顯示效果,但是同一個應用,而下面筆者將逐步教會大家如何利用Fragment製作出能夠相容不同螢幕的應用。

準備工作

建立一個專案是必不可少的,並且Android SDK的版本要在3.0以上,建議是4.0因為筆者設定的就是4.0,新建完成之後專案會自動幫我們建立好MainActivity,當然靠這一個還不足夠,我們還要新建一個Activity,並命名為DetailsActivity,另外還有兩個Fragment

分為命名為DetailsFragmentTitlesFragment,最後的目錄結構應該如下圖所示:

建立相容檢視

現在我們先把呈現部分的功能完成,我們開啟Resources\Layout下的Main.axml將下面的xml程式碼寫入:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="horizontal"
 4     android:layout_width
="fill_parent" 5 android:layout_height="fill_parent"> 6 <fragment 7 class="fragmentwalkthrough.TitlesFragment" 8 android:id="@+id/titles_fragment" 9 android:layout_width="fill_parent" 10 android:layout_height="fill_parent" /> 11 </LinearLayout
>

這裡我們就利用了fragment作為佔位符,從而顯示TitlesFragment,筆者還要注意class的完整路徑,要按照自己實際專案的名稱來,一般都是解決方案的名字的小寫加上點然後就是對應的類名了。

光有這個檢視只能應付小螢幕的顯示,我們還需要為大螢幕設計一個檢視。但是我們不能在layout下繼續新建,那樣我們就要用程式碼負責控制了,其實Android本身就已經提供了這些功能,我們只要在Resources下新建一個資料夾並且命名為layout-large,然後在該資料夾下新建一個Main.axml,這裡的檢視檔案命名必須要和layout下的一致,然後將下面的內容寫入其中:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="horizontal"
 4     android:layout_width="fill_parent"
 5     android:layout_height="fill_parent">
 6     <fragment
 7         class="fragmentwalkthrough.TitlesFragment"
 8         android:id="@+id/titles_fragment"
 9         android:layout_weight="1"
10         android:layout_width="0px"
11         android:layout_height="match_parent" />
12     <FrameLayout
13         android:id="@+id/details"
14         android:layout_weight="3"
15         android:layout_width="0px"
16         android:layout_height="match_parent" />
17 </LinearLayout>

這裡多了一個FrameLayout這個就是作為內容的容器,最後我們可以看到在我們選擇不同的項的時候,都會在這個佔位符中切換碎片(Fragment),這裡提示下我們還要把MainActivity.cs中的自動生成的程式碼刪除,最後只要剩下以下的內容即可:

1         protected override void OnCreate(Bundle bundle)
2         {
3             base.OnCreate(bundle);
4             SetContentView(Resource.Layout.Main);
5         }

完成了上面的內容,我們下面就開始從下而上來開始。

完善DetailsFragment

唯一需要學習的就是OnCreateView方法,這個方法就是來用指定碎片的檢視的,最後顯示的是返回的檢視,具體的程式碼如下所示:

 1     public class DetailsFragment : Fragment
 2     {
 3         public static DetailsFragment NewInstance(int playId)
 4         {
 5             var detailsFrag = new DetailsFragment
 6             {
 7                 Arguments = new Bundle()
 8             };
 9             detailsFrag.Arguments.PutInt("current_play_id", playId);
10             return detailsFrag;
11         }
12 
13         public int ShowPlayId
14         {
15             get
16             {
17                 return Arguments.GetInt("current_play_id", 0);
18             }
19         }
20 
21         public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
22         {
23             if (container == null)
24             {
25                 return null;
26             }
27             var scroller = new ScrollView(Activity);
28             var text = new TextView(Activity);
29             text.SetPadding(4, 4, 4, 4);
30             text.TextSize = 24;
31             text.Text = "you select " + ShowPlayId;
32             scroller.AddView(text);
33             return scroller;
34         }
35 }

我們僅僅只是對選擇的項的id儲存了,提供還提供了一個快捷方法NewInstance用來例項化這個碎片,最後的內容僅僅只是通過TextView呈現的,筆者後面也可以設計一個檢視,然後利用inflater引數例項化並返回。

完善DetailsActivity

這個活動純粹只是為了相容小螢幕的,因為它只是一個軀殼,負責將傳送給它的引數在轉發給DetailsFragment,並顯示DetailFragment。具體的程式碼如下所示:

 1     [Activity(Label = "DetailsActivity")]
 2     public class DetailsActivity : Activity
 3     {
 4         protected override void OnCreate(Bundle bundle)
 5         {
 6             base.OnCreate(bundle);
 7             var index = Intent.Extras.GetInt("current_play_id", 0);
 8             var details = DetailsFragment.NewInstance(index);
 9             var ft = FragmentManager.BeginTransaction();
10             ft.Add(Android.Resource.Id.Content, details);
11             ft.Commit();
12         }
13 }

我們注意到了FragmentManger這個類,它對於我們今後使用碎片都是非常重要的,只要在活動裡面切換碎片,刪除碎片等都要通過它。這一過程還必須要使用BeginTransaction先開啟事務,完成操作後還要通過Commit提交,否則是沒有效果的。

完善TitlesFragment

這裡我們看到了列表顯示的資料,而這些資料都是定義在Strings.xml中的,所以我們先要定義這些資源,程式碼如下所示:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3     <string name="Hello">Hello World, Click Me!</string>
 4     <string name="ApplicationName">FragmentWalkthrough</string>
 5   <string-array name="TitleList">
 6     <item>First</item>
 7     <item>Second</item>
 8     <item>Third</item>
 9     <item>Fourth</item>
10     <item>Fifth</item>
11     <item>Sixth</item>
12   </string-array>
13 </resources>

完成上面的操作後,我們就要重點學習TitlesFragment中的功能,首先我們刪除裡面預設重寫的方法,然後將繼承的類改成ListFragment,並重寫OnActivityCreated,因為我們繼承了這個類,就跟ListActivity一樣,所以不需要在設定介面。

為了能夠相容不同的螢幕,所以我們需要一個bool型別的變數去儲存當前的螢幕是屬於大還是小,從而決定相關的功能,並且還要有一個int型別的變數儲存當前所選的資料的id,然後我們就可以完善OnActivityCreated方法了:

 1         protected int _currentPlayId;
 2         protected bool _isDualPanel;
 3 
 4         public override void OnActivityCreated(Bundle savedInstanceState)
 5         {
 6             base.OnActivityCreated(savedInstanceState);
 7             //從Resources將資源取出
 8             string[] strarray = Resources.GetStringArray(Resource.Array.TitleList);
 9             
10             //例項化一個介面卡並將介面卡賦給ListAdapter
11             var adapter = new ArrayAdapter<string>(Activity, Android.Resource.Layout.SimpleListItemChecked, strarray);
12             ListAdapter = adapter;
13 
14             //判斷是否存在上一次會話的資料
15             if (savedInstanceState != null)
16             {
17                 _currentPlayId = savedInstanceState.GetInt("current_play_id", 0);
18             }
19 
20             //獲取用於碎片的佔位符
21             var detailsFrame = Activity.FindViewById<View>(Resource.Id.details);
22 
23             //根據該佔位符是否存在以及是否可見,從而決定是否為大螢幕
24             _isDualPanel = detailsFrame != null && detailsFrame.Visibility == ViewStates.Visible;
25 
26             //當前螢幕為大螢幕時操作
27             if (_isDualPanel)
28             {
29                 ListView.ChoiceMode = ChoiceMode.Single;
30                 ShowDetails(_currentPlayId);
31             }
32         }

這裡只是初始化了列表並判斷了當前屬於那種情況,下面我們就要介紹重要的ShowDetails方法,該方法將負責使用者點選某項後採用那種方式呈現,下面是該程式碼:

 1         public void ShowDetails(int playid)
 2         {
 3             _currentPlayId = playid;
 4             //判斷當前螢幕顯示的方案
 5             if (_isDualPanel)
 6             {
 7                 //為大螢幕時顯示的方案
 8 
 9                 ListView.SetItemChecked(playid, true);
10                 //通過碎片管理器查詢對應的碎片  如果是第一次顯示則返回的是null
11                 var details = FragmentManager.FindFragmentById(Resource.Id.details) as DetailsFragment;
12 
13                 //判斷是否存在該碎片的例項化物件或該物件顯示的內容是否跟當前選擇的內容一致
14                 if (details == null || details.ShowPlayId != playid)
15                 {
16                     //例項化碎片
17                     details = DetailsFragment.NewInstance(playid);
18                     var ft = FragmentManager.BeginTransaction();
19                     //將FrameLayout佔位符替換成details碎片
20                     ft.Replace(Resource.Id.details, details);
21                     ft.Commit();
22                 }
23             }
24             else
25             {
26                 //為小螢幕時顯示的方案
27                 var intent = new Intent();
28                 intent.SetClass(Activity, typeof(DetailsActivity));
29                 intent.PutExtra("current_play_id", playid);
30                 StartActivity(intent);
31             }
32         }

這面的程式碼我們通過其中的註釋就可以清楚的明白了,當然我們還要重寫ListFragment的事件,程式碼如下所示:

1         //監聽選擇事件,在每次選擇後重新顯示詳細內容
2         public override void OnListItemClick(ListView l, View v, int position, long id)
3         {
4             ShowDetails(position);
5         }

至此我們就實現了能夠在不同螢幕下顯示不同介面的方式,這在很大的程度上可以複用程式碼,而不需要非要單獨去定製專門的應用。

原始碼下載