當使用者在使用Android App時,可以讓App跳出一些浮動式、會自動關閉的的訊息提示使用者應該要做哪些動作,或者是App已經完成了哪些動作,讓使用者清楚知道目前App的執行狀況。要如何在Android實作出氣泡訊息呢?很簡單,在Android SDK中就有內建「Toast」類別可以做到這樣的功能。
Toast的使用方法
簡易作法
最簡單顯示出氣泡訊息的作法,就是直接使用Toast類別下的makeText方法來產生出一個Toast物件,再用show方法將這個Toast物件顯示出來。如以下程式碼:
Toast.makeText(context, text, duration).show();
其中需要注意的是duration這個參數,duration顧名思義就是Toast訊息顯示的持續時間,只有兩個整數數值有作用,分別是短時間的「Toast.LENGTH_SHORT」和長時間的「Toast.LENGTH_LONG」。在預設的情況下,短時間大約是2秒、而長時間大約是3.5秒。
直接使用makeText產生出來的Toast,樣式和位置由系統來決定,如下圖是直接使用Toast的makeText方法和show方法顯示出來的氣泡訊息:
用這種方式來顯示出氣泡訊息的話,會有一個很大的缺點,那就是如果在氣泡訊息尚未消失前,又使用了一次makeText方法和show方法,新的氣泡訊息就會被安排在舊的氣泡訊息消失後才會顯示,而不是立刻將舊的氣泡訊息蓋過(墊在下面),也不是直接改變舊氣泡訊息的文字。當氣泡訊息一多,可是要等很久才可以把所有的訊息顯示完哦!
進階作法
為了避免發生上述使用簡易作法產生的問題,我們必須要管理makeText之後所得到的Toast物件。可以使用一個物件變數或是類別變數來儲存Toast物件的參考,以供重複使用。如以下程式:
...
private static Toast toast;
...
private static void makeTextAndShow(final Context context, final String text, final int duration) {
if (toast == null) {
//如果還沒有用過makeText方法,才使用
toast = android.widget.Toast.makeText(context, text, duration);
} else {
toast.setText(text);
toast.setDuration(duration);
}
toast.show();
}
...
有了上面的程式後,只要呼叫makeTextAndShow方法就可以顯示出不會排隊延遲的氣泡訊息了!
自訂Toast的樣式
直接使用makeText產生出來的Toast物件,是使用系統內建的樣式,因此可能會因App執行在不同的Android裝置上而有所改變。如果想要自訂Toast的樣式,就不該使用makeText方法,應該直接用new實體化一個新的Toast物件,接著再指定一個View給他,可以將上面的makeTextAndShow方法改寫如下:
...
private static Toast toast;
private static TextView toastText;
...
private static void makeTextAndShow(final Context context, final String text, final int duration) {
if (toast == null) {
//如果還沒有建立過Toast,才建立
final ViewGroup toastView = new FrameLayout(context); // 用來裝toastText的容器
final FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
final GradientDrawable background = new GradientDrawable();
toastText = new TextView(context);
toastText.setLayoutParams(flp);
toastText.setSingleLine(false);
toastText.setTextSize(18);
toastText.setTextColor(Color.argb(0xAA, 0xFF, 0xFF, 0xFF)); // 設定文字顏色為有點透明的白色
background.setColor(Color.argb(0xAA, 0xFF, 0x00, 0x00)); // 設定氣泡訊息顏色為有點透明的紅色
background.setCornerRadius(20); // 設定氣泡訊息的圓角程度
toastView.setPadding(30, 30, 30, 30); // 設定文字和邊界的距離
toastView.addView(toastText);
toastView.setBackgroundDrawable(background);
toast = new Toast(context);
toast.setView(toastView);
}
toastText.setText(text);
toast.setDuration(duration);
toast.show();
}
...
執行結果:
自製Toast類別
由於Toast訊息算是Android App常用到的功能,因此可以將它另寫成一個類別來處理:
/**
* Android 氣泡訊息
*
* @author magiclen
*
*/
public class Toast {
// -----類別常數-----
/**
* 短時間訊息
*/
public static final int SHORT = android.widget.Toast.LENGTH_SHORT;
/**
* 長時間訊息
*/
public static final int LONG = android.widget.Toast.LENGTH_LONG;
/**
* 類別位置
*
* @author magiclen
*
*/
public static enum Position {
CENTER, CENTER_BOTTOM, CENTER_TOP, CENTER_LEFT, CENTER_RIGHT, LEFT_TOP, RIGHT_TOP, LEFT_BOTTOM, RIGHT_BOTTOM;
}
/**
* 調整Toast與螢幕邊界的距離,數值愈大距離愈遠
*/
private static final int offsetFactor = 12;
// -----類別變數-----
/**
* 儲存Context的參考
*/
private static Context context;
/**
* 儲存Toast的參考
*/
private static android.widget.Toast toast;
/**
* 儲存Toast的位置
*/
private static Position toastPosition = Position.CENTER_BOTTOM;
/**
* 儲存X邊界
*/
private static int offsetX;
/**
* 儲存Y邊界
*/
private static int offsetY;
/**
* 儲存使用者使用的ToastView
*/
private static ToastView toastView;
/**
* 設定Context給Toast使用,建議在Activity onCreate的時候都指定一次,<br/>
* 避免參考到的Context已經被釋放掉,或是變數被清空
*
* @param context
*/
public static void setContext(final Context context) {
Toast.context = context;
useDefaultOffset();
}
/**
* 指定ToastView,這個設定只有在重新呼叫showToast方法後才可以看到結果
*
* @param toastView 傳入ToastView物件
*/
public static void setToastView(final ToastView toastView) {
Toast.toastView = toastView;
toast = null;
}
/**
* 設定Toast的位置,這個設定只有在重新呼叫showToast方法後才可以看到結果
*
* @param position 傳入Toast的位置
*/
public static void setPosition(final Position position) {
if (position != null) {
Toast.toastPosition = position;
} else {
Toast.toastPosition = Position.CENTER_BOTTOM;
}
useDefaultOffset();
}
/**
* 設定Toast的位置,這個設定只有在重新呼叫showToast方法後才可以看到結果
*
* @param position 傳入Toast的位置
* @param offsetX 傳入X位移量
* @param offsetY 傳入Y位移量
*/
public static void setPosition(final Position position, final int offsetX, final int offsetY) {
if (position != null) {
Toast.toastPosition = position;
} else {
Toast.toastPosition = Position.CENTER_BOTTOM;
}
setOffset(offsetX, offsetY);
}
/**
* 使用預設的位移
*/
private static void useDefaultOffset() {
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
setOffset(dm.widthPixels / offsetFactor, dm.heightPixels / offsetFactor);
}
/**
* 設定氣泡訊息的位移
*
* @param offsetX 傳入X位移量
* @param offsetY 傳入Y位移量
*/
private static void setOffset(final int offsetX, final int offsetY) {
Toast.offsetX = offsetX;
Toast.offsetY = offsetY;
}
/**
* 顯示氣泡訊息(短時間)
*
* @param text 傳入訊息內容
*/
public static void showToast(final String text) {
showToast(text, android.widget.Toast.LENGTH_SHORT);
}
/**
* 建立Toast物件
*
* @param text 傳入訊息內容
* @param duration 傳入訊息維持的時間
* @return 傳回Toast物件
*/
private static android.widget.Toast makeText(final String text, final int duration) {
if (toastView != null) {
final android.widget.Toast toast = new android.widget.Toast(context);
toastView.setText(text);
toast.setDuration(duration);
toast.setView(toastView);
return toast;
} else {
return android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_SHORT);
}
}
/**
* 顯示氣泡訊息
*
* @param text 傳入訊息內容
* @param duration 傳入訊息維持的時間
*/
public static void showToast(final String text, final int duration) {
if (toast == null) {
// 如果Toast物件不存在,就用makeText方法建立一個新的Toast
toast = makeText(text, duration);
} else {
// 如果Toast物件存在,就重設它的持續時間和訊息文字
toast.setDuration(duration);
if (toastView == null) {
toast.setText(text);
} else {
toastView.setText(text);
}
}
// 設定氣泡訊息的位置
int gravity = 0, offsetX = 0, offsetY = 0;
switch (toastPosition) {
case CENTER:
gravity = Gravity.CENTER;
break;
case CENTER_BOTTOM:
gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
offsetY = Toast.offsetY;
break;
case CENTER_TOP:
gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
offsetY = Toast.offsetY;
break;
case CENTER_LEFT:
gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
offsetX = Toast.offsetX;
break;
case CENTER_RIGHT:
gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
offsetX = Toast.offsetX;
break;
case LEFT_TOP:
gravity = Gravity.LEFT | Gravity.TOP;
offsetX = Toast.offsetX;
offsetY = Toast.offsetY;
break;
case LEFT_BOTTOM:
gravity = Gravity.LEFT | Gravity.BOTTOM;
offsetX = Toast.offsetX;
offsetY = Toast.offsetY;
break;
case RIGHT_TOP:
gravity = Gravity.RIGHT | Gravity.TOP;
offsetX = Toast.offsetX;
offsetY = Toast.offsetY;
break;
case RIGHT_BOTTOM:
gravity = Gravity.RIGHT | Gravity.BOTTOM;
offsetX = Toast.offsetX;
offsetY = Toast.offsetY;
break;
}
toast.setGravity(gravity, offsetX, offsetY);
// 顯示出氣泡訊息
toast.show();
}
/**
* 顯示氣泡訊息(短時間)
*
* @param textId 傳入String Resoure的ID,或是整數數字
*/
public static void showToast(final int textId) {
showToast(textId, android.widget.Toast.LENGTH_SHORT);
}
/**
* 顯示氣泡訊息
*
* @param textId 傳入String Resoure的ID,或是整數數字
* @param duration 傳入訊息維持的時間
*/
public static void showToast(final int textId, final int duration) {
String text;
try {
// 嘗試取得String Resource
text = context.getResources().getString(textId);
} catch (final Exception ex) {
// 若String Resource取得失敗,則直接使用整數作為氣泡訊息
text = String.valueOf(textId);
}
showToast(text, duration);
}
/**
* 顯示氣泡訊息(短時間)
*
* @param decimalText 傳入整數或是浮點數數字作為訊息內容
*/
public static void showToast(final double decimalText) {
showToast(decimalText, android.widget.Toast.LENGTH_SHORT);
}
/**
* 顯示氣泡訊息
*
* @param decimalText 傳入整數或是浮點數數字作為訊息內容
* @param duration
*/
public static void showToast(final double decimalText, final int duration) {
if (decimalText == Math.round(decimalText)) {
// 如果傳入的數是整數
showToast(String.valueOf((int) decimalText), duration);
} else {
// 如果傳入的數不是整數
showToast(String.valueOf(decimalText), duration);
}
}
}
/**
* Toast專用的View
*
* @author magiclen
*
*/
public class ToastView extends FrameLayout {
private static int DEFAULT_TEXT_SIZE = 18;
private static int DEFAULT_TEXT_COLOR = Color.argb(0xAA, 0xFF, 0xFF, 0xFF);
private static int DEFAULT_PADDING = 30;
private static int DEFAULT_BACKGROUND_COLOR = Color.argb(0xAA, 0xFF, 0x00, 0x00);
private static int DEFAULT_RADIUS = 20;
private TextView text;
private GradientDrawable background = new GradientDrawable();
public ToastView(final Context context) {
super(context);
init(context);
}
public ToastView(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ToastView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private final void init(final Context context) {
text = new TextView(context);
final FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
text.setLayoutParams(flp);
text.setSingleLine(false);
text.setId(android.R.id.message);
text.setTextSize(DEFAULT_TEXT_SIZE);
text.setTextColor(DEFAULT_TEXT_COLOR);
addView(text);
setPadding(DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING);
background.setColor(DEFAULT_BACKGROUND_COLOR);
background.setCornerRadius(DEFAULT_RADIUS);
setBackgroundDrawable(background);
}
public void setTextSize(final float size) {
text.setTextSize(size);
}
public void setTextSize(final int unit, final float size) {
text.setTextSize(unit, size);
}
public void setTextColor(final int color) {
text.setTextColor(color);
}
public void setBackgroundColor(final int color) {
background.setColor(color);
}
public void setRadius(final float radius) {
background.setCornerRadius(radius);
}
public void setPadding(final int padding) {
setPadding(padding, padding, padding, padding);
}
void setText(final String textString) {
if (textString != null) {
text.setText(textString);
} else {
text.setText("");
}
}
}
用法舉例
Toast.setContext(context);
Toast.setPosition(Toast.Position.RIGHT_BOTTOM);
ToastView tv = new ToastView(this);
tv.setRadius(50);
tv.setTextSize(50);
tv.setBackgroundColor(Color.argb(0xAA, 0x00, 0xAA, 0x00));
Toast.setToastView(tv);
Toast.showToast("氣泡訊息!");
執行結果: