1. 可以通過Theme的方式,android默認也提供了一些Window的Theme來給我們使用,如:android.R.style.Theme_NoTitleBar(不顯示title bar),android.R.style.Theme_NoTitleBar_Fullscreen(全屏)。
2. 通過設置Window類的一些屬性來定製
如果要看源碼的話,涉及到的一些類和xml文件:
android.view.Window
android.view.WindowManager (Window的屬性管理類,而不是Window管理類)
android.view.WindowManager.LayoutParams (Window的所有可設置屬性)
android.view.ViewGroup.LayoutParams (WindowManager.LayoutParams繼承自該類)
com.android.internal.policy.impl.MidWindow (Dialog使用的Window實現類, Activity的Window實現類是PhoneWindow)
/res/style/themes.xml (在style name="Theme"下的那些item,大部分會被Window實現類讀取,如:MidWindow的generateLayout方法會讀取Window_windowIsFloating, Window_windowNoTitle等)
/res/layout/dialog_custom_title.xml
/res/layout/dialog_title_icons.xml
/res/layout/dialog_title.xml
在Dialog之外點擊時隱藏Dialog
- setCanceledOnTouchOutside(true);
下面展示的代碼將用到的基礎代碼
- public class MyDialog extends Dialog {
- private ImageView mImageView;
- public MyDialog(Context context, String imageUrl) {
- super(context);
- setCanceledOnTouchOutside(true);
- Window win = getWindow();
- win.requestFeature(Window.FEATURE_NO_TITLE);
- WindowManager.LayoutParams wAttrs = win.getAttributes();
- configWindow(win, wAttrs);
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Context ctxt = getContext();
- mImageView = new ImageView(ctxt);
- mImageView.setImageResource(R.drawable.ic_launcher);
- mImageView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
- setContentView(mImageView);
- }
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- // 在這裏設置dialog的一些屬性
- }
- }
在任意未知顯示Dialog
- // gravity默認爲Gravity.CENTER,x, y分別爲0, 0
- // x, y是相對於當前的gravity而言的
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- wAttrs.gravity = Gravity.LEFT | Gravity.TOP;
- wAttrs.x = 0;
- wAttrs.y = 100;
- }
設置Dialog的透明度
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- wAttrs.alpha =0.1F; // 0.0F(完全透明) - 1.0F
- }
設置顯示Dialog時,屏幕的亮度
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- wAttrs.screenBrightness =0.1F; // 0.0F - 1.0F(最亮)
- }
設置Dialog與屏幕間的margin
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- wAttrs.gravity = Gravity.LEFT;
- wAttrs.horizontalMargin = 0.1F;
- // 因爲現在的gravity是Gravity.LEFT和Gravity.CENTER,所以在垂直方向上是以中間爲margin的參考點
- wAttrs.verticalMargin = 0.1F;
- }
改變Dialog的大小
默認情況下,Dialog大小隻是包住其內部的view,就是layout把android:layout_xxx="fill_parent"也是如此。不知道這個是不是mIsFloating這個屬性引起的。
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- // 好像這個設了沒效果,網上找的代碼
- Display d = win.getWindowManager().getDefaultDisplay();
- wAttrs.width = (int) (d.getWidth() * 0.9F);
- wAttrs.height = (int) (d.getHeight() * 0.6F);
- }
模糊Dialog後的內容
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- win.setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
Dialog不接受Touch事件
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- win.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
- }
不變暗dialog後的內容
- private void configWindow(Window win, WindowManager.LayoutParams wAttrs) {
- win.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- }
其他一些Flag的介紹
窗口之後的內容變暗。
public static final int FLAG_DIM_BEHIND = 0x00000002;
窗口之後的內容變模糊。
public static final int FLAG_BLUR_BEHIND = 0x00000004;
不許獲得焦點。
不能獲得按鍵輸入焦點,所以不能向它發送按鍵或按鈕事件。那些時間將發送給它後面的可以獲得焦點的窗口。此選項還會設置FLAG_NOT_TOUCH_MODAL選項。設置此選項,意味着窗口不能與軟輸入法進行交互,所以它的Z序獨立於任何活動的輸入法(換句話說,它可以全屏顯示,如果需要的話,可覆蓋輸入法窗口)。要修改這一行爲,可參考FLAG_ALT_FOCUSALBE_IM選項。
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
不接受觸摸屏事件。
public static final int FLAG_NOT_TOUCHABLE = 0x00000010;
當窗口可以獲得焦點(沒有設置FLAG_NOT_FOCUSALBE選項)時,仍然將窗口範圍之外的點設備事件(鼠標、觸摸屏)發送給後面的窗口處理。否則它將獨佔所有的點設備事件,而不管它們是不是發生在窗口範圍之內。
public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
如果設置了這個標誌,當設備休眠時,點擊觸摸屏,設備將收到這個第一觸摸事件。
通常第一觸摸事件被系統所消耗,用戶不會看到他們點擊屏幕有什麼反應。
public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
當此窗口爲用戶可見時,保持設備常開,並保持亮度不變。
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
窗口占滿整個屏幕,忽略周圍的裝飾邊框(例如狀態欄)。此窗口需考慮到裝飾邊框的內容。
public static final int FLAG_LAYOUT_IN_SCREEN =0x00000100;
允許窗口擴展到屏幕之外。
public static final int FLAG_LAYOUT_NO_LIMITS =0x00000200;
窗口顯示時,隱藏所有的屏幕裝飾(例如狀態條)。使窗口占用整個顯示區域。
public static final int FLAG_FULLSCREEN = 0x00000400;
此選項將覆蓋FLAG_FULLSCREEN選項,並強制屏幕裝飾(如狀態條)彈出。
public static final int FLAG_FORCE_NOT_FULLSCREEN =0x00000800;
抖動。指的是對半透明的顯示方法。又稱“點透”。圖形處理較差的設備往往用“點透”替代Alpha混合。
public static final int FLAG_DITHER = 0x00001000;
不允許屏幕截圖。
public static final int FLAG_SECURE = 0x00002000;
一種特殊模式,佈局參數用於指示顯示比例。
public static final int FLAG_SCALED = 0x00004000;
當屏幕有可能貼着臉時,這一選項可防止面頰對屏幕造成誤操作。
public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;
當請求佈局時,你的窗口可能出現在狀態欄的上面或下面,從而造成遮擋。當設置這一選項後,窗口管理器將確保窗口內容不會被裝飾條(狀態欄)蓋住。
public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
反轉FLAG_NOT_FOCUSABLE選項。
如果同時設置了FLAG_NOT_FOCUSABLE選項和本選項,窗口將能夠與輸入法交互,允許輸入法窗口覆蓋;
如果FLAG_NOT_FOCUSABLE沒有設置而設置了本選項,窗口不能與輸入法交互,可以覆蓋輸入法窗口。
public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
如果你設置了FLAG_NOT_TOUCH_MODAL,那麼當觸屏事件發生在窗口之外事,可以通過設置此標誌接收到一個MotionEvent.ACTION_OUTSIDE事件。注意,你不會收到完整的down/move/up事件,只有第一次down事件時可以收到ACTION_OUTSIDE。
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;
當屏幕鎖定時,窗口可以被看到。這使得應用程序窗口優先於鎖屏界面。可配合FLAG_KEEP_SCREEN_ON選項點亮屏幕並直接顯示在鎖屏界面之前。可使用FLAG_DISMISS_KEYGUARD選項直接解除非加鎖的鎖屏狀態。此選項只用於最頂層的全屏幕窗口。
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
請求系統牆紙顯示在你的窗口後面。窗口必須是半透明的。
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
窗口一旦顯示出來,系統將點亮屏幕,正如用戶喚醒設備那樣。
public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
解除鎖屏。只有鎖屏界面不是加密的才能解鎖。如果鎖屏界面是加密的,那麼用戶解鎖之後才能看到此窗口,除非設置了FLAG_SHOW_WHEN_LOCKED選項。
public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
鎖屏界面淡出時,繼續運行它的動畫。
public static final int FLAG_KEEP_SURFACE_WHILE_ANIMATING =0x10000000;
以原始尺寸顯示窗口。用於在兼容模式下運行程序。
public static final int FLAG_COMPATIBLE_WINDOW = 0x20000000;
用於系統對話框。設置此選項的窗口將無條件獲得焦點。
public static final int FLAG_SYSTEM_ERROR = 0x40000000;
示例:預覽圖片的Dialog
在新浪微博和騰訊微博中,點擊一個圖片,都會用Dialog來顯示這個圖片的預覽圖
- public class ImagePreviewDialog extends Dialog {
- private ImageView mImageView;
- private boolean mImageChanged;
- private Bitmap mImage;
- public ImagePreviewDialog(Context context) {
- super(context);
- Window win = getWindow();
- win.requestFeature(Window.FEATURE_NO_TITLE);
- WindowManager.LayoutParams wAttrs = win.getAttributes();
- win.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- setCanceledOnTouchOutside(true);
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Context ctxt = getContext();
- mImageView = new ImageView(ctxt);
- mImageView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
- setContentView(mImageView);
- }
- @Override
- protected void onStart() {
- if (mImageChanged) {
- mImageView.setImageBitmap(mImage);
- mImageChanged = false;
- }
- }
- public void setImage(Bitmap bmp) {
- if (mImage == bmp) {
- return;
- }
- mImageChanged = true;
- mImage = bmp;
- }
- }
- // 在Activity中使用
- @Override
- public Dialog onCreateDialog(int id) {
- switch (id) {
- case DIALOG_PREVIEW_IMAGE:
- ImagePreviewDialog dialog = new ImagePreviewDialog(this);
- return dialog;
- }
- return super.onCreateDialog(id);
- }
- @Override
- public void onPrepareDialog(int id, Dialog dialog) {
- switch (id) {
- case DIALOG_PREVIEW_IMAGE:
- ImagePreviewDialog imgDialog = (ImagePreviewDialog) dialog;
- imgDialog.setImage(/* 當前點擊的圖片傳入 */);
- break;
- }
- }
對Dialog的理解
Dialog的顯示和dismiss實際上就是將dialog的DecorView從WindowManager添加和移除的過程
- private final Runnable mDismissAction = new Runnable() {
- @Override
- public void run() {
- dismissDialog();
- }
- };
- @Override
- public void dismiss() {
- // 不在ui線程上隱藏dialog的話
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(mDismissAction);
- } else {
- mDismissAction.run();
- }
- }
- private void dismissDialog() {
- if (mDecor == null || !mShowing) {
- return;
- }
- try {
- // 將DecorVie從WindowManager隱藏
- mWindowManager.removeView(mDecor);
- } finally {
- mDecor = null;
- mWindow.closeAllPanels();
- onStop();
- mShowing = false;
- sendDismissMessage();
- }
- }
- // show方法還使用了lazy initial的方式。在真正顯示時,才inflater佈局(通過dispatchOnCreate方法)
- public void show() {
- if (mShowing) {
- if (mDecor != null) {
- mDecor.setVisibility(View.VISIBLE);
- }
- return;
- }
- if (!mCreated) {
- dispatchOnCreate(null);
- }
- onStart();
- mDecor = mWindow.getDecorView();
- WindowManager.LayoutParams l = mWindow.getAttributes();
- if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
- WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
- nl.copyFrom(l);
- nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
- l = nl;
- }
- try {
- // 將DecorView添加到WindowManager
- mWindowManager.addView(mDecor, l);
- mShowing = true;
- sendShowMessage();
- } finally {
- }
- }