天天看點

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

在移動應用飛速發展的今天,APP隻針對IOS平台進行開發已經不夠了,如今Android在移動裝置占有近80%的市場,如此大量的潛在使用者怎麼能被忽略掉呢。

在這篇文章中,本人會介紹在IOS開發中,怎麼學習一些Android開發的理念,Android和IOS功能上本身有一定的相似之處,但是具體實作的方式各異,是以這篇文章會使用一個項目例子進行對比,說明怎麼在這兩個平台上分别去實作這個任務。

本文不會深入研究關于IOS和Android兩個平台之間的使用者體驗或者設計模式之間的差異,不過如果能夠了解Android上的一些優秀的UI範例也很有幫助:ActionBar、Overflow menu、back button share action等等。假如你很想嘗試Android開發,那麼強烈推薦你去Google Play Store上購置一台Nexus5,然後把它作為你日常使用的裝置使用一周,然後嘗試仔細了解這個作業系統的各種功能和擴充特性,如果開發者連作業系統的各種使用規則都不了解,那麼做出來的産品一定有問題。

Objective-C和Java之間有很多不同之處,如果把Objective-C的程式設計風格帶到Java裡面的話,很多代碼也許會和底層的應用架構沖突。簡單地說,就是需要注意一些差別:

去掉Objective-C裡面的類字首,因為Java裡有顯式的命名空間和包結構,是以就沒必要用類字首了。

執行個體變量的字首用“m”,不用“_”,在寫代碼的過程中要多利用JavaDoc文檔。這樣能使代碼更清晰,更适合團隊合作。

注意檢查<code>NULL</code>值,Objective-C對空值檢查做的很好,不過Java沒有。

不直接使用屬性,如果需要<code>setter</code>和<code>getter</code>,需要建立一個<code>getVariableName()</code>方法,然後顯式調用它。如果直接使用“this.object”不會調用自定義的<code>getter</code>方法,你必須使用<code>this.getObject</code>這樣的方法。

同樣的,方法命名時帶有<code>get</code>和<code>set</code>字首來标示它是<code>getter</code>和<code>setter</code>方法,Java的方法很喜歡寫成<code>actions</code>或者<code>queries</code>等,比如Java會使用<code>getCell()</code>,而不用<code>cellForRowAtIndexPath</code>。

Android應用程式主要分為兩部分。第一部分是Java源代碼,以Java包結構排布,也可以根據自己的喜好進行結構排布。最基本的結構就是分為這幾個頂層目錄:activities、fragments、views、adapters和data(models和managers)。

第二部分是<code>res</code>檔案夾,就是“resource”的簡稱,<code>res</code>目錄存放的是圖檔、xml布局檔案,還有其它xml值檔案,是非代碼資源的一部分。在IOS上,圖檔隻需要比對兩個尺寸,而在Android上有很多種螢幕尺寸需要考慮,Android上用檔案夾來管理管理圖檔、字元串,還有其它的螢幕配置數值等。<code>res</code>檔案夾裡也含有類似IOS中<code>xib</code>檔案的<code>xml</code>檔案,還有存儲字元串資源、整數值,以及樣式的xml檔案。

Activities是Android APP最基本的可視單元,就像UIViewControllers是IOS最基本的顯示元件一樣。Android系統使用一個<code>Activity</code>棧來管理<code>Activity</code>,而IOS使用<code>UINavigationController</code>進行管理。當APP啟動的時候,Android系統會把<code>Main Activity</code>壓棧,值得注意的是這是還可以再運作别的APP Activity,然後把它放到Activity棧中。傳回鍵預設會從Activity棧進行pop操作,是以如果使用者按下傳回鍵,就可以切換運作已運作的App了。

Activities還可以用Intent元件初始化别的Activity,初始化時可攜帶資料。啟動一個新的Activity類似于IOS上建立一個<code>UIViewController</code>。最基本的啟動一個新的Activity的方式就是建立一個帶有data的Intent元件。Android上實作自定義Intent初始化器的最好方法就是寫一個靜态<code>getter</code>方法。在Activity結束的時候也可以傳回資料,在Activity結束的時候可以往Intent裡面放置額外的資料。

