提醒對話框手機上的App極大地方便了人們的生活,很多業務只需用戶拇指一點即可輕鬆辦理,然而這也帶來了一定的風險,因為有時候用戶並非真的想這麼做,只是不小心點了一下而已,如果App不做任何提示的話,繼續吭哧吭哧兀自辦完業務,比如轉錯錢了、誤刪資料了,往往令用戶追悔莫及。所以對於部分關鍵業務,App為了 ...
提醒對話框
手機上的App極大地方便了人們的生活,很多業務只需用戶拇指一點即可輕鬆辦理,然而這也帶來了一定的風險,因為有時候用戶並非真的想這麼做,只是不小心點了一下而已,如果App不做任何提示的話,繼續吭哧吭哧兀自辦完業務,比如轉錯錢了、誤刪資料了,往往令用戶追悔莫及。所以對於部分關鍵業務,App為了避免用戶的誤操作,很有必要彈出消息對話框,提醒用戶是否真的要進行此項操作。這個提醒對話框便是App開發常見的AlertDialog,說起這個AlertDialog,安卓開發者都有所耳聞,該對話框不外乎消息標題、消息內容、確定按鈕、取消按鈕這四個要素,使用Java編碼顯示提醒對話框,基本跟下麵的示例代碼大同小異:
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("尊敬的用戶"); builder.setMessage("你真的要卸載我嗎?"); builder.setPositiveButton("殘忍卸載", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { tv_alert.setText("雖然依依不捨,但是只能離開了"); } }); builder.setNegativeButton("我再想想", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { tv_alert.setText("讓我再陪你三百六十五個日夜"); } }); AlertDialog alert = builder.create(); alert.show();
顯而易見上述代碼非常冗長,特別是兩個按鈕的點擊事件,又是匿名類又是函數重載,令人不堪卒讀。嘗試將以上Java代碼轉換為Kotlin代碼,則改寫後的Kotlin代碼如下所示:
val builder = AlertDialog.Builder(this) builder.setTitle("尊敬的用戶") builder.setMessage("你真的要卸載我嗎?") builder.setPositiveButton("殘忍卸載") { dialog, which -> tv_alert.text = "雖然依依不捨,還是只能離開了" } builder.setNegativeButton("我再想想") { dialog, which -> tv_alert.text = "讓我再陪你三百六十五個日夜" } val alert = builder.create() alert.show()
這下看來點擊事件的代碼在很大程度上簡化了,不過除此之外,整塊代碼依然顯得有些臃腫,尤其是運用了建造者模式的Builder類,雖然錶面上增強了安全性,但對於編碼來說其實是累贅。因此,Anko庫將其做了進一步的封裝,給Context類添加了一個擴展函數,即“alert(消息內容, 消息標題) { 幾個按鈕及其點擊事件 }”,簡化後的alert彈窗代碼舉例如下:
alert("你真的要卸載我嗎?", "尊敬的用戶") { positiveButton("殘忍卸載") { tv_alert.text = "雖然依依不捨,還是只能離開了" } negativeButton("我再想想") { tv_alert.text = "讓我再陪你三百六十五個日夜" } }.show()
現在的Kotlin代碼相比之下更方便閱讀了,並且代碼量還不到原來Java代碼的三分之一。當然,為了正常地使用這麼好的擴展函數,不要忘了在代碼文件頭部加上下麵一行導入語句:
import org.jetbrains.anko.alert
這麼精簡的Kotlin代碼,功能上可是一點都沒偷工減料的,它的提醒對話框效果與Java編碼一模一樣,都如下圖所示。
下拉選擇框
對於某些固定值的條件選擇,比如紅綠藍三原色選擇其一,一月份到十二月份選擇其中一個月份等等,這些情況在Android中用到了下拉框Spinner。界面上的Spinner控制項一開始是個右側帶向下箭頭的文本,點擊該文本會彈出一個選擇對話框,選中某一項之後,對話框消失,同時界面上的文本替換為剛纔選中的文本內容。光看下拉框的功能其實挺簡單的,可是若用Java代碼實現的話,就得費一番功夫了,下麵便是Spinner控制項的調用代碼例子:
private void initSpinner() { ArrayAdapter<String> starAdapter = new ArrayAdapter<String>(this, R.layout.item_select, starArray); starAdapter.setDropDownViewResource(R.layout.item_dropdown); Spinner sp = (Spinner) findViewById(R.id.sp_dialog); sp.setPrompt("請選擇行星"); sp.setAdapter(starAdapter); sp.setSelection(0); sp.setOnItemSelectedListener(new MySelectedListener()); } private String[] starArray = {"水星", "金星", "地球", "火星", "木星", "土星"}; class MySelectedListener implements OnItemSelectedListener { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { Toast.makeText(SpinnerDialogActivity.this, "你選擇的行星是"+starArray[arg2], Toast.LENGTH_LONG).show(); } public void onNothingSelected(AdapterView<?> arg0) {} }
不出所料這再次體現了Java編碼的尾大不掉,簡簡單單的功能在Java代碼中被分解為以下幾個專門的處理:
1、首先要定義一個數組適配器ArrayAdapter,指定待選擇的字元串數組,以及每項文本的佈局文件;
2、其次要定義一個選擇監聽器OnItemSelectedListener,在用戶選中某項時觸發,響應文本項的選中事件;
3、最後Spinner控制項依次設置選擇對話框的標題、數組適配器、選擇監聽器、預設選項等等;
我的天,這也太專業了吧,在產品經理看來,這隻是個下拉框而已,有必要搞這麼複雜嗎?然而Java代碼就是這麼錯綜複雜,要想開發Android,只能這麼搗騰,不然還有更好的法子嗎?不信的話換成Kotlin試試?說時遲那時快,在Android Studio上面把Spinner上述的Java代碼轉換為Kotlin,不一會兒就生成瞭如下的Kotlin代碼:
private fun initSpinner() { val starAdapter = ArrayAdapter(this, R.layout.item_select, starArray) starAdapter.setDropDownViewResource(R.layout.item_dropdown) val sp = findViewById(R.id.sp_dialog) as Spinner sp.prompt = "請選擇行星" sp.adapter = starAdapter sp.setSelection(0) sp.onItemSelectedListener = MySelectedListener() } private val starArray = arrayOf("水星", "金星", "地球", "火星", "木星", "土星") internal inner class MySelectedListener : OnItemSelectedListener { override fun onItemSelected(arg0: AdapterView<*>, arg1: View, arg2: Int, arg3: Long) { toast("你選擇的行星是${starArray[arg2]}") } override fun onNothingSelected(arg0: AdapterView<*>) {} }
瞧瞧,號稱終結者的Kotlin也不過爾爾,整體代碼量跟Java相比是半斤八兩,絲毫不見了往日的威風。由於這裡的Java代碼邏輯實在拐彎抹角,又是數組適配器又是選擇監聽器的,因此Kotlin對這種玩意確實沒有好辦法。既然此路不通,那就試試別的辦法唄,前面提到Spinner其實由兩部分組成,一部分是直接顯示在界面上的帶箭頭文本,另一部分是點擊後彈出的選擇對話框,所以能不能繞過Spinner,運用所見即所得的理念,乾脆把下拉框分離成兩個控制項好了。倘若僅僅是一個帶箭頭的文本,毫無疑問使用文本視圖TextView就可以了,箭頭圖標可以在佈局文件中通過drawableRight屬性來指定。於是佈局文件中的下麵Spinner節點:
<Spinner android:id="@+id/sp_dialog" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toRightOf="@+id/tv_dialog" android:gravity="left|center" android:spinnerMode="dialog" />
錶面上完全可以被下麵這個TextView節點所取代:
<TextView android:id="@+id/tv_spinner" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toRightOf="@+id/tv_dialog" android:gravity="center" android:drawableRight="@drawable/arrow_down" android:textColor="@color/black" android:textSize="17sp" />
如果再來一個選擇對話框,這樣只要給該文本視圖添加點擊事件,點擊TextView彈出選擇框,豈不是萬事大吉?正巧Anko庫已經提供了這股東風,與alert一樣來自於Context的擴展函數,它便是“selector(對話框標題, 字元串隊列) { i -> 第i項的選中處理代碼 }”,那麼將其與前面的文本視圖相結合,即可無縫實現原來的下拉框功能,具體的Kotlin代碼如下所示:
val satellites = listOf("水星", "金星", "地球", "火星", "木星", "土星") tv_spinner.text = satellites[0] tv_spinner.setOnClickListener { selector("請選擇行星", satellites) { i -> tv_spinner.text = satellites[i] toast("你選擇的行星是${tv_spinner.text}") } }
看看這幾行代碼,完全不見了數組適配器和選擇監聽器的蹤影,故而代碼量一下劇減到對應Java代碼的三分之一。當然,為了正常地使用selector函數,不要忘了在代碼文件頭部加上下麵一行導入語句:
import org.jetbrains.anko.selector
雖然把佈局文件裡面的Spinner控制項換成TextView,但是二者在功能使用上是沒什麼區別的,同樣支持點擊文本彈出選擇框,也同樣支持選中某項的回調。改造後下拉框的界面效果如下圖所示。
如此方便易用的selector,竟然撇開了數組適配器和選擇監聽器,那麼它又是怎麼實現的呢?認真閱讀Anko庫裡面的selector源碼,發現原來該函數利用了AlertDialog的setItems方法,通過setItems方法指定一串文本,並且定義了每項的點擊事件,其運行結果竟然與Spinner的選擇對話框殊途同歸。下麵給出AlertDialog對應selector函數的Java實現代碼,方便讀者理解它的本質:
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("請選擇行星"); builder.setItems(satellites, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(SpinnerDialogActivity.this, "你選擇的行星是"+starArray[arg2], Toast.LENGTH_LONG).show(); } }); builder.create().show();
進度對話框
App載入網頁之類的請求服務端行為,經常屬於耗時操作,往往要過好幾秒才能載入完畢,在此期間為了減少用戶的等待焦灼感,界面需要展示正在載入的動畫,一方面避免造成App卡死的錯覺,另一方面提示用戶耐心等待。這時就用到了進度對話框,在載入開始前彈出進度框,載入結束後關閉進度框,從而改善了載入交互的用戶體驗。
進度對話框分兩種,一種是水平進度對話框,另一種是圓圈進度對話框,下麵分別進行介紹。
水平進度對話框
水平進度對話框允許實時刷新當前進度,方便用戶知曉已處理的進展百分比。它主要包含幾個元素,包括消息標題、消息內容、對話框樣式(水平還是圓圈)、當前進度這四種,如果使用Java代碼實現該對話框,則是很常規的編碼風格,具體的Java代碼例子如下:
ProgressDialog dialog = new ProgressDialog(this); dialog.setTitle("請稍候"); dialog.setMessage("正在努力載入頁面"); dialog.setMax(100); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.show();
水平進度對話框的Java編碼,看起來十分中規中矩,可是仍然顯得拖泥帶水,很簡單的功能也花費了六行Java代碼。倘若使用Kotlin書寫,則藉助於Anko庫只需下麵兩行代碼:
val dialog = progressDialog("正在努力載入頁面", "請稍候") dialog.show()
瞧瞧,水平進度對話框的實現代碼頓時變得清爽了許多,其界面效果與Java是完全一樣的。當然,因為用到了Anko庫的擴展函數,所以務必在代碼頭部加上一行導入語句:
import org.jetbrains.anko.progressDialog
在水平進度對話框彈出之後,若想更新水平條的進度值,則可調用以下代碼設置當前進度:
dialog.progress = 進度值(取值為0到100)
當進度值達到100,意味著處理完成,此時即可調用對話框對象的dismiss函數關閉對話框,下圖展示了水平進度對話框的進度變化效果。
圓圈進度對話框
圓圈進度對話框僅僅展示轉圈的動畫效果,不支持實時刷新處理進度,自然在編碼上比水平對話框會簡化一些,可是用Java來顯示圓圈進度對話框,依舊需要下列的五行代碼:
ProgressDialog dialog = new ProgressDialog(this); dialog.setTitle("請稍候"); dialog.setMessage("正在努力載入頁面"); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); dialog.show();
如果用Kotlin實現該對話框的話,有了水平進度對話框的的先例,不出意料只需以下的兩行Kotlin代碼就行了:
val dialog = indeterminateProgressDialog("正在努力載入頁面", "請稍候") dialog.show()
註意到上面的Kotlin函數採取了首碼indeterminate,該單詞意思是“模糊的、不定的”,表示這種對話框的處理進度是不確定的,不像水平進度對話框可以明確指定當前進度,據此開發者能夠將progressDialog與indeterminateProgressDialog兩個函數區分開。由於該函數同樣來自於Anko庫,因此不要忘了在用到的代碼文件頭部加入下麵這行語句:
import org.jetbrains.anko.indeterminateProgressDialog
Kotlin實現的圓圈進度對話框,轉圈效果等同於Java實現的效果,具體的對話框界面如下圖所示。