精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Android中提取表單模型

移動開發 Android
Form Model”的基本思想是,把處理UI交互以及數據綁定和狀態保持的代碼提取到單獨的類中。這種分離非常自然,并且讓我們的Activity變得簡單。我認為在Android中這個領域不太被關注——在大多數的開發文檔中數據錄入和表單不是重點。在很多流行的社交應用程序中,大多數的畫面只是顯示信息;可能也有幾個畫面用于發微博或者消息,但不是應用的痛點。

我一直追求從Android活動中分離代碼。在最近的一個項目中,我成功的實現了傳統的”Form Model”模式,想在此分享我的感想。

“Form Model”的基本思想是,把處理UI交互以及數據綁定和狀態保持的代碼提取到單獨的類中。這種分離非常自然,并且讓我們的Activity變得簡單。

我認為在Android中這個領域不太被關注——在大多數的開發文檔中數據錄入和表單不是重點。在很多流行的社交應用程序中,大多數的畫面只是顯示信息;可能也有幾個畫面用于發微博或者消息,但不是應用的痛點。

對我來說,上兩個Android應用有特別多的數據錄入工作。部分原因是因為所處的領域(醫療、金融)和客戶(更貼近于企業應用而不是創業)。但我 們經常把“表單輸入”界面搞得一片混亂——特別是當開始添加東西的時候,比如編輯現有的條目,提示丟棄未保存的更改,以及處理旋轉而不會清除所有字段值。

使用這種表單模型方案會減少bug,讓代碼更容易理解,開發者也會變得更快樂。

搜索表單示例

我們有一個銀行應用程序,希望有一個畫面來搜索交易數據。有多個過濾條件:開始是一個金額下拉列表,一個關鍵字字段和一個金額范圍。(希望你可以想象在未來將會增加更多的這類過濾器,復雜性會激增)。

