Android分析DialogFragment源碼

一. DialogFragment源碼分析。

因為是Fragment旭寿,我們先從onCreate生命周期入手躯保。

 @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 一般這樣設(shè)置樣式
        setStyle(.....);
    }

先來DialogFragment中有個style方法

    public void setStyle(int style, @StyleRes int theme) {
        this.mStyle = style;
        if (this.mStyle == 2 || this.mStyle == 3) {
            this.mTheme = 16973913;
        }

        if (theme != 0) {
            this.mTheme = theme;
        }

    }

可以看出這里并沒有做什么操做,只是把傳進來的style和theme存到全局變量菌瘫。
因為fragment不會無緣無故去走他的生命周期方法,所以入口方法就是show()方法布卡。

    public void show(FragmentManager manager, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();
    }

可以看出這里先設(shè)置了兩個屬性mDismissed和mShownByMe 雨让。很可惜的是源碼中并沒有給這兩個屬性添加注釋,那就只能猜了忿等,從命名上猜和編程習(xí)慣猜mDismissed是記錄這個Dialog是否dismiss栖忠,mShownByMe 是記錄是否是用戶調(diào)起的show方法。
然后用manager.beginTransaction()拿到FragmentTransaction贸街,抽象理解就是當(dāng)前這個外層頁面的FragmentManager的事物庵寞,然后把當(dāng)前fragment添加到外層界面的FragmentManager,commit就是提交匾浪。
加了會怎么樣皇帮?那還用說,我們先看看Activity動態(tài)展示Fragment的代碼蛋辈,我隨便去網(wǎng)上copy一段代碼

        getSupportFragmentManager()  
                .beginTransaction()
                .add(布局的ID , fragment)
                .commit();

就都是這樣的操作属拾,所以你說添加了會怎樣,當(dāng)然是走Fragment的生命周期啊冷溶。按照Fragment的生命周期鉤子來走渐白,按順序看看DialogFragment有重寫哪些生命周期方法。

    public void onAttach(Context context) {
        super.onAttach(context);
        if (!this.mShownByMe) {
            this.mDismissed = false;
        }

    }

這里做了一個判斷逞频,應(yīng)該是為了安全性考慮纯衍,如果這個Dialog不是由我們調(diào)用show方法展示的話,還記得在show方法中有設(shè)置this.mDismissed = false;嗎 苗胀, 如果不是調(diào)用show方法襟诸,而恰巧這個Fragment的生命周期又被調(diào)用了。所以這里為了安全考慮補上this.mDismissed = false基协。
然后調(diào)用onCreate

    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.mShowsDialog = this.mContainerId == 0;
        if (savedInstanceState != null) {
            this.mStyle = savedInstanceState.getInt("android:style", 0);
            this.mTheme = savedInstanceState.getInt("android:theme", 0);
            this.mCancelable = savedInstanceState.getBoolean("android:cancelable", true);
            this.mShowsDialog = savedInstanceState.getBoolean("android:showsDialog", this.mShowsDialog);
            this.mBackStackId = savedInstanceState.getInt("android:backStackId", -1);
        }

    }

this.mShowsDialog = this.mContainerId == 0這個容器ID mContainerId 我也不太清楚是什么歌亲,先跳過。
下面 if (savedInstanceState != null) {......} 是就恢復(fù)數(shù)據(jù)的操作澜驮。可以看到官網(wǎng)在onCreate中的恢復(fù)數(shù)據(jù)的寫法是怎么寫的陷揪,十分建議學(xué)會使用這種做法,能讓代碼更為安全。
相應(yīng)的可以先來直接看看保存數(shù)據(jù)的做法

    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        if (this.mDialog != null) {
            Bundle dialogState = this.mDialog.onSaveInstanceState();
            if (dialogState != null) {
                outState.putBundle("android:savedDialogState", dialogState);
            }
        }

        if (this.mStyle != 0) {
            outState.putInt("android:style", this.mStyle);
        }

        if (this.mTheme != 0) {
            outState.putInt("android:theme", this.mTheme);
        }

        if (!this.mCancelable) {
            outState.putBoolean("android:cancelable", this.mCancelable);
        }

        if (!this.mShowsDialog) {
            outState.putBoolean("android:showsDialog", this.mShowsDialog);
        }

        if (this.mBackStackId != -1) {
            outState.putInt("android:backStackId", this.mBackStackId);
        }

    }