IOS和Android的一個大的差別是,任何一個在<code>AndroidManifest</code>檔案中注冊的Activity都可以作為程式的入口,為Activity設定一個<code>intent filter</code>屬性比如<code>“media intent”</code>,就可以處理系統的媒體檔案了。最好的例子就是編輯照片Activity。它可以打開一張照片,然後進行修改,最後在Activity結束時傳回修改後的照片。

附加提醒:要想在Activity和Fragment之間傳遞對象,必須要實作<code>Parcelable</code>接口,就像在IOS裡需要遵循協定一樣。還有,<code>Parcelable</code>對象可以存在于Activity或者Fragment的<code>savedInstanceState</code>裡,這樣在它們被銷毀後可以更容易重建它們的狀态。

下面就來看看怎麼在一個Activity中啟動另一個Activity,然後在第二個Activity結束時進行傳回。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<code>// A request code is a unique value for returning activities</code>

<code>private</code>

<code>static</code> <code>final</code> <code>int</code> <code>REQUEST_CODE_NEXT_ACTIVITY =</code><code>1234</code><code>;</code>

<code>protected</code>

<code>void</code> <code>startNextActivity() {</code>

<code>    </code><code>// Intents need a context, so give this current activity as the context</code>

<code>    </code><code>Intent nextActivityIntent =</code><code>new</code>

<code>Intent(</code><code>this</code><code>, NextActivity.</code><code>class</code><code>);</code>

<code>       </code><code>startActivityForResult(nextActivityResult, REQUEST_CODE_NEXT_ACTIVITY);</code>

<code>}</code>

<code>@Override</code>

<code>void</code> <code>onActivityResult(</code><code>int</code>

<code>requestCode,</code><code>int</code>

<code>resultCode, Intent data) {</code>

<code>    </code><code>switch</code>

<code>(requestCode) {</code>

<code>    </code><code>case</code>

<code>REQUEST_CODE_NEXT_ACTIVITY:</code>

<code>        </code><code>if</code>

<code>(resultCode == RESULT_OK) {</code>

<code>            </code><code>// This means our Activity returned successfully. For now, Toast this text. </code>

<code>            </code><code>// This just creates a simple pop-up message on the screen.</code>

<code>                </code><code>Toast.makeText(</code><code>this</code><code>,</code><code>"Result OK!"</code><code>, Toast.LENGTH_SHORT).show();</code>

<code>            </code><code>}</code>

<code>            </code><code>return</code><code>;</code>

<code>        </code><code>}   </code>

<code>        </code><code>super</code><code>.onActivityResult(requestCode, resultCode, data);</code>

<code>public</code>

<code>static</code> <code>final</code> <code>String activityResultString =</code><code>"activityResultString"</code><code>;</code>

<code>/*</code>

<code> </code><code>* On completion, place the object ID in the intent and finish with OK.</code>

<code> </code><code>* @param returnObject that was processed</code>

<code> </code><code>*/</code>

<code>void</code> <code>onActivityResult(Object returnObject) {</code>

<code>        </code><code>Intent data =</code><code>new</code>

<code>Intent();</code>

<code>(returnObject !=</code><code>null</code><code>) {</code>

<code>            </code><code>data.putExtra(activityResultString, returnObject.uniqueId);</code>

<code>        </code><code>}</code>

<code>    </code> 

<code>        </code><code>setResult(RESULT_OK, data);</code>

<code>        </code><code>finish();       </code>

Fragment的概念在Android上比較獨特,從Android3.0開始引入。Fragment是一個迷你版的控制器,可以顯示在Activity上。它有自己的狀态和邏輯,同時在一個螢幕上支援多個Fragment同時顯示。Activity充當Fragment的控制器,Fragment沒有自己的上下文環境,隻能依賴Activity存在。

使用Fragment最好的例子就是在平闆上的應用。可以在螢幕左邊放一個fragment清單,然後在螢幕的右邊放fragment的詳細資訊。Fragment可以把螢幕分成可重複利用的小塊,分别控制管理。不過要注意Fragment的生命周期,會有些細微的差别。

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

Fragment是實作Android結構化的一種新的方式,就像IOS中的不用<code>UITableview</code>而用<code>UICollectionView</code>實作清單資料結構化。因為隻使用Activity而不用Fragment的話,會簡單一些。不過,之後你會遇到麻煩。如果不使用Fragment代替全盤使用Activity的話,在後面需要利用intent和進行多螢幕支援的時候就會遇到困難。

下面看一個<code>UITableViewController</code>的例子和一個<code>ListFragment</code>的地鐵時刻表示例。

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

<code>@interface MBTASubwayTripTableTableViewController ()</code>

<code>@property (assign, nonatomic) MBTATrip *trip;</code>

<code>@end</code>

<code>@implementation MBTASubwayTripTableTableViewController</code>

<code>-(instancetype)initWithTrip:(MBTATrip *)trip</code>

<code>{</code>

<code>    </code><code>self = [super initWithStyle:UITableViewStylePlain];</code>

<code>    </code><code>if (self) {</code>

<code>        </code><code>_trip = trip;</code>

<code>        </code><code>[self setTitle:trip.destination];</code>

<code>    </code><code>}</code>

<code>    </code><code>return self;</code>

<code>-(void)viewDidLoad</code>

<code>    </code><code>[super viewDidLoad];</code>

<code>    </code><code>[self.tableView registerClass:[MBTAPredictionCell class] forCellReuseIdentifier:[MBTAPredictionCell reuseId]];</code>

<code>    </code><code>[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([MBTATripHeaderView class]) bundle:nil] forHeaderFooterViewReuseIdentifier:[MBTATripHeaderView reuseId]];</code>

<code>#pragma mark - UITableViewDataSource</code>

<code>-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView</code>

<code>    </code><code>return 1;</code>

<code>-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section</code>

<code>    </code><code>return [self.trip.predictions count];</code>

<code>#pragma mark - UITableViewDelegate</code>

<code>-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section</code>

<code>    </code><code>return [MBTATripHeaderView heightWithTrip:self.trip];</code>

<code>-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section</code>

<code>    </code><code>MBTATripHeaderView *headerView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:[MBTATripHeaderView reuseId]];</code>

<code>    </code><code>[headerView setFromTrip:self.trip];</code>

<code>    </code><code>return headerView;</code>

<code>-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath</code>

<code>    </code><code>UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[MBTAPredictionCell reuseId] forIndexPath:indexPath];</code>

<code>    </code><code>MBTAPrediction *prediction = [self.trip.predictions objectAtIndex:indexPath.row];</code>

<code>    </code><code>[(MBTAPredictionCell *)cell setFromPrediction:prediction];</code>

<code>    </code><code>return cell;</code>

<code>-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath</code>

<code>    </code><code>return NO;</code>

<code>- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath</code>

<code>    </code><code>[tableView deselectRowAtIndexPath:indexPath animated:YES];</code>

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

<code>class</code> <code>TripDetailFragment</code><code>extends</code>

