概要
1. 使用Toast遇到的问题
2. Toast源码及原理
3. 我的单例类 T.java
多个Toast依次显示,程序都结束了还在不停的显示呢
解决办法:自定义Toast + 单例类
Toast的源码不多,只有423行
有些我们常用的方法,想必不用多说,例如:
public Toast(Context context)
public void show()
public void cancel()
public static Toast makeText(Context context, int resId, int duration)
public static Toast makeText(Context context, CharSequence text, int duration)
重要的是:
有一个内部类TN
(其实是一个Binder),是TransientNotification
的缩写 短暂的通知
private static class TN extends ITransientNotification.Stub {
final Runnable mShow = new Runnable() {
public void run() {
handleShow();
}
};
final Runnable mHide = new Runnable() {
public void run() {
handleHide();
// Don't do this in handleHide() because it is also invoked by handleShow()
mNextView = null;
}
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
final Handler mHandler = new Handler();
int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
int mX, mY;
float mHorizontalMargin;
float mVerticalMargin;
/**
* @me: mNextView是Toast要显示的View
*/
View mView;
View mNextView;
WindowManagerImpl mWM;
/**
* @me: 这里有些值得注意的 params.flags
* 使其不可点击,不可获取焦点,保证屏幕高亮
* 窗口类型为WindowManager.LayoutParams.TYPE_TOAST
*/
TN() {
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
}
/**
* schedule handleShow into the right thread
*/
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
/**
* schedule handleHide into the right thread
*/
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
public void handleShow() {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView);
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
mWM = WindowManagerImpl.getDefault();
final int gravity = mGravity;
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
mWM.addView(mView, mParams);
trySendAccessibilityEvent();
}
}
private void trySendAccessibilityEvent() {
AccessibilityManager accessibilityManager =
AccessibilityManager.getInstance(mView.getContext());
if (!accessibilityManager.isEnabled()) {
return;
}
// treat toasts as notifications since they are used to
// announce a transient piece of information to the user
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(getClass().getName());
event.setPackageName(mView.getContext().getPackageName());
mView.dispatchPopulateAccessibilityEvent(event);
accessibilityManager.sendAccessibilityEvent(event);
}
public void handleHide() {
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
// note: checking parent() just to make sure the view has
// been added... i have seen cases where we get here when
// the view isn't yet added, so let's try not to crash.
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
mView = null;
}
}
}
重要的还有这个:
Toast中的mService的调用,其实通过IPC调用到了NotificationManagerService
private static INotificationManager sService;
static private INotificationManager getService() {
if (sService != null) {
return sService;
}
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
}
在关键的Toast show()方法中:
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
INotificationManager
把消息交给 Notification
去处理 Toast
显示, 消息全部是放在一个集合中,
是这么定义的 private ArrayList
这就为什么多个Toast
会一直逐个显示,直到没有消息为止.
举个自定义Toast的例子:
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom,
(ViewGroup) findViewById(R.id.llToast));
ImageView image = (ImageView) layout
.findViewById(R.id.tvImageToast);
image.setImageResource(R.drawable.icon);
TextView title = (TextView) layout.findViewById(R.id.tvTitleToast);
title.setText("Attention");
TextView text = (TextView) layout.findViewById(R.id.tvTextToast);
text.setText("自定义Toast");
toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.RIGHT | Gravity.TOP, 12, 40);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
你可以这么做,但我不打算这么做
工具类是一个独立的个体,尽量减少对其他资源的依赖
所以我这里使用代码布局来生成界面
/**
* Toast工具类
*
* 单例类
*/
public class T {
private static Toast toast = null;
private static LinearLayout toastView = null;
private static final int textId = 2345;
/**
* Toast.LENGTH_LONG
*/
public static void l(String msg) {
createToast(MyApplication.getInstance());
toast.setDuration(Toast.LENGTH_LONG);
setText(msg);
toast.show();
}
/**
* Toast.LENGTH_SHORT
*/
public static void s(String msg) {
createToast(MyApplication.getInstance());
toast.setDuration(Toast.LENGTH_SHORT);
setText(msg);
toast.show();
}
private static void setText(String msg) {
TextView tv = (TextView) toastView.findViewById(textId);
if (tv != null) {
tv.setText(msg);
}
}
private static void createToast(Context context) {
if (toastView == null) {
toastView = new LinearLayout(context);
toastView.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
toastView.setLayoutParams(layoutParams);
toastView.setBackgroundColor(Color.GRAY);
TextView textView = new TextView(context);
textView.setText("");
textView.setPadding(10, 10, 10, 10);
textView.setTextColor(Color.WHITE);
textView.setLayoutParams(layoutParams);
// 注意设置ID
textView.setId(textId);
toastView.addView(textView);
}
if (toast == null) {
toast = new Toast(context);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.setView(toastView);
}
}
}