欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android學(xué)習(xí)項(xiàng)目之簡易版微信為例(二)

 更新時(shí)間:2016年06月20日 17:01:55   作者:lijihong0723  
這篇文章主要以簡易版微信為例,實(shí)現(xiàn)簡易版微信的登陸、注冊界面的編寫與簡單交互,感興趣的小伙伴們可以參考一下

1 概述

從這篇開始,正式進(jìn)入簡易版微信的開發(fā)。深入學(xué)習(xí)前,想談?wù)剛€(gè)人對(duì)Android程序開發(fā)一些理解,不一定正確,只是自己的一點(diǎn)想法。Android程序開發(fā)不像我們在大學(xué)時(shí)候?qū)慍控制臺(tái)程序那樣,需要從main開始寫代碼邏輯,大部分邏輯控制代碼都由自己來實(shí)現(xiàn)。事實(shí)上,Android已經(jīng)為我們提供了一個(gè)程序運(yùn)行的框架,我們只需要往框架中填入我們所需的內(nèi)容即可,這里的內(nèi)容主要是:四大組件——Activity、Service、ContentProvider、BroadCast。在這四大組件中,可以實(shí)現(xiàn)前端界面顯示和后端數(shù)據(jù)處理相關(guān)的代碼控制邏輯。關(guān)于前端界面顯示主要涉及到:組件的生命周期回調(diào)管理、注冊視圖(View)的事件監(jiān)聽器、集合類型視圖的數(shù)據(jù)適配器(Adapter)、不同窗口界面的跳轉(zhuǎn)等等。關(guān)于后臺(tái)數(shù)據(jù)的交互處理,主要涉及到:異步任務(wù)(AsyncTask)、Handler/Message、網(wǎng)絡(luò)編程(HTTP或Socket)、數(shù)據(jù)庫操作(SQLiteOpenHelper或ContentProvider)等等。所以,對(duì)我們初學(xué)者來說,學(xué)習(xí)Android主要就是學(xué)習(xí)Android框架中各個(gè)類的作用和使用方法。

好,下面開始本文內(nèi)容。當(dāng)?shù)谝淮问褂梦⑿牛ɑ蚱渌S玫腁ndroid應(yīng)用)的時(shí)候,首先就是注冊、登錄,本文就來實(shí)現(xiàn)這兩個(gè)基本功能。由于剛接觸Android開發(fā),所以需要了解很多基礎(chǔ)知識(shí)點(diǎn)。我們將通過這兩個(gè)功能的實(shí)現(xiàn),學(xué)習(xí)以下幾個(gè)Android開發(fā)的知識(shí)點(diǎn):

Layout布局:制作用戶界面,Android中使用XML文件描述UI布局,類似HTML+CSS方式的界面組件方式。對(duì)后端的童鞋來說,按UI設(shè)計(jì)稿進(jìn)行布局或按需求來定制一個(gè)控件或許是學(xué)習(xí)前端最大的障礙之一。關(guān)于UI布局,本文起一個(gè)頭,隨著我們的簡易版微信應(yīng)用深入開發(fā),我們就會(huì)慢慢熟悉Android的UI布局了;關(guān)于自己動(dòng)手開發(fā)一個(gè)視圖(View),這應(yīng)該也是Android開發(fā)中的難點(diǎn),我們將在后續(xù)文章中慢慢深入學(xué)習(xí)。

Activity概念及其生命周期:布局完成后,要將布局得到的UI界面顯示出來,這就需要引入Activity組件——負(fù)責(zé)UI界面的顯示和用戶的交互。Activity應(yīng)該是Android應(yīng)用最重要的組件了 —— 一個(gè)應(yīng)用可以沒有四大組件中的其他三大組件(即:內(nèi)容提供者ContentProvider、服務(wù)Service、廣播BroadCast),但不能沒有Activity —— 這個(gè)組件類似Windows編程中的窗口,在Windows中如果沒有窗口怎么與用戶交互?

登錄、注冊功能的實(shí)現(xiàn):講完Activity后,就需要通過Activity來加入我們需要的邏輯。Android應(yīng)用程序一般都是C(客戶端)/S(服務(wù)端)結(jié)構(gòu)的,注冊、登陸功能的實(shí)現(xiàn)包括客戶端邏輯的編寫和服務(wù)器端邏輯的編寫,我們將在第4小節(jié)介紹這兩個(gè)功能的客戶端和服務(wù)端的邏輯實(shí)現(xiàn)。

最后總結(jié)本篇博文內(nèi)容,并預(yù)告下篇博文內(nèi)容,那就讓我們開啟Android學(xué)習(xí)的第二課吧!

2 Android的MVC結(jié)構(gòu)

當(dāng)學(xué)習(xí)一門新技術(shù)時(shí),我們很少會(huì)思考這門技術(shù)重點(diǎn)學(xué)習(xí)什么,應(yīng)該怎么去學(xué)習(xí)之類的問題。大多數(shù)童鞋常常會(huì)一開始就一頭扎到知識(shí)點(diǎn)的海洋中,最后自己也搞不清學(xué)會(huì)了什么。比如學(xué)習(xí)Java,一上來就從變量命名開始學(xué)、接著學(xué)習(xí)表達(dá)式、控制流、面向?qū)ο?,如果初學(xué)者也許這是合適的,但如果你已經(jīng)學(xué)會(huì)C或C++,有些知識(shí)點(diǎn)似乎就不需要學(xué)習(xí)了。比如我之前包括現(xiàn)在主要用的是C++,那一上來就會(huì)學(xué)習(xí)I/O流、集合類這些常用的知識(shí)點(diǎn),就可以開發(fā)一些小程序了。有時(shí)間的話再去看看多線程、垃圾收集以及源代碼。

學(xué)習(xí)Android也一樣,首先應(yīng)該弄清楚應(yīng)該學(xué)一些什么,這就要從高一些的層次來看Android。從架構(gòu)上來說,和很多UI框架一樣,Android用的是主流的MVC結(jié)構(gòu),這應(yīng)該是比較成熟的前端框架了。MVC框架結(jié)構(gòu)如下圖:

MVC結(jié)構(gòu)分為三部分:

控制器(Controller)部分:接收用戶輸入,通過事件分發(fā)機(jī)制確定接收者。這部分在Android中已有框架完成,我們只需在Activity中向View視圖實(shí)例對(duì)象注冊特定監(jiān)聽器即可,監(jiān)聽器實(shí)現(xiàn)的具體邏輯由我們來寫;而且監(jiān)聽器只需要知道有這么回事就行,用到去API查就可以。

模型(Model)部分:這部分主要實(shí)現(xiàn)業(yè)務(wù)邏輯的處理和數(shù)據(jù)的更新。這部分應(yīng)該是Android編程的重點(diǎn),四大組件中的Service(服務(wù))、ContentProvide(內(nèi)容提供者)都是Model(模型)有關(guān)的,另外數(shù)據(jù)存儲(chǔ),如數(shù)據(jù)庫、文件等也屬于Model范疇,這部分應(yīng)該是Android學(xué)習(xí)的重點(diǎn)。

視圖(View)部分:這部分就是用于顯示模型數(shù)據(jù)。這部分在Android中就是使用View視圖進(jìn)行UI布局,有時(shí)框架提供的View部件不滿足需求時(shí),得根據(jù)需求重寫View,實(shí)現(xiàn)我們需要的效果。

這樣劃分之后,我們就大體上知道了一個(gè)Android軟件由哪些部分組成以及它們之間如何是交互的,Android框架已經(jīng)為我們實(shí)現(xiàn)了哪些功能 ,哪些功能需要我們擴(kuò)展的,這樣我們學(xué)習(xí)起來才會(huì)有的放矢。

3 Layout布局及分析

關(guān)于做軟件UI,博主曾經(jīng)有一段比較痛苦的回憶。記得那是在大三上學(xué)期學(xué)習(xí)完《數(shù)據(jù)庫系統(tǒng)概論》這門課程之后,老師要求用ASP.NET做一個(gè)網(wǎng)站。當(dāng)時(shí)博主做的是一個(gè)在線購書系統(tǒng),不懂怎么制作網(wǎng)頁界面,于是就在Visual Studio中以拖拽控件的方式來布局,最后雖然把系統(tǒng)倒騰出來了(過程可以說是十分痛苦),但界面看了實(shí)在無法讓人產(chǎn)生購買的欲望。經(jīng)歷過這么一出之后,博主對(duì)前端界面產(chǎn)生了恐懼感和厭惡感。不過,進(jìn)入公司參加工作以來,慢慢接觸到了軟件UI的設(shè)計(jì)與實(shí)現(xiàn)過程,同時(shí)自己也動(dòng)手實(shí)現(xiàn)了一些界面布局后,才讓這種恐懼感和厭惡感慢慢減少了。在這里,博主想來一句經(jīng)驗(yàn)之談:要想做一個(gè)漂亮的UI布局,不是通過拖拽控件能拖出來的。當(dāng)然,對(duì)初學(xué)者來說,可以通過通過拖拽控件的方式來學(xué)習(xí)Android框架。

Android制作UI界面有兩種方式:

(1)通過XML配置文件的方式,博主一般稱它為“聲明式布局”(不知對(duì)不對(duì)):這種方式就是把UI要顯示的控件及這些控件的顯示方式聲明在XML文件中,然后通過Activity的SetContentView接口將布局的描述文件設(shè)置給Activity;

(2)通過Java類來添加布局控件,并設(shè)置顯示相關(guān)的屬性,博主一般稱這一布局方式為“命令式布局”。

第(1)種布局方式,即聲明式布局,一般用于變化不大UI的布局;第(2)種布局方式,即命令式布局,一般用于程序運(yùn)行時(shí)不斷變化的UI界面的布局。本篇博文將實(shí)現(xiàn)的登陸、注冊功能采用的是聲明式布局,所以本小節(jié)僅介紹聲明式布局,命令式布局將在后續(xù)博文中用到時(shí)再做詳細(xì)闡述。

