Toast是我們在App開發(fā)過程常用的提示用戶進(jìn)行了一些操作又不影響用戶正常操作的一個Android開發(fā)常用的控件
1.最基本的Toast用法
通過makeText(Context, String,Int)方法初始化一個Toast對象調(diào)用show方法進(jìn)行顯示玖像,代碼如下
<code>Toast t =Toast.makeText(this,"BasicsToast",Toast.LENGTH_SHORT);
t.show();</code>
通常都可以簡寫成
<code>
Toast.makeText(this,"BasicsToast",Toast.LENGTH_SHORT).show();</code>
2.設(shè)置Toast的顯示位置
通常一個Toast的信息是顯示在接近屏幕的底部居中坏怪,但是有些時候我們需要自定義它顯示位置的時候可是使用Toast的setGravity方法進(jìn)行自定義影所,這個方法需要三個參數(shù)Gravity常量,X偏移量箩帚,Y偏移量,代碼如下:
<code>
Toast t =Toast.makeText(this,"BasicsToast",Toast.LENGTH_SHORT);
t.setGravity(Gravity.CENTER,0 ,0);
t.show();</code>
3.自定義Toast顯示內(nèi)容
Toast的默認(rèn)方法只可以顯示文字,當(dāng)我們需要顯示圖片或者圖片和文字結(jié)合的自定義內(nèi)容時可以使用Toast提供的setView方法進(jìn)行設(shè)置我們自定義顯示的內(nèi)容了代碼如下:
首先定義需要顯示的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="標(biāo)題"
android:layout_gravity="center_horizontal"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/test"/>
</LinearLayout>
然后加載布局文件設(shè)置自定義Toast并顯示
Toast toast = new Toast(this);
toast.setView(LayoutInflater.from(this).inflate(R.layout.custom_toast_layout, null, false));
toast.setDuration(Toast.LENGTH_SHORT);
toast.show();
4.自定義顯示時間
Toast的設(shè)置顯示時間的方法<code>setDuration()</code>的參數(shù)只能是<code>Toast.LENGTH_SHORT</code>和<code>Toast.LENGTH_LONG</code>兩個前鹅;那么我們怎么去自定義Toast顯示時間呢,這就需要我們?nèi)チ私釺oast的源碼和Toast的原理來實現(xiàn)了
首先我們從Toast的<code>makeText(Context,CharSequence,int)</code>方法入手
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v=inflate.inflate(com.android.internal.R.layout. transient_notification, null);
TextView tv =(TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
從代碼我們可以看出來<code>makeText()</code>方法的作用主要就是構(gòu)造一個Toast對象碌识,加載Toast顯示的布局碾篡,設(shè)置顯示時間。
我們繼續(xù)看一下<code>show()</code>方法來看一下Toast怎么實現(xiàn)顯示
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
從<code>show()</code>方法的代碼我們可以看出在調(diào)用<code>show()</code>方法的時候會先構(gòu)造一個TN然后鍵Toast顯示相關(guān)信息發(fā)送到一個消息隊列等待顯示
那么Toast顯示的控制在哪里筏餐?
我們可以看一下TN這個類
final Runnable mShow = new Runnable() {
@Override
public void run() {
handleShow();
}
};
final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
//Don't do this in handleHide() because it is also invoked by handleShow()
mNextView = null;
}
};
我們可以看到在TN里面定義了兩個Runnable <code>mShow</code>和<code>mHide</code>來控制Toast的顯示和消失
我們接下來看下<code>handleShow</code>方法的實現(xiàn)
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView=" + mNextView);
if (mView != mNextView) {
// remove the old view if necessary handleHide();
mView = mNextView;
Context context = mView.getContext().getApplicationContext();
String packageName = mView.getContext().getOpPackageName();
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 int gravity = 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;
mParams.packageName = packageName;
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();
}
在handleShow方法中對要顯示的view的參數(shù)進(jìn)行設(shè)置然后同過WindowManager的addView方法進(jìn)行顯示!
到這里我們總算弄清楚Toast顯示的原理:
通過<code>makeText()</code>方法初始化Toast對象調(diào)用<code>show()</code>方法初始化一個TN并將需要顯示的內(nèi)容傳入一個消息隊列等待顯示开泽,在TN中通過<code>mShow</code>和<code>mHide</code>控制Toast的顯示。
那么我們怎么自定義Toast顯示時間魁瞪?
1.調(diào)用TN的mHide和mShow自己隨心所欲的控制(因為TN類是私有的僅限包訪問穆律,所以我們需要使用java的反射來實現(xiàn),但是這個方法在高版本會有bug)
2.自定義一個Toast导俘,不讓消息進(jìn)隊列 我們自己處理
3.使用Timer和handle.post相結(jié)合定時show一個Tosat
下面我們就著重說一下第二種實現(xiàn)方法自己定義一個Toast來實現(xiàn)自定義顯示時間的Toast峦耘,代碼如下
package song.com.toasttest;
import android.content.Context;
import android.graphics.PixelFormat;import android.os.Handler;
import android.view.Gravity;import android.view.LayoutInflater;
import android.view.View;import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MyToast {
private final Handler mHandler = new Handler();
private int mDuration = 2000;
private int mGravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
private int mX = 30;
private int mY = 30;
private float mHorizontalMargin;
private float mVerticalMargin;
private View mView;
private View mNextView;
private WindowManager mWindow;
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
public MyToast(Context context) {
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = android.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
mWindow = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
}
//初始化Toast所需要的參數(shù)
public static MyToast makeText(Context context, CharSequence text, int duration) {
MyToast myToast = new MyToast(context);
LinearLayout layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.my_toast_layout, null, false);
TextView textView = (TextView) layout.findViewById(R.id.message);
textView.setText(text);
myToast.mNextView = layout;
myToast.mDuration = duration;
return myToast;
}
public int getmDuration() {
return mDuration;
}
public void setmDuration(int mDuration) {
this.mDuration = mDuration;
}
public int getmGravity() {
return mGravity;
}
public void setmGravity(int mGravity) {
this.mGravity = mGravity;
}
public float getmHorizontalMargin() {
return mHorizontalMargin;
}
public void setmHorizontalMargin(float mHorizontalMargin) {
this.mHorizontalMargin = mHorizontalMargin;
}
public float getmVerticalMargin() {
return mVerticalMargin;
}
public void setmVerticalMargin(float mVerticalMargin) {
this.mVerticalMargin = mVerticalMargin;
}
public View getmView() {
return mView;
}
public void setmView(View mView) {
this.mView = mView;
}
public void show() {
mHandler.post(mShow);
if (mDuration > 0) {
mHandler.postDelayed(mHide, mDuration);
}
}
private final Runnable mShow = new Runnable() {
@Override
public void run() {
handleShow();
}
};
private final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
}
};
private void handleShow() {
if (mView != mNextView) {
handleHide();
mView = mNextView;
mParams.gravity = mGravity;
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalMargin = 1.0f;
}
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalMargin = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.horizontalMargin = mHorizontalMargin;
mParams.verticalMargin = mVerticalMargin;
if (mView.getParent() != null) {
mWindow.removeView(mView);
}
mWindow.addView(mView, mParams);
}
}
private void handleHide() {
if (mView != null) {
if (mView.getParent() != null) {
mWindow.removeView(mView);
}
}
}
}