可以看到保存fragment的數(shù)據(jù)之前悍缠,先保存dialog的數(shù)據(jù)卦绣。
我們繼續(xù)來看生命周期onActivityCreated

    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (this.mShowsDialog) {
            View view = this.getView();
            if (view != null) {
                if (view.getParent() != null) {
                    throw new IllegalStateException("DialogFragment can not be attached to a container view");
                }

                this.mDialog.setContentView(view);
            }

            Activity activity = this.getActivity();
            if (activity != null) {
                this.mDialog.setOwnerActivity(activity);
            }

            this.mDialog.setCancelable(this.mCancelable);
            this.mDialog.setOnCancelListener(this);
            this.mDialog.setOnDismissListener(this);
            if (savedInstanceState != null) {
                Bundle dialogState = savedInstanceState.getBundle("android:savedDialogState");
                if (dialogState != null) {
                    this.mDialog.onRestoreInstanceState(dialogState);
                }
            }

        }
    }

可以看出這里就是把view設(shè)置給Dialog,而這個view就是我們在onCreateView方法中所返回的view飞蚓。所以先前需要判斷view.getParent()滤港,因為一個子view不能同時擁有兩個父view。
this.mDialog.setOwnerActivity(activity);這個好像是把activity傳給Dialog玷坠,因為Dialog里面肯定要用到activity的地方蜗搔。后面的代碼就是設(shè)置能關(guān)閉,設(shè)置Cancel和Dismiss時的監(jiān)聽八堡,還有獲取savedInstanceState保存的數(shù)據(jù)。
這些操作寫在這里聘芜,我估計是因為此時activity的創(chuàng)建才剛走完兄渺。

然后是onStart

    public void onStart() {
        super.onStart();
        if (this.mDialog != null) {
            this.mViewDestroyed = false;
            this.mDialog.show();
        }
    }

記錄mViewDestroyed 為false , 然后展示Dialog汰现。
可以看出在onStart生命周期中才展示Dialog挂谍,此時頁面已經(jīng)展示出來。

看看彈框頁面消失的操作

    public void onStop() {
        super.onStop();
        if (this.mDialog != null) {
            this.mDialog.hide();
        }
    }

Fragment隱藏時把Dialog也隱藏瞎饲,相當(dāng)于把他兩的狀態(tài)都綁在一起口叙。

    public void onDestroyView() {
        super.onDestroyView();
        if (this.mDialog != null) {
            this.mViewDestroyed = true;
            this.mDialog.dismiss();
            this.mDialog = null;
        }

    }

mViewDestroyed = true, 記錄當(dāng)前頁面已經(jīng)關(guān)閉嗅战,此時dialog也跟著dismiss妄田,并且this.mDialog = null;釋放掉內(nèi)存(GC過后再不到這個引用,會來釋放掉)驮捍。這里雖然沒什么難理解的疟呐,但是這4行代碼寫得非常好,值得學(xué)習(xí)东且。

最后

    public void onDetach() {
        super.onDetach();
        if (!this.mShownByMe && !this.mDismissed) {
            this.mDismissed = true;
        }

    }

這里和dismissInternal方法我感覺是有一種是做了多線程的感覺启具,所以加了雙向判斷,看起來感覺有點繞珊泳。dismissInternal方法是Dialog關(guān)閉時調(diào)用的鲁冯。

    void dismissInternal(boolean allowStateLoss) {
        if (!this.mDismissed) {
            this.mDismissed = true;
            this.mShownByMe = false;
            if (this.mDialog != null) {
                this.mDialog.dismiss();
            }

            this.mViewDestroyed = true;
            if (this.mBackStackId >= 0) {
                this.getFragmentManager().popBackStack(this.mBackStackId, 1);
                this.mBackStackId = -1;
            } else {
                FragmentTransaction ft = this.getFragmentManager().beginTransaction();
                ft.remove(this);
                if (allowStateLoss) {
                    ft.commitAllowingStateLoss();
                } else {
                    ft.commit();
                }
            }

        }
    }

