1. 程式人生 > >Kotlin入門(20)幾種常見的對話框

Kotlin入門(20)幾種常見的對話框

set 文本替換 百分比 gac 終結者 its init 加載 運用

提醒對話框
手機上的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實現的效果,具體的對話框界面如下圖所示。

技術分享圖片

Kotlin入門(20)幾種常見的對話框