好了,理論的東西就不扯太多了,搞軟件開發(fā)的最怕聽到一大堆理論了,下面讓我們來看看登陸和注冊的布局界面的實(shí)現(xiàn)效果吧(可能還不是很完美,以后邊學(xué)習(xí)邊完善吧?。?。首先是登陸頁面(這也是打開軟件后的第一個(gè)頁面):

注冊頁面:

注冊、登錄之間交互與登錄成功后的界面,這里登錄成功后的界面上什么都沒有,所以在此就沒單獨(dú)貼出來了。圖片有點(diǎn)糊,湊合看看哈~

下面以登錄界面的代碼,來看看Android中如何實(shí)現(xiàn)界面布局的,整個(gè)UI布局代碼如下(代碼路徑:$res/layout/activity_login.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">

 <!--Top Panel-->
 <TextView
 android:layout_width="match_parent"
 android:layout_height="50dp"
 android:background="@color/colorTopPanelBackground"
 android:gravity="center"
 android:text="@string/string_login"
 android:textSize="@dimen/font_size_large"
 android:textColor="@color/colorSpecialWhite" />

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_margin="@dimen/activity_horizontal_margin"
 android:orientation="vertical">

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="50dip"
 android:orientation="horizontal"
 android:layout_marginTop="50dp">

 <TextView
 android:layout_width="50dip"
 android:layout_height="50dip"
 android:gravity="center_vertical|right"
 android:text="+86"
 android:textColor="@color/colorSpecialBlack"
 android:textSize="@dimen/font_size_medium" />

 <EditText
 android:id="@+id/edt_login_cellphone_number"
 android:layout_width="0dp"
 android:layout_height="50dip"
 android:layout_weight="1"
 android:layout_marginLeft="25dp"
 android:background="@null"
 android:hint="你的手機(jī)號(hào)碼"
 android:textSize="@dimen/font_size_medium"
 android:textColorHint="@color/colorHintText"/>

 </LinearLayout>

 <View
 android:id="@+id/dvd_login_username"
 android:layout_width="match_parent"
 android:layout_height="2px"
 android:background="@color/colorDefault" />

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="50dip"
 android:orientation="horizontal">

 <TextView
 android:layout_width="50dip"
 android:layout_height="50dip"
 android:gravity="center_vertical|right"
 android:text="@string/string_pass_word"
 android:textColor="@color/colorSpecialBlack"
 android:textSize="@dimen/font_size_medium" />

 <EditText
 android:id="@+id/edt_login_password"
 android:layout_width="0dp"
 android:layout_height="50dip"
 android:layout_weight="1"
 android:layout_marginLeft="25dp"
 android:background="@null"
 android:inputType="textPassword"
 android:textSize="@dimen/font_size_medium"
 android:hint="填入密碼"
 android:textColorHint="@color/colorHintText"/>

 </LinearLayout>

 <View
 android:id="@+id/dvd_login_password"
 android:layout_width="match_parent"
 android:layout_height="2px"
 android:background="@color/colorDefault" />

 <Button
 android:id="@+id/btn_login"
 android:layout_width="match_parent"
 android:layout_height="@dimen/button_general_height"
 android:layout_marginTop="50dip"
 android:background="@drawable/btn_common_selector"
 android:text="@string/string_login"
 android:textSize="@dimen/font_size_medium"
 android:textColor="@color/colorSpecialWhite"/>

 <Button
 android:id="@+id/btn_register"
 android:layout_width="match_parent"
 android:layout_height="@dimen/button_general_height"
 android:layout_marginTop="20dip"
 android:background="@drawable/btn_implicit_selector"
 android:text="@string/string_register"
 android:textSize="@dimen/font_size_medium" />

 </LinearLayout>

</LinearLayout>

上述代碼一層套一層,最終形成一個(gè)樹狀結(jié)構(gòu),如下圖所示:

圖中每個(gè)矩形就是一個(gè)控件(或稱為視圖),每個(gè)控件都有一套與它相關(guān)的外觀屬性(類似Web編程中的CSS),控制著該控件的顯示效果。下面對(duì)逐個(gè)控件及其外觀屬性進(jìn)行深入分析,從根節(jié)點(diǎn)開始:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">

根節(jié)點(diǎn)是一個(gè)布局控件,在這里用的是線性布局(LinearLayout),其它的布局控件還有相對(duì)布局(RelativeLayout)和幀布局(FrameLayout)。布局控件的作用就是堆砌(專業(yè)點(diǎn)的說法叫布置Arrange)控件:線性布局,顧名思義,布局方式只能按一個(gè)方向(水平horizontal或垂直vertical)堆砌控件,如上述代碼塊中,android:orientation屬性用于說明LinearLayout是水平橫向的線性布局;相對(duì)布局,這一布局方式比線性布局要復(fù)雜一下,控件之間位置關(guān)系不像線性布局那樣只能沿著一個(gè)方向,這種布局下,控件的位置根據(jù)已有的其他控件來確定的(該布局的具體實(shí)例將在后續(xù)博文中闡述);幀布局。另外,上述代碼塊中還有兩個(gè)屬性:android:layout_width和android:layout_height,用來描述該控件的寬與高,這也是每個(gè)控件都要填的屬性。這兩個(gè)屬性的值指定的是一個(gè)長度值,可以用像素(px)、點(diǎn)(pt)、設(shè)備獨(dú)立像素(dp或dip),這里用的是一個(gè)特殊值:match_parent——匹配父窗口,即長或?qū)捄透复翱谝粯?;另外一個(gè)特殊值是:wrap_content——內(nèi)容包裹,即長或?qū)捄涂臻g中內(nèi)容匹配,內(nèi)容所占區(qū)域有多大,控件的長或?qū)捑褪嵌啻蟆?/p>

<!--Top Panel-->
 <TextView
 android:layout_width="match_parent"
 android:layout_height="50dp"
 android:background="@color/colorTopPanelBackground"
 android:gravity="center"
 android:text="@string/string_login"
 android:textSize="@dimen/font_size_large"
 android:textColor="@color/colorSpecialWhite" />

這是第一個(gè)可視控件,為文本視圖(TextView),可以看到有很多屬性控制它的外觀顯示,如之前講過的寬度和高度,android:background描述該控件的背景色(很多Android控件也有這一屬性),這里采用的是引用資源的方式,采用這種方式可以提高代碼的可維護(hù)性,顏色資源具體定義在$res/values/colors.xml文件中(可以把它理解成程序設(shè)計(jì)中的常量),除了上述背景色資源,我們還定義了其他一些顏色資源,在下面的代碼中會(huì)用到:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <color name="colorPrimary">#11D31D</color>
 <color name="colorPrimaryDark">#308E0E</color>
 <color name="colorAccent">#FF4081</color>

 <!--color in default status-->
 <color name="colorDefault">#999999</color>

 <!--color in active status-->
 <color name="colorActive">@color/colorPrimary</color>

 <!--background color of top panel-->
 <color name="colorTopPanelBackground">#525252</color>

 <!--color of hint text-->
 <color name="colorHintText">#DDDDDD</color>

 <!--some special color-->
 <color name="colorSpecialBlack">#000000</color>
 <color name="colorSpecialWhite">#FFFFFF</color>