我們沒有把所有的視圖、單擊處理程序,驗證邏輯和數據綁定的代碼堆到一個Activity中,而是要創建一個 SearchForm類來處理這一切。

 

  1. public class SearchForm extends LinearLayout { 
  2.   
  3.   @InjectView(R.id.account) 
  4.   private Spinner mAccountSpinner; 
  5.   private AccountAdapter mAccountAdapter; 
  6.   
  7.   @InjectView(R.id.keyword) 
  8.   private EditText mKeywordField; 
  9.   
  10.   @InjectView(R.id.min_amount) 
  11.   private CurrencyEditText mMinAmountField; 
  12.   
  13.   @InjectView(R.id.max_amount) 
  14.   private CurrencyEditText mMaxAmountField; 
  15.   
  16.   public SearchFormModel(Context context, AttributeSet attrs) { 
  17.     super(context, attrs); 
  18.     setup(context); 
  19.   } 
  20.   
  21.   private void setup(Context context) { 
  22.     LayoutInflater.from(context).inflate(R.layout.search_form, thistrue); 
  23.   
  24.     ButterKnife.inject(this); // <3 @JakeWharton 
  25.   
  26.     mAccountAdapter = new AccountAdapter(context); 
  27.     mAccountSpinner.setAdapter(mAccountAdapter); 
  28.   } 
  29.   
  30.   public initialize(List<Account> accounts) { 
  31.     mAccountAdapter.setItems(accounts); 
  32.   } 
  33.   
  34.   public String getKeywords() { 
  35.     return mKeywordField.getText().toString(); 
  36.   } 
  37.   
  38.   public void setKeywords(String keywords) { 
  39.     mKeywordField.setText(keywords); 
  40.   } 
  41.   
  42.   public MoneyAmount getMinimumAmount() { 
  43.     return mMinAmountField.getAmount(); 
  44.   } 
  45.   
  46.   public void setMinimumAmount(double amount) { 
  47.     mMinmountField.setAmountFromDouble(amount); 
  48.   } 
  49.   
  50.   public MoneyAmount getMaximumAmount() { 
  51.     return mMaxAmountField.getAmount(); 
  52.   } 
  53.   
  54.   public void setMaximumAmount(double amount) { 
  55.     mMaxAmountField.setAmountFromDouble(amount); 
  56.   } 
  57.   
  58.   public Account getSelectedAccount() { 
  59.     return mAccountSpinner.getSelectedItem(); 
  60.   }  
  61.   
  62.   public boolean validate() { 
  63.     clearErrors(); 
  64.     boolean isValid = true
  65.   
  66.     if (!isValidAmountRange()) { 
  67.       isValid = false
  68.       mMinAmountField.setError("Invalid range"); 
  69.       mMaxAmountField.setError("Invalid range"); 
  70.     } 
  71.   
  72.     return isValid; 
  73.   } 
  74.   
  75.   private boolean isValidAmountRange() { 
  76.     return getMinimumAmount() <= getMaximumAmount(); 
  77.   } 
  78.   
  79.   private void clearErrors() { 
  80.     mMinAmountField.setError(null); 
  81.     mMaxAmountField.setError(null); 
  82.   } 
  83.   
  84.   public SearchParameters buildParameters() { 
  85.     return new SearchParameters(getSelectedAccount(), 
  86.                                 getKeywords(), 
  87.                                 getMinimumAmount(), 
  88.                                 getMaximumAmount()); 
  89.   } 
  90.   
  91.   public void persist(Bundle outState) { 
  92.     outState.putInt("SELECTED_ACCT_INDEX", mAccountSpinner.getSelectedItemPosition()); 
  93.   } 
  94.   
  95.   public void restore(Bundle bundle) { 
  96.     int accountPosition = bundle.getInt("SELECTED_ACCT_INDEX"); 
  97.     mAccountSpinner.setSelection(accountPosition, false); 
  98.   } 

 #p#

我們創建了一個類,繼承自LinearLayout(或者FrameLayout,由你的喜好決定)。它允許把相關的控件組織到一個布局中,我們將填充布局,設置列表視圖并為金額列表創建一個適配器。

我們把Android控件封裝到getter和setter方法中​——這可能會有些爭議,但我認為它使SearchForm擁有更好的公共API。我們有一個方法來驗證用戶的輸入,并根據需要提供錯誤信息。 buildParameters()方法做了一些數據綁定工作并返回業務對象。結尾的兩個方法使用了Android onSaveInstanceState中的Bundle,以處理自定義配置的更改(注意,大多數的原始UI控件會自行處理持久化)。

這是個一百行左右的代碼,大部分還不錯。這個類中所有內容似乎都屬于“搜索表單”對象,對未來的特性有良好的功能擴展點(日期范圍過濾器、支出與存款過濾器、只用支票等)。我們有意避免處理如何獲取數據,把它留給了其他更適合的地方處理這些邏輯代碼。

活動中的代碼是什么樣的呢?

 

  1. public class TransactionSearchActivity extends BaseActivity { 
  2.   
  3.   @InjectView(R.id.search_form) 
  4.   private SearchForm mForm; 
  5.   
  6.   @Override 
  7.   public void onCreate(Bundle savedInstanceState) { 
  8.     super.onCreate(savedInstanceState); 
  9.   
  10.     setContentView(R.layout.transaction_search); 
  11.     setTitle("Search Your Transactions"); 
  12.   
  13.     mForm.initialize(mAccounts); // fetch accounts via API/DB/etc 
  14.   
  15.     if (savedInstanceState != null) { 
  16.       mForm.restore(savedInstanceState); 
  17.     } 
  18.   } 
  19.   
  20.   @Override 
  21.   public boolean onOptionsItemSelected(MenuItem menu) { 
  22.     switch (menu.getItemId()) { 
  23.       case R.id.action_submit_search: 
  24.         onSubmitSearch(); 
  25.         return true
  26.     } 
  27.   
  28.     return super.onOptionsItemSelected(menu) 
  29.   } 
  30.   
  31.   private void onSubmitSearch() { 
  32.     if (mForm.validate()) { 
  33.       // Do your magic, post to an API/DB/etc 
  34.       // You have access to the domain object with mForm.buildParameters() 
  35.     } 
  36.   } 
  37.   
  38.   @Override 
  39.   public boolean onCreateOptionsMenu(Menu menu) { 
  40.     getMenuInflater().inflate(R.menu.search_menu, menu); 
  41.     return super.onCreateOptionsMenu(menu); 
  42.   } 
  43.   
  44.   @Override 
  45.   protected void onSaveInstanceState(Bundle outState) { 
  46.     super.onSaveInstanceState(outState); 
  47.   
  48.     mForm.persist(outState); 
  49.   } 

 

我們的Activity在XML布局文件中包含了一個 **標簽,并且只處理高層面的用戶交互(點擊動作欄中的提交按鈕),并協調獲取和存儲數據。繁重的UI控制和表單邏輯都委托給了 **SearchForm。

Activity的代碼在50行左右——其中大部分是處理框架中生命周期和菜單創建的樣板代碼。

總體印象

一旦涉及到API或數據庫,事情總是會變得更復雜。但總體來講,通過把表單特定的邏輯和視圖相關內容移出活動,代碼變得更容易理解。

我可以為 SearchForm編寫大量的Robolectric測試代碼而且不會帶來與活動生命周期有關的問題。我可以為表單的交互、動作欄、后端編寫測試代碼而不用考慮邊界。當為表單添加新過濾條件時,可以避免對活動做任何的更改(類似于設計模式中的開/閉原則)。

對比其他框架(從其他開發人員的角度來說),Android中數據綁定功能很弱。這種設計似乎還差點什么,因為和Android的類耦合的過于緊 密,依賴于方法的調用順序(initialize()方法應在validate()方法之前調用)——盡管如此,但我認為對于“所有內容混在一起的 Activity”來說是一種改進。

隨著表單模型越來越復雜,你可能要考慮把驗證邏輯提取到一個單獨的對象中,并且把自定義視圖功能移動到自己的控件中(就像我們例子中的 CurrencyEditText)。此外,為了更好的為用戶服務,也可以考慮把復雜的表單拆分成為多步驟向導。

我們發現這種模式可以成功的清理亂糟糟的表單代碼,建議嘗試一下。我把代碼模式稍微規范了一下,并創建了一個小的基類,以減少樣板代碼,可以隨意的使用

譯文鏈接:http://blog.jobbole.com/73195/

原文鏈接:mdswanson

本文鏈接:http://blog.jobbole.com/73195/

翻譯: 伯樂在線 - lum

 

責任編輯:chenqingxiang 來源: 伯樂在線
相關推薦

2023-11-29 11:30:17

PDF語言模型

2023-11-15 13:04:30

Python提取表格

2021-05-13 23:54:12

DockerDockerfile鏡像

2020-07-08 07:54:03

PythonPDF數據

2022-11-23 10:31:54

2025-02-17 12:00:00

PythonOpenCV提取圖像

2024-05-22 07:57:34

2022-09-29 15:39:10

服務器NettyReactor

2019-09-29 09:08:41

Python數據庫Google

2022-08-24 15:57:17

圖片輪廓

2016-01-26 11:08:54

2021-09-04 23:45:40

機器學習語言人工智能

2023-04-27 07:06:09

Categraf夜鶯

2013-04-01 11:14:56

IT大數據網絡信息化

2019-09-04 11:09:38

物聯網數據邊緣

2021-03-15 21:50:22

Linux提取文本GUI工具

2021-03-16 09:00:00

深度學習人工智能傳感器

2023-08-16 17:44:38

2021-08-16 11:51:16

微軟Windows 365Azure

2021-03-10 10:20:06

Linux文本命令
點贊
收藏

51CTO技術棧公眾號

50度灰在线观看| 91久久国产婷婷一区二区| 波多野结衣加勒比| jizz内谢中国亚洲jizz| 国产欧美日本一区二区三区| 亚洲一区二区三区香蕉 | 亚洲精品乱码久久久久久| 国产精品免费一区二区三区在线观看 | 成人黄色影片在线| 四虎永久在线精品| 日韩中文字幕高清在线观看| 精品国产青草久久久久福利| 午夜免费福利在线| 波多野结衣中文在线| 国产肉丝袜一区二区| 国产精品免费一区二区三区在线观看 | av小片在线| 成人高清视频在线观看| 国产女精品视频网站免费| 国产一级一片免费播放放a| 国产一区二区三区天码| 精品国产91乱码一区二区三区| 中文字幕有码av| 人狥杂交一区欧美二区| 亚洲男人的天堂在线aⅴ视频| 欧洲精品国产| 五月婷婷久久久| 国产成a人亚洲精| 国产日韩欧美日韩大片| 无码免费一区二区三区| 1024日韩| 欧美人与物videos| 亚洲精品电影院| 精品国产一区二区三区久久久蜜臀 | 奇门遁甲1982国语版免费观看高清| 国产免费无码一区二区视频| 成人在线免费小视频| 亚洲精品网址在线观看| aaa黄色大片| 久久av偷拍| 91精品久久久久久蜜臀| 国产精品视频中文字幕| 99久久婷婷国产综合精品首页| 欧美午夜精品伦理| 色综合久久久久无码专区| 超碰97免费在线| 亚洲综合色视频| 在线观看欧美亚洲| av网站在线免费观看| 国产日韩精品视频一区| 欧美综合77777色婷婷| 撸视在线观看免费视频| 久久综合999| 欧美精品国产精品久久久| 头脑特工队2在线播放| 岛国一区二区在线观看| 国产精品制服诱惑| 日本免费不卡视频| a在线欧美一区| 久久涩涩网站| 理论视频在线| 欧美国产丝袜视频| 亚洲一区三区电影在线观看| 免费观看在线午夜影视| 成人免费在线播放视频| 亚洲色图都市激情| 国内高清免费在线视频| 午夜久久久久久久久久一区二区| 免费在线观看视频a| 欧洲一区精品| 欧美系列亚洲系列| 中文字幕在线视频一区二区三区| 日韩视频在线直播| 亚洲高清久久久久久| 国产精品无码永久免费不卡| 国产精品羞羞答答在线观看| 色哟哟网站入口亚洲精品| 国产精品国产三级国产传播| 欧美日韩亚洲一区在线观看| 7m精品福利视频导航| 伊人中文字幕在线观看| 捆绑变态av一区二区三区| 亚洲已满18点击进入在线看片 | 干日本少妇视频| 丁香花在线电影| 在线一区二区三区四区五区| 最新免费av网址| 精品人人人人| 中文字幕日韩电影| 国产在线观看成人| 免费日韩一区二区| 成人精品久久一区二区三区| 人人妻人人玩人人澡人人爽| 国产亚洲欧美一级| 狠狠精品干练久久久无码中文字幕| av成人影院在线| 欧美日韩中文字幕一区| 亚洲免费观看在线| av亚洲在线观看| 久久久久久久999| 久久久久久av无码免费看大片| 国产精品69毛片高清亚洲| 麻豆av福利av久久av| 超碰porn在线| 在线一区二区三区四区| 性农村xxxxx小树林| 青青草成人影院| 性欧美办公室18xxxxhd| 91极品身材尤物theporn| 成人国产精品视频| 潘金莲一级淫片aaaaa免费看| 蜜桃视频www网站在线观看| 欧美精品aⅴ在线视频| a级在线观看视频| 欧美福利视频| 成人国产在线视频| 麻豆app在线观看| 午夜不卡av在线| 永久av免费在线观看| 欧美精品一二| 欧美一区二区.| 免费观看国产视频| 亚洲欧美日韩一区| 日本特黄a级片| 国产欧美日韩免费观看| 国产91精品久久久久| 午夜久久久久久久久久| 中文字幕五月欧美| 美女喷白浆视频| 免费成人av| 欧美性视频精品| 天堂在线视频网站| 亚洲精品中文字幕在线观看| 亚洲va综合va国产va中文| 加勒比久久综合| 青青青国产精品一区二区| 欧美自拍偷拍第一页| 一区二区三区欧美| 伦伦影院午夜理论片| 日韩一区二区在线免费| 国产成人拍精品视频午夜网站| 婷婷在线观看视频| 亚洲1区2区3区4区| av av在线| 黄色综合网站| 国产精品日韩一区二区三区| 日韩电影免费观看| 日韩美女一区二区三区| 青娱乐av在线| 丁香六月久久综合狠狠色| 人人妻人人澡人人爽欧美一区双| 综合伊人久久| 欧美精品电影免费在线观看| 后入内射欧美99二区视频| 亚洲午夜免费电影| 2一3sex性hd| 一区二区高清| 欧美亚洲一级二级| 亚洲成人不卡| 日韩一区二区精品视频| 99久久免费国产精精品| 一区二区三区在线免费播放| 亚洲精品成人无码毛片| 亚洲黄色三级| 蜜桃狠狠色伊人亚洲综合网站| 成人私拍视频| www.精品av.com| 亚洲av无码乱码国产精品久久| 一区二区三区精品久久久| 日韩大尺度视频| 一本综合精品| 亚洲精品在线观看免费| 大胆国模一区二区三区| 欧美美最猛性xxxxxx| 农村少妇久久久久久久| 欧美午夜宅男影院在线观看| 长河落日免费高清观看| 国产精品中文字幕日韩精品| 国产极品尤物在线| 精品国产a一区二区三区v免费| 成人精品视频久久久久| 激情av在线播放| 国产一区二区三区精品久久久 | 婷婷视频在线播放| 超碰一区二区三区| 国产成人精品一区二区| 激情视频在线观看| 日韩av中文字幕在线| 国产精品成人久久久| 亚洲综合偷拍欧美一区色| 免费看黄色的视频| 国产精品一区三区| 天天摸天天碰天天添| 亚洲综合激情在线| 欧美视频小说| 日韩不卡在线视频| 国产成人精品av在线| 天堂av最新在线| 亚洲性视频网站| 亚洲欧美黄色片| 欧美日韩一区中文字幕| 国产污视频在线观看| 国产精品女人毛片| 中文字幕免费在线播放| 久久99久久精品欧美| 国产a级一级片| 欧美在线日韩| 午夜精品一区二区在线观看| 欧美网色网址| 99视频在线播放| 韩国精品视频在线观看 | 97久久精品人人做人人爽| 亚洲高清av一区二区三区| 天堂av在线一区| av免费看网址| 牛牛国产精品| 在线观看一区欧美| 精品国产中文字幕第一页| 国产一区国产精品| 日韩中文字幕无砖| 成人欧美一区二区三区黑人| 亚洲爱爱视频| 日本欧美爱爱爱| 免费成人在线电影| 欧美极品少妇xxxxⅹ喷水| 麻豆tv入口在线看| 尤物yw午夜国产精品视频明星| 亚洲区小说区图片区| 精品国产一二三区| www.桃色av嫩草.com| 在线不卡的av| 一级黄色片视频| 欧美在线免费视屏| 久久久久久无码精品大片| 精品国产精品三级精品av网址| 久久久国产精品人人片| 亚洲免费在线看| 亚洲精品卡一卡二| 亚洲视频一区二区在线| www深夜成人a√在线| 日韩美女精品在线| 一区二区三区影视| 亚洲欧美视频在线观看| 三级黄色在线观看| 亚洲天堂精品视频| 天堂а√在线中文在线鲁大师| 国产精品美日韩| 国产日产在线观看| 国产精品高潮久久久久无| 战狼4完整免费观看在线播放版| 欧美国产日韩精品免费观看| 国产精品情侣呻吟对白视频| 国产精品美女视频| 国产成人自拍网站| 一区二区三区高清在线| wwwav国产| 亚洲欧美另类在线| 亚洲熟女www一区二区三区| 亚洲一级在线观看| 免费在线观看黄网站| 色综合天天综合网国产成人综合天 | 国产美女99p| 香蕉一区二区| 日韩精品一区二区三区丰满| 日韩综合网站| 国产91视频一区| 日韩视频二区| 999精品网站| 激情av综合网| 国产精品成人无码专区| 久久久久99精品国产片| 国产黄a三级三级| 亚洲一级片在线观看| 69国产精品视频免费观看| 欧美午夜影院一区| 亚洲国产精品欧美久久| 亚洲男人7777| 国产cdts系列另类在线观看| 久久久爽爽爽美女图片| 成人性生活视频| 亚洲淫片在线视频| 日韩大片在线免费观看| 亚洲人成77777| 欧美激情第二页| 六月激情综合网| 国产麻豆日韩欧美久久| 亚洲一区二区三区四区五区六区| 中文字幕第一区| 国产乡下妇女做爰毛片| 91黄视频在线观看| 超碰在线观看av| 亚洲午夜av电影| 秋霞在线午夜| 国产精品亚洲一区二区三区| 韩国女主播一区二区三区| 亚洲人久久久| 午夜亚洲福利在线老司机| 亚洲一二三不卡| 久久夜色精品国产噜噜av| 成人免费精品动漫网站| 色婷婷综合久色| 亚洲AV无码一区二区三区性| 国产午夜精品一区理论片飘花| 免费网站在线观看人| 国产精品自拍网| 日韩手机在线| 少妇一晚三次一区二区三区| 日本不卡一二三区黄网| 一起草在线视频| 亚洲精品日日夜夜| 中文字幕一区二区三区人妻四季| 亚洲激情在线观看| 深夜国产在线播放| 国产日韩精品在线播放| 国产精品免费不| 亚洲美免无码中文字幕在线| 国产麻豆成人精品| 长河落日免费高清观看| 色拍拍在线精品视频8848| 涩涩视频免费看| 久久久久国产精品一区| 精品欧美视频| 免费观看黄色大片| 久久成人18免费观看| a天堂中文字幕| 色综合天天做天天爱| 图片区 小说区 区 亚洲五月| 色综合久久天天综线观看| 91精品福利观看| 在线观看福利一区| 久久av资源网| 亚洲女人毛茸茸高潮| 色8久久人人97超碰香蕉987| 三级无遮挡在线观看| 国产91精品视频在线观看| 日本韩国欧美超级黄在线观看| 成人一区二区免费视频| 粉嫩av一区二区三区在线播放| 成人在线观看小视频| 91麻豆精品国产| 国产在线高潮| 亚洲直播在线一区| 国产精品地址| 午夜免费福利影院| 亚洲成人激情综合网| 日日夜夜精品免费| 91精品成人久久| 亚洲最好看的视频| www.四虎成人| 国产精品久线在线观看| 国产精品视频无码| 欧美裸身视频免费观看| 高清精品xnxxcom| 国产极品尤物在线| 国产亚洲人成网站| 怡红院成永久免费人全部视频| 日日骚av一区| 国产一区二区三区黄网站| 国产 国语对白 露脸 | 欧美日产在线观看| 国产精品一区二区三区视频网站| 91视频国产高清| 国产综合自拍| 国产白嫩美女无套久久| 日本高清无吗v一区| 在线看免费av| 97人人香蕉| 亚洲欧美日韩国产| 老司机精品免费视频| 欧美va亚洲va香蕉在线| 欧美裸体视频| 亚洲综合第一| eeuss影院一区二区三区| 午夜一级黄色片| 欧美尺度大的性做爰视频| 久久久久观看| 亚洲欧美自拍另类日韩| 一区二区三区日韩精品| 婷婷国产在线| 成人黄色av播放免费| 中文亚洲欧美| 日本成人精品视频| 日韩二区三区在线| 香蕉久久久久久| 国产毛片视频网站| 国产精品全国免费观看高清| 成人毛片在线精品国产| 国产精品久久久精品| 亚洲婷婷免费| 日韩av网站在线播放| 日韩电影免费观看中文字幕| 日韩免费在线电影| 久久综合色视频| 亚洲人成精品久久久久| 欧美一区二区三区少妇| 亚洲综合社区网| 视频一区视频二区中文| 久久久综合久久| 中文字幕欧美视频在线| 老司机在线精品视频|