編輯:關於Android編程
學過Android的人都知道,彈出一個系統API吐司只需要一行代碼,調用的是Toast對象的makeText()方法,方法裡給一個上下文,顯示的文字,和顯示的時長,然後再調用show方法就能顯示。
Toast.makeText(this,"下載失敗",Toast.LENGTH_SHORT).show();
2. Toast對象的makeText()方法分析
跟進Toast類的源碼,找到makeText()方法,可以看到這是個靜態方法,並且返回值仍然是Toast,所以在調用了makeText()方法之後,可以繼續調用show()方法。 這個方法一進來就根據上下文創建了一個吐司對象,最後返回的也是這個吐司對象,而最後吐司的顯示就是用這個對象裡面的方法,所以我們要跟進這個Toast對象的構造。
方法裡面的代碼就是獲取一個布局填充器,然後將系統布局文件轉換為View的對象,再使用這個View對象裡邊的TextView控件顯示設置的文本。
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
// 根據傳進來的上下文,創建一個吐司對象
Toast result = new Toast(context);
// 布局填充器
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 填充系統布局文件,轉換為View對象
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
// 獲取TextView文本對象,設置顯示的文字
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
// 初始化顯示的時長
result.mNextView = v;
result.mDuration = duration;
// 返回處理過的吐司對象
return result;
}
3. Toast對象的show()方法分析
跟進第二步創建的Toast構造方法,可以看到裡面並沒有關於show()方法的蹤跡,但是我們可以很清楚的發現,它又創建了一個TN對象。其他的代碼都是做一些初始化操作。
public Toast(Context context) {
mContext = context;
mTN = new TN();
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
繼續跟進TN對象,可以發現這是個內部類,而且關於吐司的大部分代碼都是在裡面實現的。先來看一下構造方法,這裡面只做了一件事情,就是創建了窗口管理器並且進行初始化,這個初始化的窗體其實就是談吐司的窗體。
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.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;
}
我們再來看看TN類的其他方法,很快我們就找到了一個特別明顯的方法就是show()方法和hide()方法,而這兩個方法正是我們要的,繼續找到mHandler的方法。
// show()方法和hide()方法
@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);
}
在mHandler的方法中,我們發現最後是調用了handleShow()和handlerHide()方法,這兩個方法裡有一句代碼特別重要,因為這句代碼就是讓吐司顯示出來的代碼。
// 顯示
public void handleShow() {
...
// 窗口管理器
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
...
// 判斷View對象的父控件不為空
if (mView.getParent() != null) {
mWM.removeView(mView);
}
// 將當前的View添加到窗口管理器中,這句代碼能讓View顯示出來
mWM.addView(mView, mParams);
}
}
// 隱藏
public void handleHide() {
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) {
// 將View對象從窗體管理器移除
mWM.removeView(mView);
}
mView = null;
}
}
二、自定義Toast
通過以上的源碼分析,我想大家已經對Android系統中Toast的彈出和隱藏有了一定的理解,那麼知道了原理,我們也可以自己做一個吐司,還可以通知指定顯示的布局文件還做各種不同樣式的吐司。
1. 自定義吐司效果圖
明白了原理,我們可以專門做一個自定義Toast的工具類,這個類負責彈出吐司和隱藏吐司。然後在MainActivity中調用,這樣點擊按鈕就能彈出和隱藏吐司了。除此之外,Demo裡還做了雙擊吐司居中,三擊吐司隱藏的點擊事件邏輯,大家可以學習一下。
2. 分析工具類的代碼實現
在Demo的Toast工具類中,我們只需要像源代碼中一樣,在CustomToastUtil的構造方法中初始化LayoutParams,然後再寫彈出Toast的方法,隱藏吐司的方法就可以了。
工具類構造方法的實現,同源碼一樣,我們這裡也是初始化窗體的一些基本參數,並且初始化吐司要顯示的布局
/**
* 構造
* @param context
*/
public CustomToastUtil(Context context) {
this.mContext = context;
initParams();
}
/**
* 初始化窗體屬性
*/
private void initParams() {
mWm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mParams = new WindowManager.LayoutParams();
mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 類型
mParams.type = WindowManager.LayoutParams.TYPE_PHONE;
// 透明,不透明會出現重疊效果
mParams.format = PixelFormat.TRANSLUCENT;
// 位置屬性
mParams.gravity = Gravity.TOP + Gravity.LEFT; // 左上
// 進來的時候把存儲的位置讀取顯示出來
mParams.x = PreferenceUtil.getInt(mContext, "lastX");
mParams.y = PreferenceUtil.getInt(mContext, "lastY");
// 初始化吐司窗口布局
mView = View.inflate(mContext, R.layout.view_toast, null);
}
接下來就是顯示吐司的邏輯,其實特別簡單,核心代碼只有一句,這裡要先獲取自定義填充布局文件中的TextView顯示文本顯示你要彈出的文本,然後對View的點擊進行了監聽,實現了雙擊將吐司居中顯示,和三擊隱藏吐司的功能。
/**
* 彈出自定義吐司
*/
public void popToast(String text) {
TextView tvName = (TextView) mView.findViewById(R.id.tv_toast_name);
// 設置顯示的文字
tvName.setText(text);
// 吐司窗體的背景可以在布局文件之中指定也可以在代碼中設置
// 設置吐司的雙擊事件,點擊之後會到中心點
mView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 雙擊事件處理邏輯
System.arraycopy(mHits1, 1, mHits1, 0, mHits1.length - 1);
mHits1[mHits1.length - 1] = SystemClock.uptimeMillis();
if (mHits1[0] >= (SystemClock.uptimeMillis() - 500)) {
// 雙擊之後執行
// 讓吐司移動到x中心,y不需要對中
// 更新窗體的坐標
mParams.x = (mWm.getDefaultDisplay().getWidth() - mView.getWidth()) / 2;
mWm.updateViewLayout(mView, mParams);
// 點擊完退出的時候也把位置信息存儲起來
PreferenceUtil.putInt(mContext, "lastX", mParams.x);
PreferenceUtil.putInt(mContext, "lastY", mParams.y);
}
// 三擊事件處理邏輯
System.arraycopy(mHits2, 1, mHits2, 0, mHits2.length - 1);
mHits2[mHits2.length - 1] = SystemClock.uptimeMillis();
if (mHits2[0] >= (SystemClock.uptimeMillis() - 600)) {
// 點擊之後將吐司移除掉
if (mView != null) {
if (mView.getParent() != null) {
mWm.removeView(mView);
}
}
}
}
});
// 設置吐司的觸摸滑動事件
mView.setOnTouchListener(new View.OnTouchListener() {
int startX;
int startY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 按下
// 手指按下時的坐標位置
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE: // 移動
// 移動後的坐標位置
int newX = (int) event.getRawX();
int newY = (int) event.getRawY();
// 偏移量
int dx = newX - startX;
int dy = newY - startY;
// 給偏移量設置邊距
// 小於x軸
if (mParams.x < 0) {
mParams.x = 0;
}
// 小於y軸
if (mParams.y < 0) {
mParams.y = 0;
}
// 超出x軸
if (mParams.x > mWm.getDefaultDisplay().getWidth() - mView.getWidth()) {
mParams.x = mWm.getDefaultDisplay().getWidth() - mView.getWidth();
}
// 超出y軸
if (mParams.y > mWm.getDefaultDisplay().getHeight() - mView.getHeight()) {
mParams.y = mWm.getDefaultDisplay().getHeight() - mView.getHeight();
}
// 更新窗體的坐標
mParams.x += dx;
mParams.y += dy;
mWm.updateViewLayout(mView, mParams);
// 重新賦值起始坐標
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP: // 抬起
// 抬起來的時候保存最後一次的位置,下次進來時直接顯示出來
PreferenceUtil.putInt(mContext, "lastX", mParams.x);
PreferenceUtil.putInt(mContext, "lastX", mParams.y);
break;
default:
break;
}
return false; // 當有父控件有點擊事件時,這裡要返回false,不然父控件就拿不到點擊事件了
}
});
if (mView != null) {
if (mView.getParent() != null) {
mWm.removeView(mView);
}
}
// 添加到窗體管理器中才能顯示出來
mWm.addView(mView, mParams);
}
最後是吐司的隱藏方法,這個方法只需要將View從窗體管理器中移除掉就好了。
/**
* 從父窗體中移除吐司
*/
public void hideToast() {
if (mView != null) {
if (mView.getParent() != null) {
mWm.removeView(mView);
}
}
}
以上代碼簡單的實現了自定義Toast的顯示和隱藏,當然你也可以給顯示的Toast加一個背景,這樣吐司看起來就更加的漂亮了。以上就是本次分享的全部內容,謝謝大家。
最近項目做完了,有閒暇時間,一直想做一個類似微信中微信發說說,既能實現拍照,選圖庫,多圖案上傳的案例,目前好多App都有類似微信朋友圈的功能,能過發表說說等附帶圖片上傳。
引言Android在3.0中引入了Fragments的概念,其目的是用在大屏幕設備上–例如平板電腦上,支持更加動態和靈活的UI設計。平板電腦的屏幕要比手機的大
又想到快要過年了,到時候還不知道群裡要發好多紅包,所以我將之前在網上宕的一份微信搶紅包的代碼修改了一下,實現了QQ搶紅包!可以支持搶QQ拼手氣紅包,普通紅包,口令紅包,現
先給大家炫下效果圖:首先過程中碰到的幾個問題:1、對 EditText 進行自定義背景2、運行時自動 EditText 自動獲得焦點3、在獲得焦點時即清空 hint ,而