</resources>

之后一個(gè)屬性android:grivity用于描述控件中內(nèi)容的對(duì)齊方式,此處就是TextView中文本的對(duì)齊方式(為居中對(duì)齊)。再接下來一個(gè)屬性是android:text,用來指定TextView中的文本內(nèi)容,這里同樣是引用另一個(gè)資源文件中的字符串資源,文件位于$res\values\string。xml中,這個(gè)文件專門用來定義字符串常量,除了上述字符串外,還定義了一些其他字符串資源:

<resources>
 <string name="app_name">MyChat</string>

 <!-- TODO: Remove or change this placeholder text -->
 <string name="hello_blank_fragment">Hello blank fragment</string>

 <!--constant string used in resource-->
 <string name="string_nick_name">昵稱</string>
 <string name="string_pass_word">密碼</string>
 <string name="string_login">登錄</string>
 <string name="string_register">注冊</string>
 <string name="string_dialog_title">提示</string>
 <string name="string_dialog_tips_prefix">正在</string>
 <string name="string_dialog_tips_suffix">,請(qǐng)稍等...</string>

</resources>

接下來兩個(gè)屬性分別定義了文本的大小和顏色,同樣使用索引資源的方式,其中文本顏色使用的是前面已經(jīng)講過的顏色資源,文本大小的資源定義在$res\valuse\dimens.xml文件中,這一文件就是用來定義和尺寸有關(guān)的資源(如長度、大小等),在這個(gè)文件中還定義了其它一些尺寸資源,如下:

<resources>
 <!-- Default screen margins, per the Android Design guidelines. -->
 <dimen name="activity_horizontal_margin">16dp</dimen>
 <dimen name="activity_vertical_margin">16dp</dimen>

 <dimen name="horizontal_line_margin">20dp</dimen>
 <dimen name="contact_image_width">50dp</dimen>
 <dimen name="contact_image_height">50dp</dimen>
 <dimen name="context_image_top_buttom_margin">15dp</dimen>
 <dimen name="activity_registration_vertical_margin">16dp</dimen>

 <!--following tags define font size-->
 <dimen name="font_size_medium">16sp</dimen>
 <dimen name="font_size_small">14sp</dimen>
 <dimen name="font_size_large">18sp</dimen>
 <dimen name="font_size_xsmall">12sp</dimen>
 <dimen name="font_size_xlarge">20sp</dimen>

 <!--following tags defi-->
 <dimen name="button_general_height">40dp</dimen>

</resources>

到這里,我們就把第一個(gè)控件——頂部標(biāo)題的TextView控件——分析完了??梢钥吹剑瑸榱颂岣叽a的可維護(hù)性和復(fù)用性,我們將大多數(shù)屬性值都定義在相應(yīng)的資源文件中。下面的控件分析起來應(yīng)該就簡單多了,接下來又是一個(gè)布局控件,里面存放的是一個(gè)登陸表單:

<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_margin="@dimen/activity_horizontal_margin"
 android:orientation="vertical">

這里沒什么可以講的,這里有一個(gè)地方需要注意,新增了一個(gè)android:layout_margin屬性,用于描述控件的上、下、左、右外邊距,如下:

上、下、左、右外邊距也可以獨(dú)立控制,對(duì)于的屬性分別為:android:margin_Top、android:margin_Buttom、android:margin_Left和android:margin_Right。接下來就是表單的內(nèi)容區(qū)域了,首先要顯示兩個(gè)輸入框及其說明文字,輸入框使用的是EditText控件,說明文本使用的是TextView,它們是水平排列的,所以需要用線性布局把它們套起來,代碼如下:

<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="50dip"
 android:orientation="horizontal"
 android:layout_marginTop="50dp">

 <TextView
 android:layout_width="50dip"
 android:layout_height="50dip"
 android:gravity="center_vertical|right"
 android:text="+86"
 android:textColor="@color/colorSpecialBlack"
 android:textSize="@dimen/font_size_medium" />

 <EditText
 android:id="@+id/edt_login_cellphone_number"
 android:layout_width="0dp"
 android:layout_height="50dip"
 android:layout_weight="1"
 android:layout_marginLeft="25dp"
 android:background="@null"
 android:hint="你的手機(jī)號(hào)碼"
 android:textSize="@dimen/font_size_medium"
 android:textColorHint="@color/colorHintText"/>

 </LinearLayout>

 <View
 android:id="@+id/dvd_login_username"
 android:layout_width="match_parent"
 android:layout_height="2px"
 android:background="@color/colorDefault" />

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="50dip"
 android:orientation="horizontal">

 <TextView
 android:layout_width="50dip"
 android:layout_height="50dip"
 android:gravity="center_vertical|right"
 android:text="@string/string_pass_word"
 android:textColor="@color/colorSpecialBlack"
 android:textSize="@dimen/font_size_medium" />

 <EditText
 android:id="@+id/edt_login_password"
 android:layout_width="0dp"
 android:layout_height="50dip"
 android:layout_weight="1"
 android:layout_marginLeft="25dp"
 android:background="@null"
 android:inputType="textPassword"
 android:textSize="@dimen/font_size_medium"
 android:hint="填入密碼"
 android:textColorHint="@color/colorHintText"/>

 </LinearLayout>

 <View
 android:id="@+id/dvd_login_password"
 android:layout_width="match_parent"
 android:layout_height="2px"
 android:background="@color/colorDefault" />

