精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

av一区二区在线看| 亚洲男人av在线| 在线观看av的网址| 亚洲第一黄色片| 久久一区国产| 久热爱精品视频线路一| 久久精品aⅴ无码中文字字幕重口| 成人福利影视| 日本一区二区成人| 国产欧美一区二区在线播放| 久操视频在线免费观看| 午夜日韩电影| 国产一区二区三区在线视频| 美国黄色一级视频| 欧美一级二级视频| 一卡二卡欧美日韩| 性欧美.com| 无码国产精品一区二区免费16| 蜜臀久久99精品久久久久宅男| 久久久久久久久久久人体| 亚洲一区二区自偷自拍| 成人免费直播在线| 7777精品伊人久久久大香线蕉经典版下载 | 天天干天天草天天射| 免费高清在线视频一区·| 98精品国产自产在线观看| 成人欧美一区二区三区黑人一 | 日本一区二区三区免费看| www.桃色av嫩草.com| 青青草成人在线观看| 69av在线视频| 国产性70yerg老太| 先锋资源久久| 中文字幕在线观看日韩| 女同毛片一区二区三区| 成人激情自拍| 精品美女一区二区三区| 日韩高清在线一区二区| 日本成人在线网站| 欧美午夜精品久久久久久孕妇 | 亚洲精品高清视频| 国产普通话bbwbbwbbw| 日韩精品一二三四| 欧美一级淫片播放口| 九九九国产视频| 国产综合精品| 久久久久久国产精品久久| 人人干在线观看| 久久亚洲专区| 日韩在线播放av| 99热在线观看精品| 91欧美国产| 久久久国产影院| 免费国产羞羞网站美图| 国产精品久久久久久久| xxxxx91麻豆| 国产成人av免费在线观看| 99成人在线视频| 久久亚洲电影天堂| 四虎永久免费在线| 欧美一区二区| 久久免费精品视频| 中文字幕亚洲精品在线| 99精品福利视频| 亲子乱一区二区三区电影| 青青操免费在线视频| 国产欧美一区二区三区国产幕精品| 性亚洲最疯狂xxxx高清| 全部毛片永久免费看| 免费一区视频| 国产又爽又黄的激情精品视频| 国产乱码精品一区二区| 国产成人av网站| 国外成人在线视频网站| 天堂网在线播放| 国产欧美日韩在线| 99re8这里只有精品| 91黄色在线| 亚洲不卡在线观看| 少妇人妻互换不带套| 国产精品久久乐| 欧美成人精品福利| v8888av| 91欧美在线| 性欧美激情精品| 亚洲视频在线观看一区二区| 国产高清在线观看免费不卡| 国产日韩精品推荐| h网站在线免费观看| 亚洲女同女同女同女同女同69| 国产中文字幕乱人伦在线观看| 97se综合| 欧美一二三四区在线| 亚洲av无码一区二区三区网址 | 男人揉女人奶房视频60分| 欧美色片在线观看| 精品国产sm最大网站免费看| 天堂久久精品忘忧草| 欧美一区二区三区免费看| 57pao国产成人免费| 国产美女精品视频国产| 久久亚洲精精品中文字幕早川悠里| 亚洲成人第一| 俺来也官网欧美久久精品| 欧美亚洲自拍偷拍| 精品人妻伦一二三区久| heyzo久久| 久久久人成影片一区二区三区观看 | 男男成人高潮片免费网站| 97碰碰视频| eeuss影院在线播放| 亚洲第一成人在线| 亚洲a级黄色片| 妖精视频一区二区三区| 欧美超级乱淫片喷水| 国产一区免费看| www.欧美.com| 天天做天天躁天天躁| 精品亚洲a∨| 亚洲人成网站999久久久综合| 久久久无码精品亚洲国产| 美女视频网站久久| 欧美性色黄大片人与善| 成人av影院在线观看| 欧美一区二区三区四区五区 | 在线观看国产91| 中文字幕乱码在线| 欧美日韩视频| 91九色国产在线| 91最新在线| 在线看国产一区| 91精彩刺激对白露脸偷拍| 亚洲婷婷在线| 成人国产一区二区| 亚洲精品天堂| 欧美一区二区高清| www深夜成人a√在线| 老鸭窝一区二区久久精品| 日本一区不卡| 在线成人视屏| 一区二区福利视频| 免费的毛片视频| 久久综合久色欧美综合狠狠| 国产精品专区在线| 精品女人视频| 97视频人免费观看| 精品国产伦一区二区三区| 中文字幕在线一区| 亚洲最大天堂网| 天天色综合色| 亚洲最大的av网站| 中文字幕资源网在线观看| 91精品国产麻豆| 欧美日韩在线观看成人| 国产二区国产一区在线观看| 精品久久久无码人妻字幂| 999久久久精品一区二区| 国内精品久久久久伊人av| 天天干视频在线观看| 色婷婷久久久久swag精品| 白白色免费视频| 美女视频一区二区三区| 日韩视频在线观看视频| 午夜日韩影院| 国内精品久久久久久影视8| 四虎影院在线域名免费观看| 色诱视频网站一区| 任你操精品视频| 国产成人在线影院| 你懂的av在线| 欧美理论在线播放| 成人中文字幕+乱码+中文字幕| 免费黄网站在线播放| 欧美第一区第二区| 成年免费在线观看| 国产欧美日韩精品在线| av中文字幕网址| 91久久中文| 日韩av高清在线播放| 香蕉久久一区| 欧美极品第一页| 国产在线黄色| 日韩亚洲欧美综合| 日本午夜视频在线观看| 中文字幕欧美激情一区| 精品人妻一区二区乱码| 亚洲一级在线| 永久免费在线看片视频| 欧美美女在线直播| 国产又爽又黄的激情精品视频 | 国产一级在线| 欧美一级高清片| 羞羞影院体验区| 国产精品短视频| 在线精品一区二区三区| 蜜臀久久99精品久久久久宅男| 香港三级日本三级a视频| 国产毛片一区二区三区| 成人免费看片网址| 偷拍中文亚洲欧美动漫| 色综合久久88| 中文字幕在线播放| 日韩精品在线观看视频| 国产伦精品一区二区三区免.费 | 天堂网av2014| 91精品国产色综合久久久蜜香臀| 青青操免费在线视频| 亚洲免费观看高清在线观看| 久久精品—区二区三区舞蹈| 成人免费高清视频在线观看| 亚洲怡红院在线| 日韩和的一区二区| 少妇高潮喷水在线观看| 亚洲九九视频| 亚洲成人午夜在线| 日韩中文av| 国产麻豆日韩| 日本一区二区三区电影免费观看| 国产精品白嫩美女在线观看| 国产v日韩v欧美v| 欧美日韩第一页| 久久亚洲天堂| www.欧美三级电影.com| 免费在线黄色网址| 日韩精品亚洲精品| 人人妻人人澡人人爽人人欧美一区 | 国精品人妻无码一区二区三区喝尿 | 亚洲欧美亚洲| 欧美大片免费播放| 亚洲欧美偷拍自拍| 夜夜爽99久久国产综合精品女不卡 | 一区二区三区影院| 国产探花在线视频| 国产精品嫩草影院com| 免费福利视频网站| 久久午夜老司机| 人妻无码一区二区三区| 不卡av在线免费观看| 成人欧美精品一区二区| 粉嫩嫩av羞羞动漫久久久 | japanese国产在线观看| 色先锋资源久久综合| 国产免费一区二区三区四区五区| 五月天欧美精品| 91在线看视频| 欧美日韩美女视频| 69成人免费视频| 欧美日韩免费网站| 东京热一区二区三区四区| 欧美性生交xxxxx久久久| 综合激情网五月| 色综合天天综合网天天狠天天 | 手机在线中文字幕| 最新热久久免费视频| 999精品在线视频| 亚洲精品成人在线| 国产亚洲色婷婷久久99精品| 亚洲成av人片在线| 日韩精品在线免费视频| 色综合一区二区| 日本一区二区三区久久| 在线不卡一区二区| www.麻豆av| 欧美精品一区二区三区在线播放 | 99r国产精品| 国产三级av在线播放| 中文字幕精品一区二区精品绿巨人 | 天堂网av成人| 视频一区二区在线观看| 99国产精品一区二区| 国产精品免费看久久久无码| 1024日韩| 中文字幕有码av| 国内成人免费视频| 成年人的黄色片| 久久久不卡网国产精品二区| 老司机深夜福利网站| 一区二区三区加勒比av| 国产污污视频在线观看| 欧美日本视频在线| 亚洲精品无amm毛片| 亚洲欧美日韩国产成人| 欧美精品hd| 韩剧1988在线观看免费完整版| 日本成人伦理电影| 99久热re在线精品996热视频 | 亚洲欧洲自拍偷拍| 大地资源网3页在线观看| 亚洲**2019国产| 欧美xxxx性| 精品国产免费久久久久久尖叫 | 日韩有码片在线观看| 国产夫妻在线播放| 成人激情综合网| 中文字幕精品影院| 老司机午夜免费福利视频| 鲁大师成人一区二区三区| 亚洲最大天堂网| 久久久99精品免费观看| 久草视频中文在线| 欧美视频一区在线| 天天干免费视频| 久久天天躁夜夜躁狠狠躁2022| 欧美二三四区| 国产欧美一区二区视频| 婷婷综合激情| 91蝌蚪视频在线观看| 成人av免费在线观看| а天堂中文在线资源| 激情成人中文字幕| 亚洲爆乳无码一区二区三区| 最近2019年手机中文字幕| 在线观看爽视频| 高清国产一区| 影视一区二区| 爱爱爱爱免费视频| 国产视频一区在线播放| 日韩av一二三区| 精品国产乱码久久久久久影片| 成人在线播放免费观看| 国产精品美女久久| 欧美猛男男男激情videos| 黄色国产一级视频| 成人一区在线看| 免费一级肉体全黄毛片| 777a∨成人精品桃花网| 在线毛片网站| 国产在线视频不卡| 成人精品亚洲| 手机在线看福利| 欧美高清在线一区二区| 无码人妻久久一区二区三区| 日韩精品在线免费播放| av资源网在线播放| 国产免费一区二区| 亚洲国产99| 国产一级伦理片| 欧美日韩另类视频| 每日更新av在线播放| 4k岛国日韩精品**专区| 青青草这里只有精品| 欧美在线观看www| 99久久er热在这里只有精品15 | 久草在线视频福利| www.av一区视频| 激情欧美丁香| 日本一区二区在线免费观看| 午夜久久久久久久久| 完全免费av在线播放| 欧美日韩成人一区| 欧美边添边摸边做边爱免费| 国产美女扒开尿口久久久| 99久久夜色精品国产亚洲1000部| 无尽裸体动漫2d在线观看| 中文字幕一区av| aaa一区二区三区| 欧美精品videosex性欧美| 老司机在线精品视频| wwwxxx黄色片| 欧美国产成人在线| 99国产精品一区二区三区| 久久国产精品久久久久久| 18国产精品| 中文字幕乱码人妻综合二区三区| 国产清纯美女被跳蛋高潮一区二区久久w | 欧美国产日韩在线观看成人| 日韩免费视频一区| 国产ktv在线视频| 亚洲成人自拍| 成人激情午夜影院| 国产成人一级片| 精品国产一区二区三区久久久狼| 麻豆国产精品| 国产视频九色蝌蚪| 中文幕一区二区三区久久蜜桃| 国产视频在线免费观看| 97热精品视频官网| 日本一区二区高清不卡| 一卡二卡三卡四卡五卡| 欧美日韩国产在线播放| 你懂的在线播放| 91欧美视频网站| 新狼窝色av性久久久久久| 中文字幕第69页| 亚洲国产精品系列| 国产精品久久久久久吹潮| 六月婷婷激情综合| 国产偷v国产偷v亚洲高清| 99久久精品免费看国产交换| 国产91成人video| 五月天久久网站| 扒开jk护士狂揉免费| 制服丝袜中文字幕一区| 一二三四视频在线中文| 一区中文字幕在线观看| 97se亚洲国产综合在线| 国产精品无码在线播放| 日本不卡免费高清视频| 亚洲视频综合| 99久久久免费精品|