如果生命周期onDetach先執(zhí)行,mShownByMe 還是為true色查,所以onDetach中的判斷不會走薯演,之后還會走dismissInternal。如果dismissInternal先執(zhí)行综慎,mDismissed為false涣仿,走判斷里的方法,
mDismissed = true
his.mShownByMe = false

if (this.mDialog != null) {
       this.mDialog.dismiss();
}

表示沒走onDestroyView方法,所以這里再走一次this.mDialog.dismiss();
mViewDestroyed = true
mBackStackId 回退狀態(tài)好港,一般流程會等于-1愉镰,這時讓外層的FragmentManager移除當(dāng)前Fragment。

我覺得這里就是處理一個多線程的結(jié)果钧汹,調(diào)用的順序可以是以下幾種情況
(1)onDestroyView - > onDetach -> dismissInternal
(2)onDestroyView - > dismissInternal-> onDetach
(3)dismissInternal- > onDestroyView -> onDetach
所以可以看出丈探,最主要的方法是dismissInternal,它一定會調(diào)用拔莱,哪怕是在Fragment銷毀之后碗降。所以這里我有個問題:fragment銷毀了,那this就有可能被釋放為空吧塘秦,那 ft.remove(this);這個操作不是有可能報空指針嗎讼渊?這個要看FragmentManager的源碼之后才知道,也許它在里面有判空操作尊剔。
他的這個多線程的邏輯應(yīng)該是挺穩(wěn)定的爪幻,就是看著會很繞,如果先調(diào)生命周期再調(diào)dismissInternal基本是沒問題须误,如果先調(diào)dismissInternal再調(diào)onDestroyView 的話挨稿,onDestroyView里面的this.mDialog.dismiss();還是會走一遍,只不過mViewDestroyed已經(jīng)為true京痢,不會再走dismissInternal里面的邏輯奶甘。
而且Dialog內(nèi)部的dismiss方法里的邏輯也有判斷,防止多次調(diào)用祭椰。
所以可以看出java的多線程是一個很麻煩的家伙臭家,為了保證調(diào)用順序沒問題,需要加一大堆判斷吭产,而且久了可能連自己也看得懵侣监,不好意思扯遠了。

最后還有一個方法沒講到—— onCreateDialog
他是在調(diào)用fragment的onGetLayoutInflater方法時調(diào)用的臣淤,onGetLayoutInflater方法是在fragment中的getLayoutInflater()方法調(diào)用后調(diào)用的橄霉,而這個getLayoutInflater()我暫時也找不到在哪里調(diào)用,但是我們可以通過打印的方式來判斷onCreateDialog再哪個生命周期之間調(diào)用邑蒋。


可以看到姓蜂,是在調(diào)onCreateView之前調(diào)用的。

所以說DialogFragment里面的Dialog在onCreate之后創(chuàng)建医吊,在onStart中展示钱慢,在onDestroyView中關(guān)閉。
也能看出卿堂,F(xiàn)ragment中其實并沒有做什么復(fù)雜的邏輯操作束莫,都是在處理生命周期懒棉、保存數(shù)據(jù)這些操作,可以看出這很符合谷歌說的建議用DialogFragment代替Dialog的概念览绿,確實是加了一層用于管理的Fragment策严。所以最核心的功能還是Dialog的功能,最核心的代碼還是Dialog的代碼饿敲。

二.Dialog源碼簡單分析

相比DialogFragment妻导,這里我不會像上面一樣那么詳細的分析,我只會分析某些方法怀各。
從DialogFragment可以在知道在onCreate之后創(chuàng)建Dialog

    @NonNull
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        return new Dialog(this.getActivity(), this.getTheme());
    }

我們進去看看構(gòu)造方法做了什么

    Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (themeResId == ResourceId.ID_NULL) {
                final TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                themeResId = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, themeResId);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setOnWindowSwipeDismissedCallback(() -> {
            if (mCancelable) {
                cancel();
            }
        });
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);

        mListenersHandler = new ListenersHandler(this);
    }

首先如果判斷是否在創(chuàng)建時有傳theme



這就是我們之前說的傳0表示沒樣式倔韭。
可以額外說說這個默認的樣式(這章暫時用不上),可以在themes.xml中找到

        <!-- Dialog attributes -->
        <item name="dialogTheme">@style/Theme.Dialog</item>
        <item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons</item>
        <item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title</item>
        <item name="dialogTitleDecorLayout">@layout/dialog_title</item>
        <item name="dialogPreferredPadding">@dimen/dialog_padding</item>
        <item name="dialogCornerRadius">0dp</item>

mContext = new ContextThemeWrapper(context, themeResId);
就是把themeResId給保存到ContextThemeWrapper里面瓢对。
之后創(chuàng)建一個Window 寿酌,final Window w = new PhoneWindow(mContext);并且設(shè)置一些監(jiān)聽的事件,并且設(shè)置Gravity居中(所以默認的Dialog都是居中顯示)沥曹。
其實可以模仿他們這里的獲取windowManger的方法

mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

這樣就創(chuàng)建好了Dialog份名,Dialog就是一個window。

然后設(shè)置布局給Dialog

可以看出在onActivityView中把布局設(shè)置給Dialog妓美,跳進去看源碼

    public void setContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
        mWindow.setContentView(view, params);
    }

也就是給這個window設(shè)置View

之后看看Dialog的展示

在onStart中展示Dialog,跳進去看源碼

   public void show() {
        if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }

        mCanceled = false;

        if (!mCreated) {
            dispatchOnCreate(null);
        } else {
            // Fill the DecorView in on any configuration changes that
            // may have occured while it was removed from the WindowManager.
            final Configuration config = mContext.getResources().getConfiguration();
            mWindow.getDecorView().dispatchConfigurationChanged(config);
        }

        onStart();
        mDecor = mWindow.getDecorView();

        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
            final ApplicationInfo info = mContext.getApplicationInfo();
            mWindow.setDefaultIcon(info.icon);
            mWindow.setDefaultLogo(info.logo);
            mActionBar = new WindowDecorActionBar(this);
        }

        WindowManager.LayoutParams l = mWindow.getAttributes();
        boolean restoreSoftInputMode = false;
        if ((l.softInputMode
                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
            l.softInputMode |=
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
            restoreSoftInputMode = true;
        }

        mWindowManager.addView(mDecor, l);
        if (restoreSoftInputMode) {
            l.softInputMode &=
                    ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        }

        mShowing = true;

        sendShowMessage();
    }

mWindow.getDecorView()這個要詳細說又要扯到window鲤孵,一直扯其它的估計都講不完壶栋,所以這里先不講window。先把這行代碼理解成獲取window頂層的view普监。
然后設(shè)置ActionBar贵试,一般我們都沒有的。
之后這行就很熟悉凯正,就是設(shè)置window的屬性

WindowManager.LayoutParams l = mWindow.getAttributes();

之后就是處理軟鍵盤的操作毙玻。注意,這個show方法中最關(guān)鍵的方法就是顯示window的頁面廊散,也就是這句代碼(因為window的相關(guān)內(nèi)容不打算在這章講)桑滩,所以先了解。

mWindowManager.addView(mDecor, l);

最后在記錄當(dāng)前狀態(tài)為展示允睹。

我們再來額外先看看隱藏的方法
    public void hide() {
        if (mDecor != null) {
            mDecor.setVisibility(View.GONE);
        }
    }

可以看出并沒有關(guān)閉window运准,只是對view做setVisibility隱藏操作。

最后看看關(guān)閉彈框

在onDestroyView中關(guān)閉Dialog缭受,跳進去看看源碼

    void dismissDialog() {
        if (mDecor == null || !mShowing) {
            return;
        }

        if (mWindow.isDestroyed()) {
            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
            return;
        }

        try {
            mWindowManager.removeViewImmediate(mDecor);
        } finally {
            if (mActionMode != null) {
                mActionMode.finish();
            }
            mDecor = null;
            mWindow.closeAllPanels();
            onStop();
            mShowing = false;

            sendDismissMessage();
        }
    }