上述代碼中的大部分屬性在前面都已經(jīng)介紹過了,新增的屬性有只有三個(gè),下面分別介紹。android:inputType用于描述輸入框的輸入類型,如這里用到的是密碼類型:textPassword,這樣就可以將輸入的字母變成一個(gè)個(gè)小點(diǎn)點(diǎn),如下:

對(duì)于EditText編輯框控件,還有其他輸入類型(input type),如下:

(1)text

(2)textEmailAddress

(3)textUri

(4)number

(5)phone

設(shè)置不同的輸入類型,運(yùn)行時(shí)效果就是輸入文本時(shí),彈出的軟鍵盤不同,如inputType設(shè)置為textEmailAddress時(shí),則鍵盤上多了一個(gè)@符號(hào),

當(dāng)inputType設(shè)置為number或phone時(shí),則軟鍵盤為:

剩下的兩個(gè)屬性都和輸入框都和提示文字有關(guān),分別為android:hint和android:textColorHint,分別用于描述提示文字的文本內(nèi)容和文本顏色,如下:

上圖中輸入框下面有一條綠色的橫線,這里采用的做法是設(shè)置一個(gè)高度為2px(一般來說,1px就可以,不過個(gè)人感覺在這里不夠明顯,所以就設(shè)為2px了)、寬度為match_parent的View,如下:

<View
 android:id="@+id/dvd_login_password"
 android:layout_width="match_parent"
 android:layout_height="2px"
 android:background="@color/colorDefault" />

再接下來就是兩個(gè)Button按鈕,分別用于觸發(fā)登錄、注冊操作:

代碼如下:

<Button
 android:id="@+id/btn_login"
 android:layout_width="match_parent"
 android:layout_height="@dimen/button_general_height"
 android:layout_marginTop="50dip"
 android:background="@drawable/btn_common_selector"
 android:text="@string/string_login"
 android:textSize="@dimen/font_size_medium"
 android:textColor="@color/colorSpecialWhite"/>

 <Button
 android:id="@+id/btn_register"
 android:layout_width="match_parent"
 android:layout_height="@dimen/button_general_height"
 android:layout_marginTop="20dip"
 android:background="@drawable/btn_implicit_selector"
 android:text="@string/string_register"
 android:textSize="@dimen/font_size_medium" />

這些代碼在之前都有所涉及,在這里也不再贅述了。這里有一點(diǎn)特殊:設(shè)置背景用了一個(gè)稱為選擇器(selector)的資源——因?yàn)榘粹o按下和彈起的時(shí)候,其背景是不一樣的,如下為按下狀態(tài):

和前面的彈起時(shí)的狀態(tài)比較一下,背景色變深了,這就用選擇器資源了。該資源為$res\color目錄下(注:創(chuàng)建Android工程時(shí),默認(rèn)是沒有這個(gè)文件夾的,需要手動(dòng)創(chuàng)建),內(nèi)容如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:state_pressed="true"
 android:color="#308E0E"/> <!-- pressed -->
 <item android:color="#11D31D"/> <!-- default -->
</selector>

分別指定了按下和默認(rèn)的顏色。到此為止,我們將登陸布局頁面講完了,注冊頁面和登陸頁面沒什么區(qū)別,在這里就不在闡述了。最后,這里還想分享一些個(gè)人經(jīng)驗(yàn):

(1)布局其實(shí)就是兩步:第一步:確定用什么控件;第二步:為控件配上屬性;

(2)這么多屬性,書寫時(shí)最好要有一個(gè)次序,我的做法是:第一個(gè)是id屬性;其次是必要的寬、高屬性,接下來是布局相關(guān)的屬性,如內(nèi)外邊距等,再接著是背景色、內(nèi)容的對(duì)齊方式等,最后是控件內(nèi)容,如文字內(nèi)容、顏色、大小等,這是一個(gè)由外到內(nèi)、從通用屬性到特殊屬性的一個(gè)書寫次序。

(3)盡量將一些屬性值定義在資源文件中,便于代碼的后期維護(hù)和復(fù)用。

3 Activity概念及其生命周期

第2小結(jié)詳細(xì)講述了登錄界面,介紹了涉及到的View組件及相關(guān)外觀樣式屬性。定義在xml中的UI布局文件只是一個(gè)靜態(tài)文件,需要加載到應(yīng)用程序中,才能被渲染并顯示出來,這就要用到本節(jié)所講的Activity(活動(dòng))。本節(jié)主要總結(jié)Activity的一些理論知識(shí),包括Activity的概念和生命周期。

3.1 什么是Activity

很多關(guān)于Android編程的書籍對(duì)Activity的概念都或多或少有一些闡述,但個(gè)人感覺都不是很系統(tǒng)。本文在這里拋出一塊磚,總結(jié)一下自己對(duì)Android中Activity組件的理解(當(dāng)然有很多不完善的地方,以后我會(huì)慢慢補(bǔ)充)如下:

(I)從MVC模式的角度看,Activity相當(dāng)于Controller——一邊是接收用戶請(qǐng)求,另一邊將請(qǐng)求分發(fā)(dispatch)到各處理單元中;也就是在一個(gè)Android中,Activity起到一個(gè)核心的作用:

圖中標(biāo)出了通過Activity啟動(dòng)各大組件的APIs,這些APIs都定義在Activity中。當(dāng)然,上述圖示僅是一個(gè)簡單的模型,在接收用戶請(qǐng)求時(shí)可能還會(huì)用到其他組件,在圖中并沒有一一給出,這些組件會(huì)在后續(xù)博文中深入學(xué)習(xí)。

(II)從設(shè)計(jì)模式模式的角度看,Activity可以看做是一個(gè)門面模式(Facade Pattern)。在Activity中聚合了很多組件,見下圖:

對(duì)內(nèi)封裝復(fù)雜組件,對(duì)外提供簡單的接口,同時(shí)也能獨(dú)立獲取這些組件實(shí)例,這就是門面模式的典型應(yīng)用吧!這里需要注意的是,Activity繼承自ContextThemeWrapper,在ContextThemeWrapper中聚合了Resource,通過它訪問程序資源;而ContextThemeWrapper繼承自ContextWrapper,通過它可以得到內(nèi)容解析器(ContentResolver)等組件。總的來說,Activity提供了很多功能,封裝了很多組件,使用起來也非常靈活。

(III)從一個(gè)Android開發(fā)者角度看,Activity是一個(gè)狀態(tài)機(jī)。Activity定義了管理一個(gè)活動(dòng)的生命周期的一系列事件,通過這些事件可以保存應(yīng)用程序的狀態(tài),這些事件將在3.2中闡述。

3.2 Activity的生命周期

Activity的生命周期是每一本講Android編程的書必講的內(nèi)容,也是Android程序設(shè)計(jì)的重點(diǎn)。Android的四大組件都有生命周期的概念,但Activity的生命周期最復(fù)雜,下圖是來自Android SDK文檔的一張Activity生命周期事件回調(diào)函數(shù)的調(diào)用次序:

這個(gè)圖有點(diǎn)像操作系統(tǒng)課中進(jìn)程狀態(tài)轉(zhuǎn)換圖——各種狀態(tài)切來切去。初看這張圖的時(shí)候,應(yīng)該會(huì)感覺有點(diǎn)亂,其實(shí)理清楚了的話,圖中顯示了也就是四條狀態(tài)變換路徑:

(I)中間垂直方向走下來:Activity launched → onCreate→ onStart→ onResume→ running→ onPause→ onStop→ onDestroy→ Activity shutdown

   注:這種情況下,用戶打開應(yīng)用程序首頁,做了事情后就退出應(yīng)用了。

(2)內(nèi)圈:Activity launched → onCreate→ onStart→ onResume→ running→ onPause→ User navigates to the activity→ onResume→ ……

(3)中圈:Activity launched → onCreate→ onStart→ onResume→ running→ onPause→ onStop→ User navigates to the activity→ onRestart→ onResume→ ……    

(4)外圈:Activity launched → onCreate→ onStart→ onResume→ running→ onPause [→ onStop→]App Process Killed→User navigates to the activity→ onCreate→ onRestart→ onResume→ ……

   注:標(biāo)紅加方括號(hào)的onStop表示可有可無,也就是外圈有兩條路線——Pause狀態(tài)下被Kill掉和Stop狀態(tài)下被Kill掉。

正因?yàn)橛羞@么多狀態(tài)變換路徑,就是因?yàn)橛脩艚换?fù)雜導(dǎo)致。下面,對(duì)生命周期的事件回調(diào)做以下簡單說明:

(1)onPause和onResume對(duì)應(yīng),onStop和onStart/onRestart對(duì)應(yīng);

(2)執(zhí)行過onPause、onStop和onDestroy的Activity在系統(tǒng)內(nèi)存不足的情況下都有可能被Kill掉,當(dāng)然Kill的優(yōu)先級(jí)不同,Destroy的最先被Kill,其次是Stop的,實(shí)在沒轍了才Kill Pause狀態(tài)的Activity。

(3)對(duì)于被Kill掉的Activity,如果用戶重新回到那個(gè)Activity的話,需要再次調(diào)用onCreate方法,創(chuàng)建Activity實(shí)例;

(4)onStop不一定始終都被執(zhí)行,如Pause狀態(tài)的Activity也可能被Kill掉,所以保存應(yīng)用程序狀態(tài)數(shù)據(jù)的代碼,應(yīng)該寫在onPause中;

(5)各事件回調(diào)函數(shù)的調(diào)用時(shí)機(jī)及Activity狀態(tài)如下:

4 登錄功能的實(shí)現(xiàn)詳解

從這節(jié)開始,我們正式進(jìn)入登錄、注冊功能的實(shí)現(xiàn)。由于使用C/S結(jié)構(gòu),所以分為客戶端和服務(wù)器端兩個(gè)部分,客戶端和服務(wù)器端之間交互采用HTTP協(xié)議,如下:

4.1 客戶端邏輯

客戶端繼承Activity得到兩個(gè)子類,LoginActivity和ResgiterActivity。注冊功能的客戶端代碼與登錄類似,在此不再贅述。下面來看LoginActivity的代碼邏輯,在LoginActivity中主要重寫了onCreate方法,這一方法中首先加載該Activity的UI,即$res/layout/activity_login.xml(在第2小節(jié)已經(jīng)詳細(xì)分析過了),然后對(duì)注冊、登陸兩個(gè)按鈕添加監(jiān)聽器,代碼邏輯如下:

@Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);

 setContentView(R.layout.activity_login);

 mLoginButton = (Button) findViewById(R.id.btn_login);
 mRegisterButton = (Button) findViewById(R.id.btn_register);
 mEditTextUserName = (EditText) findViewById(R.id.edt_login_cellphone_number);
 mEditTextPassword = (EditText) findViewById(R.id.edt_login_password);
 mDvdPassword = findViewById(R.id.dvd_login_password);
 mDvdUserName = findViewById(R.id.dvd_login_username);

 mLoginButton.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {

 Log.d("OnClick", "Enter the click callback of Login Button");

 String cellphone_number = mEditTextUserName.getText().toString().trim();
 String pass_word = mEditTextPassword.getText().toString().trim();

 Map<String, String> params = new HashMap<String, String>();
 params.put("url", LOGIN_PATH);
 params.put("cellphone_number", cellphone_number);
 params.put("pass_word", pass_word);

 new LoginAsyncTask(LoginActivity.this).execute(params);
 }
 });

 mRegisterButton.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 // Enter into the register activity
 Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
 startActivity(intent);
 }
 });

 mEditTextPassword.setOnFocusChangeListener(new View.OnFocusChangeListener() {
 @Override
 public void onFocusChange(View v, boolean hasFocus) {
 if (hasFocus) {
  mDvdPassword.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
 } else {
  mDvdPassword.setBackgroundColor(getResources().getColor(R.color.colorDefault));
 }
 }
 });

 mEditTextUserName.setOnFocusChangeListener(new View.OnFocusChangeListener() {
 @Override
 public void onFocusChange(View v, boolean hasFocus) {
 if (hasFocus) {
  mDvdUserName.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
 } else {
  mDvdUserName.setBackgroundColor(getResources().getColor(R.color.colorDefault));
 }
 }
 });
 }

說明:

(1)第5行,調(diào)用setContentView設(shè)置UI布局文件,這句代碼必須先寫上,否則后面的View視圖都沒法獲得;

(2)第7~12行,調(diào)用findViewById方法得到對(duì)應(yīng)ID的View,此處的ID在編寫UI布局時(shí)指定;

(3)通過匿名內(nèi)部類的方式,向登錄按鈕注冊Click事件監(jiān)聽器,在該事件監(jiān)聽器的內(nèi)部邏輯中,首先從手機(jī)號(hào)的編輯框和密碼的編輯框中獲得文本內(nèi)容,然后通過一個(gè)異步任務(wù)(AsyncTask)將內(nèi)容發(fā)送到服務(wù)器端,異步任務(wù)的代碼如下:

public class LoginAsyncTask extends AsyncTask<Map<String, String>, Void, Boolean> {

 private ProgressDialog mDialog;
 private Context mContext;

 public LoginAsyncTask(Context context) {
 mDialog = new ProgressDialog(context);
 mDialog.setTitle("提示信息");
 mDialog.setMessage("正在登錄,請(qǐng)稍等...");

 mContext = context;
 }

 @Override
 protected Boolean doInBackground(Map<String, String>... params) {
 String url = params[0].get("url");

 Map<String, String> mapParams = new Hashtable();
 for (Map.Entry<String, String> entry : params[0].entrySet()) {
 if (!entry.getKey().equals("url")) {
 mapParams.put(entry.getKey(), entry.getValue());
 }
 }

 String result = null;
 try {
 result = HttpUtil.sendPostRequest(url, mapParams, "utf-8");
 } catch (Exception e) {
 e.printStackTrace();
 }

 return result.equals("True") ? true : false;
 }

 @Override
 protected void onPostExecute(Boolean result) {
 super.onPostExecute(result);
 if (mDialog.isShowing()) mDialog.dismiss();
 if (result) {
 // jump to Main page
 Intent intent = new Intent(mContext, MainActivity.class);
 mContext.startActivity(intent);
 } else {
 Toast.makeText(mContext, "登錄失敗!", Toast.LENGTH_LONG).show();
 }
 }
}

異步任務(wù),顧名思義就是要開啟一個(gè)線程來執(zhí)行的代碼邏輯。在Android中,顯示應(yīng)用程序界面和接受用戶輸入的代碼都是在UI線程中執(zhí)行,所以UI線程一般不允許阻塞,否則會(huì)造成用戶體驗(yàn)差。對(duì)一些耗時(shí)操作,需要由非UI線程來執(zhí)行,執(zhí)行完成后的結(jié)果由UI線程來更新。在這里,因?yàn)樘峤挥脩裘兔艽a的網(wǎng)絡(luò)操作耗時(shí)較長,如果直接在UI線程中執(zhí)行的話,會(huì)導(dǎo)致UI線程阻塞,引起Android Not Responding異常(見下圖,很熟悉吧),所以得放在異步任務(wù)中執(zhí)行。

Android框架中在Java多線程框架之上,引入了AsyncTask(異步任務(wù))和Handler/Message/Loop兩種機(jī)制來實(shí)現(xiàn)多線程編程。下面對(duì)異步任務(wù)做一個(gè)簡單說明(詳細(xì)講的話可能需要一小節(jié)的內(nèi)容),Handler/Message/Loop機(jī)制較復(fù)雜,后續(xù)用到了我們再介紹。AsyncTask是一個(gè)抽象類,必須要重寫的方法為doInBackground方法,這個(gè)方法運(yùn)行在后臺(tái)線程中,在上述代碼中就是執(zhí)行發(fā)送網(wǎng)絡(luò)請(qǐng)求,并對(duì)返回結(jié)果進(jìn)行解析。另外還有兩個(gè)函數(shù)也比較重要,onPreExecute和onPostExecute。onPreExecute運(yùn)行在UI線程中,并且在doInBackground之前調(diào)用,主要用于異步任務(wù)的初始化,例如顯示進(jìn)度對(duì)話框等;onPostExecute在doInBackground之后調(diào)用,也是運(yùn)行于UI線程,主要用于異步任務(wù)結(jié)果在UI中的更新顯示。上述代碼中主要是根據(jù)返回結(jié)果,判斷登陸是否成功,如果成功,則跳轉(zhuǎn)到MainActivity,即通過startActivity開啟一個(gè)新的Activity,即應(yīng)用程序的主界面;如果失敗,則彈出一個(gè)Toast提示用戶登錄失敗。

另外可以看到AsyncTask是一個(gè)模板類,有三個(gè)模板參數(shù),如上述程序中AsyncTask<Map<String, String>, Void, Boolean>,其中第一個(gè)模板參數(shù),如Map<String, String>,用于指定doInBackground的入?yún)㈩愋?;第三個(gè)模板參數(shù),如此處的Boolean,是doInBackground的返回值類型,同時(shí)是onPostExecute的入?yún)㈩愋?;第二個(gè)模板參數(shù),用于指定進(jìn)度值的類型(可以為Integer或Float等),也就是說,異步任務(wù)可以將進(jìn)度值更新到UI線程中顯示,在這里由于沒有用到進(jìn)度條刻度信息,所以類型設(shè)為Void。

發(fā)送命令,獲取服務(wù)器的返回結(jié)果的邏輯,我們封裝在HttpUtil類中,如下:

public class HttpUtil {

 public static String sendPostRequest(
 String path, Map<String, String> params, String encoding)
 throws Exception {

 StringBuilder sb = new StringBuilder();
 if (params != null && !params.isEmpty()) {
 for (Map.Entry<String, String> entry : params.entrySet()) {
 sb.append(entry.getKey()).append("=");
 sb.append(URLEncoder.encode(entry.getValue(), encoding));
 sb.append("&");
 }
 sb.deleteCharAt(sb.length() - 1);
 }

 HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
 conn.setConnectTimeout(5000);
 conn.setRequestMethod("POST");
 conn.setDoOutput(true);

 OutputStream os = conn.getOutputStream();
 os.write(sb.toString().getBytes());
 os.flush();

 if (conn.getResponseCode() == 200) {
 String result = StreamTool.readStream(conn.getInputStream());
 return result;
 } else {
 return null;
 }
 }
}

主要邏輯就是把命令參數(shù)用&符號(hào)鏈接起來,寫到HttpURLConnection中,并通過HttpURLConnection發(fā)送到服務(wù)器端;服務(wù)器返回后,讀取狀態(tài)碼,如果為200,則表明連接執(zhí)行成功,此時(shí)讀取從服務(wù)器返回的值,通過StreamTool從返回的流中讀取,代碼邏輯如下:

public class StreamTool {

 public static String readStream(InputStream stream) throws IOException {

 StringBuilder sb = new StringBuilder();
 BufferedReader in = new BufferedReader(new InputStreamReader(stream));

 String line;
 while ((line = in.readLine()) != null) {
 sb.append(line);
 System.out.println("===>" + line);
 }

 return sb.toString();
 }

}

至此,登陸功能的客戶端代碼就寫完了。有興趣的童鞋可以到云盤上去下載源代碼:http://pan.baidu.com/s/1skHOkxB(有空去注冊一個(gè)github,感覺用百度云盤分享太low,^__^)。

4.2 服務(wù)端邏輯

服務(wù)器端就是寫兩個(gè)Servlet,LoginServlet和RegisterServlet。我們將數(shù)據(jù)庫查詢和修改操作封裝在UserDAOImpl中,在LoginServlet和RegisterServlet中調(diào)用UserDAOImpl的接口,實(shí)現(xiàn)用戶信息的驗(yàn)證和添加,下面仍然以LoginServlet為例。

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 request.setCharacterEncoding("utf-8");
 response.setCharacterEncoding("utf-8");
 
 // parse parameters from client.
 String cellphone_number = request.getParameter("cellphone_number");
 String pass_word = request.getParameter("pass_word");
 
 PrintWriter writer = response.getWriter();
 
 try {
 UserDAOInterface userDAO = new UserDAOImpl();
 User user = userDAO.queryByCellPhoneNumber(cellphone_number);
 
 if (user != null && user.getPassWord().equals(pass_word))
 {
 writer.append("True");
 System.out.println("True");
 }
 else
 {
 writer.append("False");
 System.out.println("False");
 }
 } catch (SQLException | ClassNotFoundException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } finally {
 
 }
 }

簡單解釋一下:獲取從客戶端提交的參數(shù),分別是手機(jī)號(hào)和用戶密碼,通過手機(jī)號(hào)從數(shù)據(jù)庫查詢對(duì)應(yīng)的用戶(封裝在User實(shí)例中)記錄,如果用戶不為空,且用戶密碼正確,則返回True,否則返回為空。服務(wù)器端的代碼就是Java Web編程,所以在這里就不詳細(xì)討論了。

5 總結(jié)

最后總結(jié)一下,本文我們主要學(xué)習(xí)了Layout布局以及展示布局的組件——Activity,對(duì)涉及到的View進(jìn)行較詳細(xì)的分析,對(duì)Activity的概念和生命周期回調(diào)也進(jìn)行了介紹,最后以介紹了登錄功能的代碼實(shí)現(xiàn),注冊功能和登陸功能類似,感興趣的童鞋可以把代碼down下來瞅瞅。

下一次將介紹好友列表功能的實(shí)現(xiàn),敬請(qǐng)關(guān)注^__^

源碼下載:http://xiazai.jb51.net/201606/yuanma/MyChat(jb51.net).rar

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論