1. 程式人生 > >Android Dialog顯示成Activity(全屏)

Android Dialog顯示成Activity(全屏)

前言

我們都知道要想讓一個Activity顯示成為Dialog的樣式可以對Activity指定Dialog的主題(Theme),但今天使用PreferenceScreen時發現內嵌的PreferenceScreen將一個對話方塊(Dialog)顯示出了Activity的效果。這是怎麼做到的呢,今天就來分析一下其中的原理。

現象

在preference的配置檔案中配置內嵌的PreferenceScreen標籤。如下:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <ListPreference
android:defaultValue="180" android:entries="@array/pref_sync_frequency_titles" android:entryValues="@array/pref_sync_frequency_values" android:key="sync_frequency" android:negativeButtonText="@null" android:positiveButtonText="@null" android:title
="@string/pref_title_sync_frequency" />
<PreferenceScreen android:title="preference screen" android:key="pref_screen_key"> <Preference android:title="@string/pref_title_system_sync_settings"> <intent android:action="android.settings.SYNC_SETTINGS" />
</Preference> </PreferenceScreen> </PreferenceScreen>

點選Preference Screen後彈出的頁面顯示效果:
這裡寫圖片描述

PreferenceScreen#onClick

那麼PreferenceScreen是怎樣處理點選事件的呢,原始碼如下(SDK/sources/android-25/android/preference/PreferenceScreen.java):

    @Override
    protected void onClick() {
        if (getIntent() != null || getFragment() != null || getPreferenceCount() == 0) {
            return;
        }

        showDialog(null);
    }

    private void showDialog(Bundle state) {
        Context context = getContext();
        if (mListView != null) {
            mListView.setAdapter(null);
        }

        LayoutInflater inflater = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View childPrefScreen = inflater.inflate(mLayoutResId, null);
        View titleView = childPrefScreen.findViewById(android.R.id.title);
        mListView = (ListView) childPrefScreen.findViewById(android.R.id.list);
        if (mDividerSpecified) {
            mListView.setDivider(mDividerDrawable);
        }

        bind(mListView);

        // Set the title bar if title is available, else no title bar
        final CharSequence title = getTitle();
        Dialog dialog = mDialog = new Dialog(context, context.getThemeResId());
        if (TextUtils.isEmpty(title)) {
            if (titleView != null) {
                titleView.setVisibility(View.GONE);
            }
            dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        } else {
            if (titleView instanceof TextView) {
                ((TextView) titleView).setText(title);
                titleView.setVisibility(View.VISIBLE);
            } else {
                dialog.setTitle(title);
            }
        }
        dialog.setContentView(childPrefScreen);
        dialog.setOnDismissListener(this);
        if (state != null) {
            dialog.onRestoreInstanceState(state);
        }

        // Add the screen to the list of preferences screens opened as dialogs
        getPreferenceManager().addPreferencesScreen(dialog);

        dialog.show();
    }

其中顯示Dialog的程式碼與平常使用Dialog並無太大區別。但為何平時我們使用時就是Dialog樣式,而這兒就是Activity的樣式呢?

經過仔細debug後,最終發現這兒使用Dialog的方式與平時我自己使用還是不太一樣的。最重要的區別就在於Dialog的建立方式了!如下:

Dialog dialog = mDialog = new Dialog(context, context.getThemeResId());

這是該處例項化Dialog的程式碼,其中使用了Dialog的兩個引數的構造方式。有context和一個theme的resource id。
而平時我們使用時一般使用的方式是一個引數的構造方法,如下:

Dialog dialog = new Dialog(context);

那麼問題終於找到了,原來是因為構造時傳了一個theme導致的。

從程式碼可以看出傳入的theme應該是當前activity對應的theme,那麼是否意味著讓Dialog顯示成Activity效果的方式也與讓Activity顯示成Dialog的方式一致——使用對應的Activity(Dialog)的Theme就可以了?

答案是肯定的,於是我們也可以使用Activity的Theme將Dialog也顯示為Activity了。

Theme中有什麼魔法

通過斷點除錯(沒有對應除錯機原始碼的話就用Log輸出的方式吧…),在Dialog顯示全屏後中斷執行,查看了其中一些變數的值。
因為想到最終我們看到的Dialog和Activity效果最大的不同是在其寬高是否填滿了手機螢幕,因此著重查看了Dialog的window的相關屬性。除錯過程中發現了一個重要資訊。

對於當前顯示成Activity的Dialog,其window的attr中寬高屬性為-1(MATCH_PARENT)

getWindow().getAttributes().width = -1
getWindow().getAttributes().height = -1

而對於正常建立的Dialog,其window的attr中寬高屬性卻不是-1的。因此追查對width變數賦值的程式碼,追查到PhoneWindow中的一段程式碼,如下:

//SDK/sources/android-25/com/android/internal/policy/PhoneWindow.java

        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }

其中的setLayout(WRAP_CONTENT, WRAP_CONTENT);即為對attr的width和height賦值的方法。可以看到,該處先從屬性集中獲取了theme中定義的windowIsFloating屬性,然後判斷若是floating(浮動)的,就設定window的attr引數寬高為WRAP_CONTENT。

setLayout定義在PhoneWindow的父類Window中,定義如下:

    /**
     * Set the width and height layout parameters of the window.  The default
     * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT
     * or an absolute value to make a window that is not full-screen.
     *
     * @param width The desired layout width of the window.
     * @param height The desired layout height of the window.
     *
     * @see ViewGroup.LayoutParams#height
     * @see ViewGroup.LayoutParams#width
     */
    public void setLayout(int width, int height) {
        final WindowManager.LayoutParams attrs = getAttributes();
        attrs.width = width;
        attrs.height = height;
        dispatchWindowAttributesChanged(attrs);
    }

從註釋中可以看到,若未指定寬高,就會使用MATCH_PARENT,即為填滿手機視窗。

所以,可以斷定,在Dialog的Theme中肯定是指定了android:windowIsFloatingtrue

檢視一個基礎的Dialog的Theme內容,其中關於window的屬性有如下這些:

<!-- SDK/platforms/android-25/data/res/values/themes.xml -->
<style name="Theme.Dialog">
        <item name="windowFrame">@null</item>
        <item name="windowTitleStyle">@style/DialogWindowTitle</item>
        <item name="windowBackground">@drawable/panel_background</item>
        <item name="windowIsFloating">true</item>
        <item name="windowContentOverlay">@null</item>
        <item name="windowAnimationStyle">@style/Animation.Dialog</item>
        <item name="windowSoftInputMode">stateUnspecified|adjustPan</item>
        <item name="windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
        <item name="windowActionModeOverlay">true</item>
        ...
    </style>

可以看到,其中除了定義android:windowIsFloating,還指定了其他一些值,那些值具體是控制什麼的,在那些類中被使用就不去探查了,待有需要時再檢視吧(雖然根據以window開始的名字猜測其最有可能也是在Window或者PhoneWindow)。

相關推薦

Android Dialog顯示Activity

前言 我們都知道要想讓一個Activity顯示成為Dialog的樣式可以對Activity指定Dialog的主題(Theme),但今天使用PreferenceScreen時發現內嵌的PreferenceScreen將一個對話方塊(Dialog)顯示出了Acti

android UI 的去標題欄和去狀態列