<code>ListFragment {</code>

<code>    </code><code>/**</code>

<code>     </code><code>* The configuration flags for the Trip Detail Fragment.</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code>

<code>static</code> <code>final</code> <code>class</code> <code>TripDetailFragmentState {</code>

<code>        </code><code>public</code>

<code>static</code> <code>final</code> <code>String KEY_FRAGMENT_TRIP_DETAIL =</code><code>"KEY_FRAGMENT_TRIP_DETAIL"</code><code>;</code>

<code>    </code><code>protected</code>

<code>Trip mTrip;</code>

<code>     </code><code>* Use this factory method to create a new instance of</code>

<code>     </code><code>* this fragment using the provided parameters.</code>

<code>     </code><code>*</code>

<code>     </code><code>* @param trip the trip to show details</code>

<code>     </code><code>* @return A new instance of fragment TripDetailFragment.</code>

<code>static</code> <code>TripDetailFragment newInstance(Trip trip) {</code>

<code>        </code><code>TripDetailFragment fragment =</code><code>new</code>

<code>TripDetailFragment();</code>

<code>        </code><code>Bundle args =</code><code>new</code>

<code>Bundle();</code>

<code>        </code><code>args.putParcelable(TripDetailFragmentState.KEY_FRAGMENT_TRIP_DETAIL, trip);</code>

<code>        </code><code>fragment.setArguments(args);</code>

<code>        </code><code>return</code>

<code>fragment;</code>

<code>TripDetailFragment() { }</code>

<code>    </code><code>@Override</code>

<code>View onCreateView(LayoutInflater inflater, ViewGroup container,</code>

<code>                             </code><code>Bundle savedInstanceState) {</code>

<code>        </code><code>Prediction[] predictions= mTrip.predictions.toArray(</code><code>new</code>

<code>Prediction[mTrip.predictions.size()]);</code>

<code>        </code><code>PredictionArrayAdapter predictionArrayAdapter =</code><code>new</code>

<code>PredictionArrayAdapter(getActivity(), predictions);</code>

<code>        </code><code>setListAdapter(predictionArrayAdapter);</code>

<code>super</code><code>.onCreateView(inflater,container, savedInstanceState);</code>

<code>void</code> <code>onViewCreated(View view, Bundle savedInstanceState) {</code>

<code>        </code><code>super</code><code>.onViewCreated(view, savedInstanceState);</code>

<code>        </code><code>TripDetailsView headerView =</code><code>new</code>

<code>TripDetailsView(getActivity());</code>

<code>        </code><code>headerView.updateFromTripObject(mTrip);</code>

<code>        </code><code>getListView().addHeaderView(headerView);</code>

下面,我們來分析Android上特有的一些元件。

<code>ListView</code>和IOS的<code>UITableView</code>最像,也是使用最頻繁的元件之一。類似于UITableView的<code>UITableViewController</code>,<code>ListView</code>也有一個<code>ListActivity</code>,還有<code>ListFragment</code>。這些元件會更好地處理一些布局問題,也為操作資料擴充卡提供了便利,這個接下來會說到。下面這個例子就是使用<code>ListFragment</code>來展示資料,類似<code>TableView</code>的<code>datasource</code>。

關于datasource,Android上沒有datasource和delegate,隻有Adapter。Adapter有很多種形式,主要功能其實就是為了把datasource和delegate合在一起。Adapter拿到資料然後填充到Listview中,在ListView中初始化響應的元件并顯示出來,下面是arrayAdapter的使用:

<code>class</code> <code>PredictionArrayAdapter</code><code>extends</code>

<code>ArrayAdapter&lt;Prediction&gt; {</code>

<code>    </code><code>int</code>

<code>LAYOUT_RESOURCE_ID = R.layout.view_three_item_list_view;</code>

<code>PredictionArrayAdapter(Context context) {</code>

<code>        </code><code>super</code><code>(context, R.layout.view_three_item_list_view);</code>

<code>PredictionArrayAdapter(Context context, Prediction[] objects) {</code>

<code>        </code><code>super</code><code>(context, R.layout.view_three_item_list_view, objects);</code>

<code>View getView(</code><code>int</code>

<code>position, View convertView, ViewGroup parent)</code>

<code>    </code><code>{</code>

<code>        </code><code>Prediction prediction =</code><code>this</code><code>.getItem(position);</code>

<code>        </code><code>View inflatedView = convertView;</code>

<code>        </code><code>if</code><code>(convertView==</code><code>null</code><code>)</code>

<code>        </code><code>{</code>

<code>            </code><code>LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);</code>

<code>            </code><code>inflatedView = inflater.inflate(LAYOUT_RESOURCE_ID, parent,</code><code>false</code><code>);</code>

<code>        </code><code>TextView stopNameTextView = (TextView)inflatedView.findViewById(R.id.view_three_item_list_view_left_text_view);</code>

<code>        </code><code>TextView middleTextView = (TextView)inflatedView.findViewById(R.id.view_three_item_list_view_middle_text_view);</code>

<code>        </code><code>TextView stopSecondsTextView = (TextView)inflatedView.findViewById(R.id.view_three_item_list_view_right_text_view);</code>

<code>        </code><code>stopNameTextView.setText(prediction.stopName);</code>

<code>        </code><code>middleTextView.setText(</code><code>""</code><code>);</code>

<code>        </code><code>stopSecondsTextView.setText(prediction.stopSeconds.toString());</code>

<code>inflatedView;</code>

可以看到,adapter裡面有一個很重要的方法叫getView,和IOS的<code>cellForRowAtIndexPath</code>方法一樣。還有一個相似之處就是循環利用的政策,和IOS6上的實作很相似。在Android和IOS上循環利用View都很重要,事實上它對清單的實作有很大幫助。這個adapter很簡單,使用了一個内建的類<code>ArrayAdapter</code>來存放資料,也解釋了怎麼把資料填入<code>ListView</code>中。

IOS開發者在寫Android的過程中還要注意的就是Android的生命周期。可以先從Activity的生命周期文檔開始:

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

本質上Activity的生命周期很像UIViewController的生命周期,主要差別在于Android上可以任意銷毀Activity,是以保證Activity的資料和狀态很重要,如果在<code>onCreate()</code>中儲存了的話,可以在saved state中恢複Activity的狀态。最好的方法就是使用<code>saveInstanceState</code>來存儲bundled資料,例如下面的<code>TripListActivity</code>是示例工程的一部分,用來儲存目前顯示的資料:

<code>static</code> <code>Intent getTripListActivityIntent(Context context, TripList.LineType lineType) {</code>

<code>    </code><code>Intent intent =</code><code>new</code>

<code>Intent(context, TripListActivity.</code><code>class</code><code>);</code>

<code>    </code><code>intent.putExtra(TripListActivityState.KEY_ACTIVITY_TRIP_LIST_LINE_TYPE, lineType.getLineName());</code>

<code>    </code><code>return</code>

<code>intent;</code>

<code>static</code> <code>final</code> <code>class</code> <code>TripListActivityState {</code>

<code>static</code> <code>final</code> <code>String KEY_ACTIVITY_TRIP_LIST_LINE_TYPE =</code><code>"KEY_ACTIVITY_TRIP_LIST_LINE_TYPE"</code><code>;</code>

<code>TripList.LineType mLineType;   </code>

<code>void</code> <code>onCreate(Bundle savedInstanceState) {</code>

<code>   </code><code>super</code><code>.onCreate(savedInstanceState);</code>

<code>   </code><code>mLineType = TripList.LineType.getLineType(getIntent().getStringExtra(TripListActivityState.KEY_ACTIVITY_TRIP_LIST_LINE_TYPE));</code>

<code>}   </code>

還有一個要注意的地方就是螢幕旋轉:如果螢幕發生旋轉,會改變Activity的生命周期。也就是說,Activity會先被銷毀,然後再重建。如果已經儲存了資料和狀态,Activity可以重建原來的狀态,實作無縫重建。很多APP開發者在遇到APP旋轉時會出現問題,因為Activity沒有處理旋轉的改變。注意不要用鎖定螢幕的方向來解決這個問題,因為這樣會存在一個隐含的生命周期的bug,在某些情況下還是可能發生的。

Fragment的生命周期和Activity的很像,但是有一些差別:

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

還有一個問題就是Fragment和Activity通信的問題。需要注意的是<code>onAttach()</code>方法在<code>onActivityCreated()</code>方法之前被調用,這就意味着在fragment建立完成後Activity還不能保證已經存在。如果需要為父Activity設定接口或者代理,則需要在<code>onActivityCreated()</code>方法調用之後。

Fragment也有可能會在系統需要的時候被建立和銷毀。如果要儲存它的狀态,那麼也要像Activity一樣進行處理。下面這個是示例項目中的一個小例子,<code>trip</code>清單Fragment會記錄相應的資料,和上面的地鐵時間示例一樣:

<code>/**</code>

<code> </code><code>* The configuration flags for the Trip List Fragment.</code>

<code>static</code> <code>final</code> <code>class</code> <code>TripListFragmentState {</code>

<code>static</code> <code>final</code> <code>String KEY_FRAGMENT_TRIP_LIST_LINE_TYPE =</code><code>"KEY_FRAGMENT_TRIP_LIST_LINE_TYPE"</code><code>;</code>

<code>static</code> <code>final</code> <code>String KEY_FRAGMENT_TRIP_LIST_DATA =</code><code>"KEY_FRAGMENT_TRIP_LIST_DATA"</code><code>;</code>

<code> </code><code>* Use this factory method to create a new instance of</code>

<code> </code><code>* this fragment using the provided parameters.</code>

<code> </code><code>*</code>

<code> </code><code>* @param lineType the subway line to show trips for.</code>

<code> </code><code>* @return A new instance of fragment TripListFragment.</code>

<code>static</code> <code>TripListFragment newInstance(TripList.LineType lineType) {</code>

<code>    </code><code>TripListFragment fragment =</code><code>new</code>

<code>TripListFragment();</code>

<code>    </code><code>Bundle args =</code><code>new</code>

<code>    </code><code>args.putString(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_LINE_TYPE, lineType.getLineName());</code>

<code>    </code><code>fragment.setArguments(args);</code>

<code>TripList mTripList;</code>

<code>void</code> <code>setTripList(TripList tripList) {</code>

<code>    </code><code>Bundle arguments =</code><code>this</code><code>.getArguments();</code>

<code>    </code><code>arguments.putParcelable(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_DATA, tripList);</code>

<code>    </code><code>mTripList = tripList;</code>

<code>    </code><code>if</code>

<code>(mTripArrayAdapter !=</code><code>null</code><code>) {</code>

<code>        </code><code>mTripArrayAdapter.clear();</code>

<code>        </code><code>mTripArrayAdapter.addAll(mTripList.trips);</code>

<code>    </code><code>super</code><code>.onCreate(savedInstanceState);</code>

<code>(getArguments() !=</code><code>null</code><code>) {</code>

<code>        </code><code>mLineType = TripList.LineType.getLineType(getArguments().getString(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_LINE_TYPE));</code>

<code>        </code><code>mTripList = getArguments().getParcelable(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_DATA);</code>

還要注意的是,Fragment經常會在<code>onCreate</code>方法中利用<code>bundled</code>參數重建自己的狀态。而自定義的Trip清單模型類相關的<code>setter</code>方法也會把對象添加到<code>bundled</code>參數中。這樣就可以保證在Fragment被銷毀或者重建時,比如螢幕旋轉後,可以利用最新的資料去重建狀态。

和Android上其它部分的開發工作一樣,指定布局檔案也有自己的優缺點。Android上的布局檔案都存放在<code>res/layouts</code>檔案夾中,以易讀的xml形式存儲。

地鐵清單布局

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

<code>&lt;</code><code>RelativeLayout</code>

<code>xmlns:android</code><code>=</code><code>"http://schemas.android.com/apk/res/android"</code>

<code>    </code><code>xmlns:tools</code><code>=</code><code>"http://schemas.android.com/tools"</code>

<code>    </code><code>android:layout_width</code><code>=</code><code>"match_parent"</code>

<code>    </code><code>android:layout_height</code><code>=</code><code>"match_parent"</code>

<code>    </code><code>tools:context</code><code>=</code><code>"com.example.androidforios.app.activities.MainActivity$PlaceholderFragment"</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>ListView</code>

<code>        </code><code>android:id</code><code>=</code><code>"@+id/fragment_subway_list_listview"</code>

<code>        </code><code>android:layout_width</code><code>=</code><code>"match_parent"</code>

<code>        </code><code>android:layout_height</code><code>=</code><code>"match_parent"</code>

<code>        </code><code>android:paddingBottom</code><code>=</code><code>"@dimen/Button.Default.Height"</code><code>/&gt;</code>

<code>    </code><code>&lt;</code><code>Button</code>

<code>        </code><code>android:id</code><code>=</code><code>"@+id/fragment_subway_list_Button"</code>

<code>        </code><code>android:layout_height</code><code>=</code><code>"@dimen/Button.Default.Height"</code>

<code>        </code><code>android:minHeight</code><code>=</code><code>"@dimen/Button.Default.Height"</code>

<code>        </code><code>android:background</code><code>=</code><code>"@drawable/button_red_selector"</code>

<code>        </code><code>android:text</code><code>=</code><code>"@string/hello_world"</code>

<code>        </code><code>android:textColor</code><code>=</code><code>"@color/Button.Text"</code>

<code>        </code><code>android:layout_alignParentBottom</code><code>=</code><code>"true"</code>

<code>        </code><code>android:gravity</code><code>=</code><code>"center"</code><code>/&gt;</code>

<code>&lt;/</code><code>RelativeLayout</code><code>&gt;</code>

下面這個是IOS上用UITableView和UIButton來制作的類似效果:

iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點
iOS程式員必須知道的Android要點 iOS程式員必須知道的Android要點

可以發現,Android的布局檔案更容易閱讀和了解,而且提供了多種布局方式,我們隻介紹了其中的一小部分。

通常來說,我們接觸的最基本的UI結構就是<code>ViewGroup</code>的子類,RelativeLayout、LinearLayout、FrameLayout是最常用的。這些ViewGroup的子類可以容納别的View,并包含了一些排布控件的屬性。

一個很好的例子就是上面用到的<code>RelativeLayout</code>,在裡面可以使用<code>android:layout_alignParentBottom="true"</code>來把按鈕定位到布局底部。

最後,如果要在Fragment或者Activity中使用這些控件的話,可以在<code>onCreateView()</code>方法中使用布局的資源ID:

<code>View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {</code>

<code>inflater.inflate(R.layout.fragment_subway_listview, container,</code><code>false</code><code>);</code>

請使用dp(density-independent pixels),不直接使用dx(pixels);

不要在可視化編輯器中移動布局元件——通常來說可視化編輯器在你調好高和寬後,會為元件添加一些多餘的像素,是以最好就是直接操作xml檔案;

如果在布局的<code>height</code>和<code>width</code>看到有用<code>fill_parent</code>這個屬性的話,你會發現在API

8的時候這個屬性就已經被限制了,改用<code>match_parent</code>替換。

Android上的資料存儲也和IOS上差不多:

SharedPreferences、NSUserDefaults;

記憶體存儲對象;

internal、external檔案讀寫document directory檔案讀寫;

SQLite資料庫存儲Core Data形式資料庫存儲。

之前已經讨論的東西隻是描述了Android的大概 ,要想好好利用Android上的更多的特性,本人建議你看看下面的這些概念:

ActionBar,Overflow Menu,還有Menu Button;

跨應用間資料共享;

響應系統actions;

好好學習Java的特性:泛型、抽象方法和抽象類等等;

看看Google的低版本相容庫;

我們可以學到更多的解決問題的技巧和方式。因為兩平台的實作細節各不相同,也許了解Android的工作原理可以對IOS的下一個版本的開發工作有所幫助。系統之間有很多相似的地方,誰知道下個版本的IOS會出現什麼呢?

[ 轉載必須在正文中标注并保留原文連結、譯文連結和譯者等資訊。]

繼續閱讀