成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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提取圖像

2022-09-29 15:39:10

服務器NettyReactor

2024-05-22 07:57:34

2016-01-26 11:08:54

2022-08-24 15:57:17

圖片輪廓

2019-09-29 09:08:41

Python數據庫Google

2021-09-04 23:45:40

機器學習語言人工智能

2019-09-04 11:09:38

物聯網數據邊緣

2013-04-01 11:14:56

IT大數據網絡信息化

2023-04-27 07:06:09

Categraf夜鶯

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技術棧公眾號

主站蜘蛛池模板: 91精品国产91久久久久久 | 在线亚洲免费视频 | 欧美一区二区久久 | 玖草资源 | 日韩在线视频一区二区三区 | 国产成人精品一区二 | 国产偷久久一级精品60部 | 夜夜爽99久久国产综合精品女不卡 | 国产精品视频一二三区 | 午夜免费网站 | 91看片网| 精品不卡| 欧美亚洲综合久久 | 亚洲国产一区二区在线 | 成人欧美一区二区三区黑人孕妇 | 日本不卡视频在线播放 | 91天堂网 | 亚洲成人动漫在线观看 | 亚洲精品456 | 国产免费av在线 | 日韩精品免费在线 | 亚洲福利 | 国产精品视频免费观看 | 午夜视频在线免费观看 | 久久国产精品一区二区三区 | 欧美日韩精品在线免费观看 | 一区二区在线免费观看 | 日韩欧美三区 | 少妇无套高潮一二三区 | 精品一区二区三区在线播放 | 免费性视频| 黄色大片在线 | 精品久久久久久久久久久久久久 | 中文字幕久久久 | 亚洲成人久久久 | 电影午夜精品一区二区三区 | 男女羞羞视频在线观看 | 亚洲视频免费观看 | 最新免费视频 | 久久国产精99精产国高潮 | 亚洲精品一区二区三区丝袜 |