你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> 使用MonoTouch.Dialog簡化iOS界面開發

使用MonoTouch.Dialog簡化iOS界面開發

編輯:IOS開發綜合
MonoTouch.Dialog簡稱MT.D,是Xamarin.iOS的一個RAD工具包。它提供易於使用的聲明式API,不需要使用導航控制器、表格等ViewController來定義復雜的應用程序UI,使得快速開發應用程序UI成為可能。   MT.D的作者是Xamarin的CTO:Miguel de Icaza,MT.D基於表格來創建UI,它提供的API使得創建基於表格的UI變得更加簡單。       API介紹 MonoTouch.Dialog提供了兩種API來定義用戶界面:   Low-level Elements API: 低級別的元素API,通過層次化的樹型結構(類似於DOM)來表示UI及其部件。它提供了最大的靈活性用於創建和控制UI。此外,元素API支持通過定義JSON方式來動態生成UI。 High-Level Reflection API:高級反射API,也稱為綁定API(Binding API),通過Attribute在實體類上標記元素類型等信息,然後基於對象提供的信息自動創建UI,並且提供對象與UI元素之間的自動綁定。注意:此API不提供細粒度的控制。 MT.D內置了一套UI元素,開發人員也可以通過擴展現有UI元素或者創建新的元素來支持自定義布局。   此外,MT.D內置了一些增強用戶體驗的特性,比如”pull-to-refresh”下拉刷新、異步加載圖片、和搜索的支持。   在使用MT.D之前,有必要對它的組成部分進行了解:   DialogViewController:簡稱DVC,繼承自UITableViewController,所有MT.D的元素都需要通過它來顯示到屏幕上。當然,也可以像普通的UITableViewController一樣來使用它。 RootElement:是DVC的頂層容器,它包含多個Section,然後每個Section包含UI元素。RootElement不會被呈現在界面上,而是它們的子元素Section及Element被呈現。 Section:Section在表格中作為一個單元格的分組呈現(與UITableView的Section一樣),它有Header和Footer屬性,可以設置成文字或者是自定義視圖。 Element:Element即元素作為最基本的控件,表示TableView的實際單元格,MT.D內置了各種不同類型的元素。     DialogViewController(DVC)   元素API和反射API都是使用DialogViewController來呈現,DVC繼承自UITableViewController,所有UITableViewController的屬性與方法都可以在DVC上使用。   DVC提供了多個構造函數來對它進行初始化,這裡只看參數最多的一個構造函數:   DialogViewController(UITableViewStyle style, RootElement root, bool pushing) style即列表樣式,默認都是UITableViewStyle.Grouped分組顯示,可以設置為UITableViewStyle.Plain不分組。   root即根元素,它下面的所有Section/Element都會被DVC呈現出來。   pushing參數用於是否顯示返回按鈕,一般用在有UINavigationController的時候。   例如,創建一個不分組顯示的DVC:     var dvc = new DialogViewController(UITableViewStyle.Plain, root) 在實際應用開發中,一般很少會直接創建DVC的實例,而是通過繼承的方法對每一個視圖進行定制:     class LwmeViewController: DialogViewController {   public LwmeViewController(): base(UITableViewStyle.Plain, null) {     this.Root = new RootElement("囧月") {       //...創建Section及Element     };     } } 然後通過重寫DVC的一些方法來定制自己的視圖。   在完全使用MT.D開發的app中,可以把DVC做為根視圖控制器:     [Register("AppDelegate")] public partial class AppDelegate : UIApplicationDelegate {     UIWindow window;     public override bool FinishedLaunching(UIApplication app, NSDictionary options)     {         window = new UIWindow(UIScreen.MainScreen.Bounds);         window.RootViewController = new LwmeViewController();         window.MakeKeyAndVisible();         return true;     }     // ... } 假如app需要使用UINavigationController,可以把DVC作為UINavigationController的根視圖控制器:     nav = new UINavigationController(new DialogViewController(root)); window.RootViewController = nav;     RootElements   DialogViewController需要一個RootElement作為根節點,它的子節點只能是Section,各種Element必須作為Section的子節點來呈現。     // 在使用NavigationController的時候,RootElement的Caption會被呈現為NavigationItem的內容 var root = new RootElement ("囧月 - 博客園") {      new Section("隨筆") {  // 分組的文字         new StringElement("MonoTouch.Dialog") // 元素     }     new Section("評論") {         new EntryElement("內容")     } } RootElement還可以作為Section的子元素,當這個RootElement被點擊的時候,實際上會打開一個新的視圖,如下(官方DEMO):       var root = new RootElement ("Meals") {     new Section ("Dinner"){             new RootElement ("Dessert", new RadioGroup ("dessert", 2)) {                 new Section () {                     new RadioElement ("Ice Cream", "dessert"),                     new RadioElement ("Milkshake", "dessert"),                     new RadioElement ("Chocolate Cake", "dessert")                 }             }         }     } 此外,還可以通過LINQ(語句或表達式)和C# 3.0新增的對象和集合初始化語法來創建元素的層次結構:     var root = new RootElement("囧月-lwme.cnblogs.com") {   new string[] {"隨筆", "評論", "RSS"}.Select(     x => new Section(x) {       "內容1,內容2,內容3,內容4".Split(',').Select(         s => new StringElement(s, delegate {           Console.WriteLine("內容被點擊");         })       )     }   ) } 通過這種做法,可以很容易的結合XML或數據庫,完全從數據創建復雜的應用程序。       Sections   Section用來對Element元素進行分組顯示,它可以包含任何標准內容(Element/UIView/RootElement),但RootElement只能包含它。   可以把Section的Header/Footer設置為字符串或者UIView:   var section = new Section("Header", "Footer") // 使用字符串 var section = new Section(new UIImageView(Image.FromBundle("header.png"))); // 使用UIView     內置元素介紹 MT.D內置了這些元素:   StringElement:呈現為普通的文本,左邊為Caption右邊為Value StyledStringElement:繼承自StringElement,使用內置的單元格樣式或自定義格式,提供了字體、顏色、背景、換行方式、顯示的行數等屬性可供設置 MultilineElement:呈現為多行的文本 StyledMultilineElement:繼承自MultilineElement,多了一些可以設置的屬性(類似StyledStringElement) EntryElement:文本框,用於輸入普通字符串或者密碼(isPassword參數),除了Caption/Value外,還有Placeholder屬性用於設置文本框提示文本。除此之外,還可以設置KeyboardType屬性,用來限制數據輸入: Numeric 數字 Phone 電話 Url 網址 Email 郵件地址 BooleanElement:呈現為UISwitch CheckboxElement:呈現為復選框 RadioElement:呈現為單選框,需要放置在有RadioGroup的RootElement的Section中,使用起來顯得有點麻煩 BadgeElement:呈現為垂直居中的文本左邊一個圖標 (57x57) ImageElement:用於選取圖片 ImageStringElement:繼承自StringElement,類似於BadgeElement FloatElement:呈現為UISlider ActivityElement:呈現為loading加載動畫 DateElement:日期選擇 TimeElement:時間選擇 DateTimeElement:日期時間選擇 HtmlElement:呈現為一個普通的文本,通過Url屬性設置網址,點擊之後自動打開一個UIWebView加載網站 MessageElement:呈現為類似收件箱郵件的樣式,有許多屬性可以設置(Body/Caption/Date/Message/NewFlag/Sender/Subject) LoadMoreElement:呈現為一個用於加載更多的普通文本,點擊後顯示加載動畫,在相應的事件裡進行一些邏輯處理 UIViewElement:所有類型的UIView都可以通過UIViewElement來呈現到表格上 OwnerDrawnElement:這是一個抽象類,可以通過繼承它來創建自定義的視圖 JsonElement:繼承自RootElement,用於加載JSON內容來自動創建視圖(從本地/網絡上的json文件/字符串) 官方也給出了一個元素的結構樹:       Element        BadgeElement        BoolElement           BooleanElement       - uses an on/off slider           BooleanImageElement  - uses images for true/false        EntryElement        FloatElement        HtmlElement        ImageElement    MessageElement        MultilineElement        RootElement (container for Sections)        Section (only valid container for Elements)        StringElement           CheckboxElement           DateTimeElement               DateElement               TimeElement           ImageStringElement           RadioElement           StyleStringElement       UIViewElement     處理動作   Element提供了NSAction類型的委托作為回調函數來處理動作(大部分Element都有一個NSAction類型的Tapped事件),比如處理一個觸摸事件:   new Section () {         new StringElement ("點我 - 囧月",                  delegate { Console.WriteLine ("元素被點擊"); }) }     檢索元素的值   繼承自Element的元素默認有Caption屬性,用來在單元格左邊顯示標題;大部分Element都有一個Value屬性,用來顯示在單元格右邊。   在回調函數中通過Element的屬性來獲取對應的值:     var element = new EntryElement ("評論", "輸入評論內容", null); var taskElement = new RootElement ("囧月-博客-評論"){         new Section () { element },         new Section ("獲取評論內容") {                 new StringElement ("獲取",                          delegate { Console.WriteLine (element.Value); })         } };     設置元素的值   如果元素的屬性是可操作的,如EntryElement.Value,可以直接通過屬性設置它的值。   不可操作的如EntryElement.Caption,或者StringElement.Value/StringElement.Caption屬性,直接設置元素的值不會反映在界面上,需要通過RootElement.Reload方法來重新加載才可以更新內容:   var ee = new EntryElement ("評論", "輸入評論內容", null);  var se = new StringElement("時間", DateTime.Now.ToString());  var root = new RootElement ("囧月-博客-評論"){          new Section () { ee, se },          new Section ("獲取評論內容") {                  new StringElement ("獲取",                          delegate {                            Console.WriteLine (element.Value);                            // 直接設置元素內容                           ee.Value = DateTime.Now.ToString();                            // 不可直接設置的屬性                           se.Caption = "新標題";                           se.Value = DateTime.Now.ToString();                           root.Reload(se, UITableViewRowAnimation.None);                         })          }  };     反射API 反射API通過使得創建UI界面變得非常簡單:   創建一個類,並使用MT.D的Attribute來標記它的字段/屬性 創建BindingContext的實例,並把上一步類型的實例作為參數 創建DialogViewController,並把它的Root設置為BindingContext的Root 先來一個簡單的例子:       class Blogger {   [Section("登錄博客"),   Entry("輸入用戶名"), Caption("用戶名")]   public string Username;       [Password("輸入密碼"), Caption("密碼")]   public string Password;       [Checkbox, Caption("下次自動登錄")]   public bool Remember;       [Section("開始登錄", "請確認你輸入的信息"),   Caption("登錄"),   OnTap("Login")]   public string DoLogin; }   public class LwmeViewController: DialogViewController {   BindingContext context;   Blogger blog;   public LwmeViewController(): base(UITableViewStyle.Grouped, null) {     blog = new Blogger { Username = "囧月" };     context = new BindingContext(this, blog, null);     this.Root = context.Root;   }     public void Login() {     context.Fetch(); // 通過Fetch方法把文本框輸入的信息反饋到blog實例上     if (string.IsNullOrWhiteSpace(blog.Username) ||         string.IsNullOrWhiteSpace(blog.Password)) {         var tip = new UIAlertViewController( "出錯提示", "用戶名和密碼必須填寫", null, "確定", null);         tip.Show();     }     // 進行登錄操作...   } } 為了避免阻塞UI線程(用戶界面假死),一般都會使用異步操作,比如上面的登錄可能使用WebClient的UploadStringAsync異步方法,然後在相應事件中進行操作;這裡需要注意,使用了異步方法之後,在相應的事件中可能就不是UI線程,將不能直接對UI相關元素進行操作,類似於Winform/Wpf,MonoTouch提供了兩個方法用於在非UI線程操作UI元素:InvokeOnMainThread/BeginInvokeOnMainThread。   現在,來看一下MT.D為反射API提供了多少Attribute:   EntryAttribute:文本框 PasswordAttribute:密碼輸入框,繼承自EntryAttribute CheckboxAttribute:復選框 DateAttribute:日期 TimeAttribute:時間 DateTimeAttribute:日期時間 HtmlAttribute:普通的文本,點擊後打開一個UIWebView MultilineAttribute:多行文本 RadioSelectionAttribute:呈現為RadioElement,字段/屬性需要是int類型,數據源需要實現IEnumerable接口 CaptionAttribute:用於設置元素的Caption,如果不設置的話,將使用元素的屬性/字段名 AlignmentAttribute:用於設置元素內容的對齊方式 OnTapAttribute:用於設置點擊事件,參數為一個字符串對應執行的方法名 RangeAttribute:用於設置UISlider的值范圍 SkipAttribute:使用此Attribute的屬性/字段將不被用於作為UI元素呈現 除了以上列出的,還有3個元素沒有對應的Attribute:   StringElement:即普通文本,沒有對應的Attribute,string類型的字段/屬性默認會被呈現為StringElement BooleanElement:即UISwitch,bool類型的字段/屬性會被呈現為BooleanElement FloatElement:即UISlider,float類型的字段/屬性會被呈現為FloatElement 再來一個例子:     class Blogger {   public string Username = "囧月"; // 呈現為StringElement   public bool Remember; // 呈現為BooleanElement   public float Value; // 呈現為FloatElement   [Multiline]   public string Description;   [Range(0, 100)]   public float Value2; // 可以使用Range來標明范圍   [Skip]   public string ignoreField; // 不被呈現 } 另外,對於RadioElement類型的元素,除了可以使用RadioSelectionAttribute外,MT.D還提供了一個方法支持直接從Enum類型:   public enum Category {   Blog,   Post,   Comment } class Blogger {   public Category ContentCategory; }   class Blogger2 {   [RadioSelection("CategorySource")] // 設置數據源   public int ContentCategory; // 字段/屬性必須是int類型   // 數據源只要實現IEnumerable接口,不限制類型   public List<string> CategorySource = new List<string>{ "Blog", "Post", "Comment" }; } 注意:字段/屬性的類型必須與相應的Element的值類型對應,否則不會被呈現,比如:   EntryElement只能使用string類型,用int就不會被呈現 FloatElement只能使用float類型,double/decimal類型都無效 BooleanElement只能使用bool類型 RadioElement類型只能使用enum類型或者int類型並設置數據源 DateElement/TimeElement/DateTimeElement只能使用日期相關類型 反射API大大簡化了UI界面的開發,但是它不能很好支持細粒度控制,如果對UI定制要求比較高,建議還是直接使用元素API。   當然,如果只是偶爾需要直接訪問某個Element,可以通過DVC的Root屬性來找到對應的Element,但是操作起來比較繁瑣:     var section1 = this.Root[0]; var element1 = section1[0] as StringElement;     JSON元素 MT.D支持從本地/遠程的json文件、或者已解析的JsonObject對象實例來創建JSON元素。   假如有這麼一個簡單的json文件:     {     "title": "囧月",     "sections": [          {           "elements" : [             {                 "id" : "lwme-username",                 "type": "entry",                 "caption": "用戶名",                 "placeholder": "輸入用戶名"             },             {                 "id" : "lwme-date",                 "type": "date",                 "caption": "日期",                 "value": "00:00"             }          ]         }     ]   } 通過內置的方法來加載它:     var root = JsonElement.FromFile("lwme.json"); // 加載本地json var root = new JsonElement("load from json", "lwme.cnblogs.com/lwme.json"); // 加載遠程json var dvc = new DialogViewController(root); // 可以直接把JsonElement作為根元素 另外,還可以通過json文件裡設置的id來獲得對應的Element:     var username = taskElement ["lwme-username"] as EntryElement; var date = taskElement ["lwme-date"] as DateElement; 通過json元素這種方式,可以創建非常靈活的界面,同時也能大大減小客戶端的大小。   json文件裡各種標記的屬性對應元素的各種屬性,完整的JSON格式見官方文檔:http://docs.xamarin.com/guides/ios/user_interface/monotouch.dialog/monotouch.dialog_json_markup/。       其他特性     Pull-to-Refresh(下拉刷新)支持   DialogViewController提供了一個RefreshRequested事件,只需要實現它就可以為表格提供下拉刷新支持:   var dvc = new DialogViewController(root); dvc.RefreshRequested  += (s, e) {   // 處理數據... lwme.cnblogs.com   dvc.ReloadComplete(); // 處理完成之後調用這個方法完成加載 }; 另外,也有TriggerRefresh()方法來直接調用下拉刷新;還可以通過重寫MakeRefreshTableHeaderView(RectangleF)方法來自定義刷新頭部的內容。       搜索支持   DialogViewController提供了一些屬性及方法用於搜索的支持:   EnableSearch:啟用搜索支持 SearchPlaceholder:搜索框提示文本 StartSearch():開始搜索 FinishSearch():完成搜索 PerformFilter():執行過濾 SearchButtonClicked():按下搜索按鈕 OnSearchTextChanged():搜索文本框內容改變 SearchTextChanged:事件,同上 一般情況下只需要通過EnableSearch屬性來啟用搜索即可,更多的定制可以通過以上的方法/事件來實現。       後台加載圖片   MT.D提供了一個ImageLoader用於在後台加載圖片:     new BadgeElement( ImageLoader.DefaultRequestImage( new Uri("http://lwme.cnblogs.com/xx.png"), this), "囧月") // 等同於ImageLoader.DefaultLoader.RequestImage方法 下載的圖片會被緩存在內存中(默認緩存50張圖片),ImageLoader.Purge()方法可用於清理緩存。更多的自定義操作可以通過創建ImageLoader實例來實現。       創建自定義元素   可以通過繼承Element或者更具體的類型來創建自定義的元素。創建自定義元素將需要重寫以下方法:     // 為元素創建UITableViewCell,設置內容及樣式並呈現在表格上 UITableViewCell GetCell (UITableView tv) // (可選)設置元素的高度,重寫這個方法需要實現IElementSizing接口 float GetHeight (UITableView tableView, NSIndexPath indexPath); // (可選)釋放資源 void Dispose (bool disposing); // (可選)為元素呈現摘要內容,比如StringElement就呈現為Caption string Summary () // (可選)元素被點擊/觸摸時,很多元素的Tapped事件就是在這個方法裡實現 void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath path) // (可選)如果需要支持搜索,需要在方法中檢測用戶輸入是否匹配 bool Matches (string text)   static NSString MyKey = new NSString ("lwmeCustomElementKey"); protected override NSString CellKey {     get {         return MyKey;     } }     關於數據驗證   MT.D沒有為Element提供任何驗證的方法,如果需要對用戶輸入進行驗證,自己實現驗證邏輯,比如元素的Tapped事件中進行數據驗證:     var ee = new EntryElement ("評論", "輸入評論內容", null);  var root = new RootElement ("囧月-博客-評論"){          new Section () { ee },          new Section ("獲取評論內容") {                  new StringElement ("獲取",                          delegate {                            if (string.IsNullOrEmpty(ee.Value)) {                             var tip = new UIAlertViewController(                               "出錯提示", "內容必須填寫", null, "確定", null);                             tip.Show();                           }                         })         } };  
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved