Android 提醒微技巧

小陽世界2018發表於2016-10-21

1. Dialog

首先來介紹一下Dialog的用法吧,其實很簡單,相信大多數人都是經常使用的:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">AlertDialog.Builder builder = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> AlertDialog.Builder(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
builder.setTitle(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Title"</span>)
       .setMessage(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Dialog content."</span>)
       .setPositiveButton(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"OK"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DialogInterface.OnClickListener() {
               <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
               <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onClick</span>(DialogInterface dialog, 
               <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> which) {
               }
       })
       .setNegativeButton(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Cancel"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DialogInterface.OnClickListener() {
               <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
               <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onClick</span>(DialogInterface dialog,
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> which) {
               }
        })
       .show();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

這段程式碼就可以彈出一個非常精美的Dialog了,如下圖所示: 

現在這個Dialog是Material Design風格的,因為我是在6.0系統上執行的,因此會自動賦予這樣的風格。但是如果在老版本系統上執行,比如說2.3系統,會是什麼樣的效果呢?執行一下就知道了,如下圖所示: 

額。。這個效果就比較醜了,不過沒辦法,這就是2.3系統當時的風格呀。

人的審美總是在進步的,我們有沒有什麼辦法在老版本的系統中也使用Material Design風格的Dialog呢?當然有,Google已經充分考慮到了這一點,在appcompat-v7庫中也提供了一個AlertDialog類,完整路徑是:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">android.support.v7.app.AlertDialog</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

我們使用這個包中的AlertDialog,就能讓對話方塊在所有的系統版本中都保持一致的風格了。現在在2.3系統中重新執行一下,效果如下所示: 

可以看到,現在的效果就比較不錯了,這也算是一個小技巧吧。

Dialog的作用是給使用者一個提示資訊,並讓使用者根據提示做出判斷。而Dialog的特徵就是,它會阻止你原本正在進行的操作,必須停止下來對Dialog進行處理。但是,大多數的人可能並不喜歡這樣被打斷,也許使用者正在處理一項重要的操作,突然彈出一個Dialog遮擋住了他原本的操作,這個時候使用者會變得很惱火。

因此,使用Dialog的時候還是謹慎一點比較好,儘量不要給使用者帶來糟糕的體驗感。

2. Toast

說到不會阻擋使用者原本正在進行的操作,這就延伸到我們今天的第二個主題,Toast。Toast只會彈出一段資訊,告訴使用者某某事情已經發生了,過一段時間後就會自動消失。它完全不會阻擋使用者的任何操作,甚至使用者也可以完全不用理會Toast。

那麼我們還是先來看一下Toast的基本用法吧,如下所示:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Toast.makeText(context, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"things happened"</span>, Toast.LENGTH_SHORT).show();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

最後一個引數用於指定Toast顯示的時長,Toast.LENGTH_SHORT表示顯示時間較短,Toast.LENGTH_LONG表示顯示時間較長。

不過也不是說Toast的用法就一點深度都沒有了,比如說上述的寫法就會存在如下圖所示的問題:

可以看到,這裡我快速連續點選了五次按鈕,Toast就觸發了五次。這樣的體驗其實是不好的,因為也許使用者是手抖了一下多點了幾次,導致Toast就長時間關閉不掉了。又或者我們其實已在進行其他操作了,應該彈出新的Toast提示,而上一個Toast卻還沒顯示結束。

因此,最佳的做法是將Toast的呼叫封裝成一個介面,寫在一個公共的類當中,如下所示:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Util</span> {</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Toast toast;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">showToast</span>(Context context, 
        String content) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (toast == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            toast = Toast.makeText(context,
                         content, 
                         Toast.LENGTH_SHORT);
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
            toast.setText(content);
        }
        toast.show();
    }

}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>

可以看到,這裡和我們平時使用Toast的方式並不一樣,這裡會先判斷Toast物件是否為空,如果是空的情況下才會呼叫makeText()方法來去生成一個Toast物件,否則就直接呼叫setText()方法來設定顯示的內容,最後再呼叫show()方法將Toast顯示出來。由於不會每次呼叫的時候都生成新的Toast物件,因此剛才我們遇到的問題在這裡就不會出現了。

呼叫的時候也很簡單,只需要把Context物件和Toast要顯示的內容傳進來就可以了:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Util.showToast(context, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"things happened"</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

現在我們再重新執行一遍程式,效果如下圖所示: 

可以看到,現在不管我們觸發多少次Toast呼叫,都只會持續一次Toast顯示的時長,這也算是一個小技巧吧。

Toast的作用是告訴使用者現在發生了什麼事情,不會阻擋使用者的操作,但同時使用者只能被動接收這個事情,因為沒有什麼辦法來讓使用者是選擇同意還是拒絕。

雖說Toast在使用者體驗方面要比Dialog好一些,但是也要慎用,尤其是涉及到一些敏感操作的時候。比如說刪除資料,只給使用者一個提示:“你的資料已被刪除”,而不給使用者選擇是否要刪除的機會,這個時候使用者可能就要暴走了。

3. Snackbar

如果說Dialog和Toast是兩個極端的話,那麼Snackbar就是處於中間的位置了。Snackbar和Toast比較相似,但是用途更加廣泛,並且它是可以和使用者進行互動的。Snackbar使用一個動畫效果從螢幕的底部彈出來,過一段時間後也會自動消失。

在使用Snackbar之前,首先需要在app/build.gradle中新增相應的依賴:

<code class="language-groovy hljs matlab has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">dependencies <span class="hljs-cell" style="box-sizing: border-box;">{
    compile <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'com.android.support:design:23.4.0'</span>
}</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

然後就可以使用Snackbar了,它的用法和Toast是比較相似的:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Snackbar.make(view, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"data deleted"</span>,Snackbar.LENGTH_LONG)
        .setAction(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Undo"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> View.OnClickListener(){
                <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onClick</span>(View v) {
                }
            })
        .show();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

這裡呼叫Snackbar的make()方法來建立一個Snackbar物件,make()方法的第一個引數需要傳入一個view,只要是當前介面佈局的任意一個view都可以,Snackbar會使用這個view來自動查詢最外層的佈局,用於展示Snackbar。第二個引數就是Snackbar中顯示的內容,第三個引數是Snackbar顯示的時長。這些和Toast都是類似的。

接著這裡又呼叫了一個setAction()方法來設定一個動作,從而讓Snackbar不僅僅是一個提示,而是可以和使用者進行互動的。最後呼叫show()方法讓Snackbar顯示出來。

現在重新執行一下程式,效果如下圖所示: 


可以看到,Snackbar的效果有點類似於Toast,不過它是從螢幕底部彈出來的。另外Snackbar上面可以加入和使用者互動的按鈕,比如刪除資料的時候給使用者一個Undo的選項,從這些小的細節方面都可以提升很多的使用者體驗。

4. 總結

現在你有三種方式可以給使用者提示資訊,Dialog、Toast和Snackbar,下面我們對這三種方式的使用時機做個總結吧。

  • Dialog:當提示資訊是至關重要的,並且必須要由使用者做出決定才能繼續的時候,使用Dialog。
  • Toast:當提示資訊只是告知使用者某個事情發生了,使用者不需要對這個事情做出響應的時候,使用Toast。
  • Snackbar:以上兩者之外的任何其他場景,Snackbar可能會是你最好的選擇。

相關文章