也很簡單胁澳,就是移除窗口,然后再改變狀態(tài)米者。

mWindowManager.removeViewImmediate(mDecor);

可能有人會問韭畸,咦宇智,那為什么沒有看到在哪里設(shè)置樣式。
如果我們傳的是資源文件來設(shè)置樣式的話胰丁,資源文件會傳給context随橘,context會傳給window,樣式的設(shè)置就是在window內(nèi)部設(shè)置的隘马。如果我們動態(tài)設(shè)置樣式的話太防,一般都寫

getDialog().getWindow().XXXXXXX

這樣設(shè)置也是傳給window來設(shè)置。

三. 總結(jié)

從源碼我們可以看出酸员,DialogFragment實質(zhì)上還是操作Dialog蜒车,而Dialog實質(zhì)上是操作Window。所以我們是不是得出一個結(jié)論幔嗦,如果想測試某個屬性對Dialog有什么影響酿愧,基本上可以直接測這條屬性對Window有什么影響。
之后我總結(jié)的Dialog的一些屬性的分析邀泉,就可以寫到Window相關(guān)的地方嬉挡,關(guān)鍵的還是window,但是window的源碼就沒Dialog的這么簡單了汇恤,這個之后再講庞钢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市因谎,隨后出現(xiàn)的幾起案子基括,更是在濱河造成了極大的恐慌,老刑警劉巖财岔,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件风皿,死亡現(xiàn)場離奇詭異,居然都是意外死亡匠璧,警方通過查閱死者的電腦和手機桐款,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夷恍,“玉大人魔眨,你說我怎么就攤上這事〔锰” “怎么了冰沙?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長执虹。 經(jīng)常有香客問我拓挥,道長,這世上最難降的妖魔是什么袋励? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任侥啤,我火速辦了婚禮当叭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盖灸。我一直安慰自己蚁鳖,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布赁炎。 她就那樣靜靜地躺著醉箕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪徙垫。 梳的紋絲不亂的頭發(fā)上讥裤,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音姻报,去河邊找鬼己英。 笑死,一個胖子當(dāng)著我的面吹牛吴旋,可吹牛的內(nèi)容都是我干的损肛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荣瑟,長吁一口氣:“原來是場噩夢啊……” “哼治拿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笆焰,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤忍啤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后仙辟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡鳄梅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了粟焊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片项棠。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡挎峦,死狀恐怖坦胶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情税弃,我是刑警寧澤则果,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站茸时,受9級特大地震影響可都,放射性物質(zhì)發(fā)生泄漏渠牲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一答姥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敲长,春花似錦祈噪、人聲如沸辑鲤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盐杂。三九已至,卻和暖如春强衡,著一層夾襖步出監(jiān)牢的瞬間漩勤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留亿傅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓镜沽,卻偏偏與公主長得像缅茉,于是被迫代替她去往敵國和親译打。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 目錄介紹 1.簡單用法 2.AlertDialog源碼分析2.1 AlertDialog.Builder的構(gòu)造方法...
    楊充211閱讀 1,109評論 1 1
  • 前言 在Android開發(fā)中,Dialog被使用的非常頻繁,因此這里總結(jié)了一下Dialog的幾種使用方式 方式一 ...
    sweetying閱讀 2,207評論 2 35
  • title: Dialog官方文檔總結(jié)date: 2016-03-14 21:38:14tags: Dialogc...
    Passon_Fang閱讀 3,038評論 0 7
  • 本文出自 Eddy Wiki 韵洋,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-androi...
    eddy_wiki閱讀 3,268評論 0 20
  • 今天媽媽罵我了搪缨,說倉鼠市場有病茵不讓我玩,但是在學(xué)校我剛來時他們對我很好痹届,漸漸的就沒有人和我一起玩了僵控,我的同桌×...
    夏天的生日閱讀 109評論 0 1