Android 設定隱藏標題欄和狀態列有兩種方法: 第一:在程式碼中實現 PS:設定隱藏標題欄和狀態列的程式碼一定要寫在 setContentView(R.layout.activity_main)前面,否則會報錯。 protected void onCreate(Bund

Android面試集錦之Activity知識整理

面試集錦是參考了慕課網BAT某大神的視訊。 本文分為四個部分: 1.Activity生命週期 2.Activity任務棧 3.Activity啟動模式 4.scheme跳轉協議 一、Activity的生命週期 什麼是Activity? android與使用者進行互動的時候,

Android判斷Activity是否的方式!!!

在Android中設定全屏的方式大致有兩種,一種是通過程式碼控制,一種是通過theme樣式配置。 那如何判斷一個Activity是不是全屏呢? 可以有以下幾種方式: 1.判斷window flag屬性(程式碼控制) if ( (activity.getWindow().getAttr

Android 跨進程啟動Activity的三種解決方案

orien 但是 解決 icon draw lun 簡單 android基礎 分享 原文鏈接:http://www.cnblogs.com/feidu/p/8057012.html 當Android跨進程啟動Activity時,過程界面很黑屏(白屏)短暫時間(幾百毫秒?)。

Androidactivity實現無標題欄透明

1.在xml檔案中進行配置 在專案的清單檔案AndroidManifest.xml中,在對應Activity進行如下配置即可。 2.編寫程式碼設定 在對應activity的onCreate()方法中加入如下程式碼即可 實現全屏效果: getWindow().set

Android 跨程序啟動Activity的三種解決方案

當Android跨程序啟動Activity時,過程介面很黑屏(白屏)短暫時間(幾百毫秒?)。當然從桌面Lunacher啟動一個App時也會出現相同情況,那是因為App冷啟動也屬於跨程序啟動Activity。為什麼沒會出現這種情況呢?真正元凶就是Android建立

android AppCompat, splash啟動白,去掉狀態列,以及splash與虛擬按鍵遮擋

在做專案是,需要加一個splash,以美化應用,消除app啟動等待的乏味 一、使用環境         使用Android Studio activity 繼承 AppCompatActivity, 實現splash功能         測試環境,沒有虛擬返回按鍵的安卓手機

Android上透明狀態列,顯示以及沉浸模式

在Android上為了實現全屏顯示,透明狀態列,沉浸模式等效果,往往需要我們掌握和系統UI顯示隱藏相關的各種Flag。Android上API版本混亂,各種Flag林立。今天我們就來聊聊這些Flags。 在Android Kitkat中引入Immersive Mode

設定Activity顯示的兩種方法

 方法1:AndroidManifest.xml 裡,Activity的 android:theme  指定為" @android:style/Theme.NoTitleBar.Fullscreen" 示例:   <application         andr

android之隱藏狀態列,顯示和隱藏虛擬按鍵

廢話不多說,直接貼程式碼 //去除title requestWindowFeature(Window.FEATURE_NO_TITLE);   //去掉Activity上面的狀態列 getWindow().setFlags(WindowManager.La

小米紅米1S 電信/聯通版 專用TWRP2.8.1.1中文版 觸摸/支持MTP掛載內外置存儲

red jsb 防止 文化 sdcard article read pop 兩個 轉載請註明出處和鏈接: http://blog.csdn.net/syhost/article/details/39340477 說說中文TWRP的簡史:

WebView 視訊播放,按鈕顯示不出來,後不能播放視訊

最近專案的一個需求,需要在Webview 裡面播放視訊遇到了一些問題: 視訊可以正常播放但是,視訊底部的全屏按鈕沒了,只有一個音量按鈕。修改後,點選全屏,視訊不能播放。 接下來一 一解決: 問題1 :沒有全屏按鈕: 如圖:可以看到只有一個音量按鈕,沒有全屏按鈕。 查了一

使用ScrollView時解決android佈局不能撐滿的問題

      當ScrollView巢狀LinearLayout,在某些手機上下面會留白,問題的解決辦法是在第一層LinearLayout裡面巢狀多個 LinearLayout,最重要的是將ScrollView中android:fillV

Android--四大元件之Activity

####1. Activity是什麼? ####2. 生命週期 ######1). Activity跳轉 ######2). 從後臺啟動 ######3). 橫豎屏切換 ####3. 啟動模式 ######1). 任務棧 ######2). laucherMode ######3).

開機桌面無法顯示或只顯示出一個我的文件視窗的解決方法

現象:若是中病毒後,開機桌面無法顯示(黑屏)或只顯示出一個我的文件資料夾(當前使用者)視窗解決方法(針對XP/Vista OS):修改登錄檔winlogon的鍵值 Shell項值為:Explorer.exe (原始預設值)以下為XP 系統中HKEY_LOCAL_MACHIN

Android程式設計實現WebView自適應方法小結

本文例項講述了Android程式設計實現WebView自適應全屏的方法。分享給大家供大家參考,具體如下: 第一種: settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(t

C++實現螢幕截圖截圖

最近維護的專案,在某些情況下,光有日誌還不行,於是添加了截圖功能,特定情況下,會自動截圖,輔助分析,從而改程序序。以下是截圖實現程式碼。 void CDemoDlg::ScreenShot(void) { CWnd *pDesktop = GetDesktopWi

小米紅米1S 電信/聯通版 專用TWRP2.8.1.1中文版 觸控/支援MTP掛載內外接儲存

轉載請註明出處和連結: 說說中文TWRP的簡史:         中文TWRP是本人在2012.10月在原英文TWRP的基礎上首次修改編譯成功, 後經suky大神完善字型檔生成指令碼併產生新的字模儲存提取方式, 進而形成兩種漢化方式, 並且經本人修改都支援多國語言

截圖的各種實現or長截圖

全屏截圖: /** * 傳入的activity是要截圖的activity */ public static Bitmap getViewBitmap(Activity activity) {