//1. 控件
Button button = new Button(this);
button.setText("Window Button");
//2. 佈局參數
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSPARENT);
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
layoutParams.x = 100;
layoutParams.y = 300;
// 必需要有type否則會異常: the specified window type 0 is not valid
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
//3. 獲取WindowManager並添加控件到Window中
WindowManager windowManager = getWindowManager();
windowManager.addView(button, layoutParams);
複製代碼
public interface ViewManager{
public void addView(View view, ViewGroup.LayoutParams params);
//添加View
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
//更新View
public void removeView(View view);
//刪除View
}
複製代碼
建立工具類:
/**
* 吐司工具類 避免點擊屢次致使吐司屢次,最後致使Toast就長時間關閉不掉了
* @param context
* @param content
*/
private static Toast toast;
public static void showToast(Context context, String content) {
if (toast == null) {
toast = Toast.makeText(context.getApplicationContext(), content, Toast.LENGTH_SHORT);
} else {
toast.setText(content);
}
toast.show();
}
複製代碼
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
ViewGroup rootView = (ViewGroup) content.getChildAt(0);
複製代碼
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//就是在這裏調用了Activity.attach()呀,接着調用了Activity.onCreate()和Activity.onStart()生命週期,
//可是因爲只是初始化了mDecor,添加了佈局文件,尚未把
//mDecor添加到負責UI顯示的PhoneWindow中,因此這時候對用戶來講,是不可見的
Activity a = performLaunchActivity(r, customIntent);
......
if (a != null) {
//這裏面執行了Activity.onResume()
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
if (!r.activity.mFinished && r.startsNotResumed) {
try {
r.activity.mCalled = false;
//執行Activity.onPause()
mInstrumentation.callActivityOnPause(r.activity);
}
}
}
}
複製代碼
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());//將DecorView添加到WindowManager
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);//DecorView可見
}
複製代碼
wm.addView(mDecor, getWindow().getAttributes());
起到了重要的做用,由於其內部建立了一個ViewRootImpl對象,負責繪製顯示各個子View。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);
}
複製代碼
@Override
public void show(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.obtainMessage(0, windowToken).sendToTarget();
}
@Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
複製代碼
/** @hide */
oneway interface ITransientNotification {
void show();
void hide();
}
複製代碼
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.removeViewImmediate(mView);
}
mView = null;
}
}
複製代碼
final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
複製代碼
private static boolean isUidSystem(int uid) {
final int appid = UserHandle.getAppId(uid);
return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
}
private static boolean isCallerSystem() {
return isUidSystem(Binder.getCallingUid());
}
複製代碼
android.view.WindowManager$BadTokenException
Unable to add window -- token android.os.BinderProxy@7f652b2 is not valid; is your activity running?
複製代碼
Toast.makeText(this,"瀟湘劍雨-yc",Toast.LENGTH_SHORT).show();
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
複製代碼
new Thread(new Runnable() {
@Override
public void run() {
ToastUtils.showRoundRectToast("瀟湘劍雨-楊充");
}
}).start();
複製代碼
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
ToastUtils.showRoundRectToast("瀟湘劍雨-楊充");
Looper.loop();
}
}).start();
複製代碼
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (local == BOTTOM) {
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.BottomDialog);
} else if (local == CENTER || local == TOP) {
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CenterDialog);
}
}
複製代碼
STYLE_NORMAL:會顯示一個普通的dialog
STYLE_NO_TITLE:不帶標題的dialog
STYLE_NO_FRAME:無框的dialog
STYLE_NO_INPUT:沒法輸入內容的dialog,即不接收輸入的焦點,並且觸摸無效。
複製代碼
<style name="CenterDialog" parent="@android:style/Theme.Dialog">
<item name="android:windowTitleStyle">@null</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowAnimationStyle">@style/CenterDialogAnimationStyle</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
</style>
複製代碼
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == 0) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
//建立一個Context
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
//獲取一個WindowManager對象
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//建立一個Window對象
final Window w = new PhoneWindow(mContext);
//將Window對象w賦值給mWindow
mWindow = w;
//爲Windowd對象設置回調,而且它自己實現了這些回調函數
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
//爲Window對象設置WindowManager對象
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
//建立一個對話框監聽Handler
mListenersHandler = new ListenersHandler(this);
}
複製代碼
public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
// Obtain a new message so this dialog can be re-used
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}
public void setOnCancelListener(final OnCancelListener listener) {
if (listener != null) {
mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
} else {
mCancelMessage = null;
}
}
private static final class ListenersHandler extends Handler {
private WeakReference<DialogInterface> mDialog;
public ListenersHandler(Dialog dialog) {
mDialog = new WeakReference<DialogInterface>(dialog);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DISMISS:
((OnDismissListener) msg.obj).onDismiss(mDialog.get());
break;
case CANCEL:
((OnCancelListener) msg.obj).onCancel(mDialog.get());
break;
case SHOW:
((OnShowListener) msg.obj).onShow(mDialog.get());
break;
}
}
}
複製代碼
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
}
複製代碼
PopupWindow popupWindow = new PopupWindow(this);
View inflate = LayoutInflater.from(this).inflate(R.layout.view_pop_custom, null);
popupWindow.setContentView(inflate);
popupWindow.setAnimationStyle(R.style.BottomDialog);
popupWindow.showAsDropDown(mTv1);
複製代碼