packageandroid.widget;
import android.app.INotificationManager;
import android.app.ITransientNotification;
importandroid.content.Context;
importandroid.content.res.Configuration;
importandroid.content.res.Resources;
importandroid.graphics.PixelFormat;
importandroid.os.Handler;
importandroid.os.RemoteException;
import android.os.ServiceManager;
importandroid.util.Log;
importandroid.view.Gravity;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.view.WindowManager;
importandroid.view.accessibility.AccessibilityEvent;
importandroid.view.accessibility.AccessibilityManager;
/**
*toast 是一个view,这个view为用户包含了非常简单的信息。这个toast类帮助你去创建和显示简单信息。
*
* 当这个view展示给用户的时候,做为悬浮view显示在应用之上。这个view将不会获得焦点。
* 当toast出现的时候用户可能还正在打字。这个机制意在尽可能不引人注意,但是又可以展示用户想要看到的信息。
* 两个示例,一个是声音控制面板,另一个是简短的信息告诉你设置以保存。
*
* 使用这个类的最简单的方法是调用静态方法去构造你需要的界面和返回一个新的toast对象。
*
* Atoast is a view containing a quick little message for the user. The toast class
*helps you create and show those.
*{@more}
*
* <p>
*When the view is shown to the user, appears as a floating view over the
*application. It will never receivefocus. The user will probably be in the
*middle of typing something else. Theidea is to be as unobtrusive as
*possible, while still showing the user the information you want them to see.
*Two examples are the volume control, and the brief message saying that your
*settings have been saved.
* <p>
*The easiest way to use this class is to call one of the static methods thatconstructs
*everything you need and returns a new Toast object.
*
*<div class="special reference">
* <h3>Developer Guides</h3>
* <p>For information about creating Toastnotifications, read the
*<a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">ToastNotifications</a> developer
*guide.</p>
* </div>
*/
public class Toast {
static finalString TAG= "Toast";
static final boolean localLOGV= false;
/**
* Show the view or text notification for a short period of time. This time
* could be user-definable. This is the default.
* @see #setDuration
*
* 显示时长,这个时间是可以设置的,这个常量是默认值
*/
public static final int LENGTH_SHORT = 0;
/**
* Show the view or text notification for a long period of time. This time
* could be user-definable.
* @see #setDuration
*
* 显示时长,跟SHORT相对应
*/
public static final int LENGTH_LONG = 1;
final Context mContext;
final TN mTN;
int mDuration;
View mNextView;
/**
* Construct an empty Toast object. You must call {@link#setView} before you
* can call {@link #show}.
*
* @param context The context to use. Usually your {@link android.app.Application}
* or {@link android.app.Activity} object.
*
* 创建一个空的toast对象。在你调用#show之前必须调用#setView方法。
*
*/
public Toast(Context context) {
mContext= context;
mTN= new TN();
//在Gravity属性的基础上,在y轴偏移mY
mTN.mY =context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
//默认显示位置Equivalent to:Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
/**
* Show the view for the specified duration.
*
* 显示view,实际是加入显示队列
*/
public voidshow() {
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
}
}
/**
* Close the view if it's showing, or don't show it if it isn't showingyet.
* You do not normally have to call this. Normally view will disappear on its own
* after the appropriate duration.
*
* 如果view已显示则关闭显示,如果还没显示则不显示。通常不用调用这个方法。view在显示一定时长后就会消失。
*/
public voidcancel() {
mTN.hide();
try{
getService().cancelToast(mContext.getPackageName(), mTN);
} catch(RemoteException e) {
// Empty
}
}
/**
* Set the view to show.
* @see #getView
*
* 设置要显示的view
*/
public voidsetView(View view) {
mNextView= view;
}
/**
* Return the view.
* @see #setView
*
* 获得view
*/
public View getView() {
return mNextView;
}
/**
* Set how long to show the view for.
* @see #LENGTH_SHORT
* @see #LENGTH_LONG
*
* 设置显示时长,使用#LENGTH_SHORT、#LENGTH_LONG常量
*/
public voidsetDuration(intduration) {
mDuration= duration;
}
/**
* Return the duration.
* @see #setDuration
*
* 过去显示时长
*/
public intgetDuration() {
return mDuration;
}
/**
* Set the margins of the view.
*
* @param horizontalMargin The horizontal margin, in percentage of the
* container width, betweenthe container's edges and the
* notification
* @param verticalMargin The vertical margin, in percentage of the
* container height, betweenthe container's edges and the
* notification
*
* 设置view的边距
*/
public voidsetMargin(floathorizontalMargin, floatverticalMargin) {
mTN.mHorizontalMargin =horizontalMargin;
mTN.mVerticalMargin = verticalMargin;
}
/**
* Return the horizontal margin.
* 获取边距
*/
public floatgetHorizontalMargin() {
return mTN.mHorizontalMargin;
}
/**
* Return the vertical margin.
* 获取边距
*/
public floatgetVerticalMargin() {
return mTN.mVerticalMargin;
}
/**
* Set the location at which the notification should appear on thescreen.
* @see android.view.Gravity
* @see #getGravity
*
* 设置显示位置,在Gravity属性的基础上,在xy轴偏移mXmY
*/
public voidsetGravity(intgravity, intxOffset, intyOffset) {
mTN.mGravity = gravity;
mTN.mX = xOffset;
mTN.mY = yOffset;
}
/**
* Get the location at which the notification should appear on thescreen.
* @see android.view.Gravity
* @see #getGravity
*
* 获取显示位置
*/
public intgetGravity() {
return mTN.mGravity;
}
/**
* Return the X offset in pixels to apply to the gravity's location.
* 获取Gravity属性的基础上的偏移量
*/
public intgetXOffset() {
return mTN.mX;
}
/**
* Return the Y offset in pixels to apply to the gravity's location.
* 获取Gravity属性的基础上的偏移量
*/
public intgetYOffset() {
return mTN.mY;
}
/**
* Make a standard toast that just contains a text view.
*
* @param context The context to use. Usually your {@link android.app.Application}
* or {@link android.app.Activity} object.
* @param text The text to show. Can be formatted text.
* @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
* {@link #LENGTH_LONG}
*
* 创建一个标准的toast对象,仅仅只包括一个TextView
*/
public staticToast makeText(Context context, CharSequence text, int duration) {
Toast result = newToast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification,null);
/*com.android.internal.R.layout.transient_notification所指布局
* <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?android:attr/toastFrameBackground">
<TextView
android:id="@android:id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.Toast"
android:textColor="@color/bright_foreground_dark"
android:shadowColor="#BB000000"
android:shadowRadius="2.75"
/>
</LinearLayout>*/
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView= v;
result.mDuration= duration;
returnresult;
}
/**
* Make a standard toast that just contains a text view with the textfrom a resource.
*
* @param context The context to use. Usually your {@link android.app.Application}
* or {@link android.app.Activity} object.
* @param resId The resource id of thestring resource to use. Can be formattedtext.
* @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
* {@link #LENGTH_LONG}
*
* @throws Resources.NotFoundException if the resource can't be found.
*
* 同上,使用resId找到字符串
*/
public staticToast makeText(Context context, intresId, intduration)
throws Resources.NotFoundException {
return makeText(context,context.getResources().getText(resId), duration);
}
/**
* Update the text in a Toast that was previously created using one ofthe makeText() methods.
* @param resId The new text for the Toast.
*
* 跟新之前使用makeText()方法创建的toast里的text。
*/
public voidsetText(intresId) {
setText(mContext.getText(resId));
}
/**
* Update the text in a Toast that was previously created using one ofthe makeText() methods.
* @param s The new text for the Toast.
*
* 跟新之前使用makeText()方法创建的toast里的text。
*/
public voidsetText(CharSequence s) {
if(mNextView == null) {
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message);
if(tv == null){
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
tv.setText(s);
}
//=======================================================================================
// All the gunk below is the interaction with the Notification Service,which handles
// the proper ordering of these system-wide.
//=======================================================================================
private static INotificationManagersService;
static private INotificationManagergetService() {
if(sService!= null){
return sService;
}
sService= INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
}
private static class TN extends ITransientNotification.Stub {
finalRunnable mShow= new Runnable() {
@Override
public void run() {
handleShow();
}
};
finalRunnable mHide= new Runnable() {
@Override
public void run() {
handleHide();
// Don't do this in handleHide() because it is also invoked byhandleShow()
mNextView = null;
}
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
finalHandler mHandler= newHandler();
int mGravity;
int mX, mY;
float mHorizontalMargin;
float mVerticalMargin;
View mView;
View mNextView;
WindowManager mWM;
TN() {
// XXX This should be changed to use a Dialog,with a Theme.Toast
// defined that sets up the layout paramsappropriately.
final WindowManager.LayoutParamsparams = mParams;
params.height= WindowManager.LayoutParams.WRAP_CONTENT;
params.width= WindowManager.LayoutParams.WRAP_CONTENT;
params.format= PixelFormat.TRANSLUCENT;
params.windowAnimations= com.android.internal.R.style.Animation_Toast;
params.type= WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
params.flags= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
}
/**
* schedule handleShow into the right thread
*/
@Override
public void show() {
if(localLOGV) Log.v(TAG, "SHOW:" + this);
mHandler.post(mShow);
}
/**
* schedule handleHide into the right thread
*/
@Override
public void hide() {
if(localLOGV) Log.v(TAG, "HIDE:" + this);
mHandler.post(mHide);
}
public void handleShow() {
if(localLOGV) Log.v(TAG, "HANDLESHOW: " + this + "mView=" + mView
+ " mNextView=" + mNextView);
if(mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
Context context = mView.getContext().getApplicationContext();
if (context == null) {
context = mView.getContext();
}
mWM =(WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
// We can resolve the Gravity here by using the Locale for getting
// the layout direction
final Configuration config = mView.getContext().getResources().getConfiguration();
final intgravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
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 theyare used to
// announce a transient piece ofinformation 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, "HANDLEHIDE: " + this + "mView=" + mView);
if(mView != null) {
// note: checking parent() just to make sure the view has
// been added... i have seen caseswhere 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;
}
}
}
}