詳細解讀DialogFragment

来源:http://www.cnblogs.com/mercuryli/archive/2016/04/09/5372496.html
-Advertisement-
Play Games

原博客地址:http://www.cnblogs.com/tianzhijiexian/p/4161811.html 相信看這篇文章的人都應該知道android中的Dialog了吧,如果對於Dialog還不是很瞭解可以看我之前的一篇詳解文章: Dialog詳解:http://www.cnblogs. ...


原博客地址:http://www.cnblogs.com/tianzhijiexian/p/4161811.html

相信看這篇文章的人都應該知道android中的Dialog了吧,如果對於Dialog還不是很瞭解可以看我之前的一篇詳解文章:

Dialog詳解:http://www.cnblogs.com/tianzhijiexian/p/3867731.html

隨著Fragment這個類的引 入,Google官方推薦大家使用DialogFragment來代替傳統的Dialog,那麼是不是說我們之前學習的Dialog知識都沒有用處了呢? 非也,新的fragment是來方便大家更好的管理和重用Dialog,之前的知識其實都是可以拿來就用的,僅僅需要少許的改變。

 

一、Dialog和DialogFragment的區別和優劣

新來的DialogFragment讓dialog也變成了碎片,相比之前來說應該做了很多優化和處理,對於程式員來看對話框現在更加具體了,就是一個activity上的一個fragment,我們也可以用fragment的知識來管理對話框了。

我們看看之前是怎麼運用對話框對象的

複製代碼
  AlertDialog dialog = new AlertDialog.Builder(this)
        .setTitle("Dialog")
        .setMessage("thisis a dialog")
        .show();
複製代碼

如果這個時候屏幕方向發生變化,就會導致Activity重建,然後之前顯示的對話框就不見了。查看log可以發現這個錯誤:

04-1917:30:06.999: E/WindowManager(14495): Activitycom.example.androidtest.MainActivity has leaked windowcom.android.internal.policy.impl.PhoneWindow$DecorView{42ca3c18 V.E.....R....... 0,0-1026,414} that was originally added here

當然我們也可以無視這個錯誤,因為程式不會因此崩潰(看來android本身就已經預料到這種情況了)。

如果我們想要在旋轉屏幕的時候也能保證這個對話框顯示就需要做一定的處理了,在activity要銷毀前設立一個標誌,看這時對話框是否是顯示狀態,如果是那麼activity在下次建立時直接顯示對話框。

在onSaveInstanceState中

複製代碼
@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (dialog != null && dialog.isShowing()) {
            outState.putBoolean("DIALOG_SHOWN", true);
        }
    }
複製代碼

在onCreat中

複製代碼
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        
        if (savedInstanceState != null) {
            boolean isShown = savedInstanceState.getBoolean("DIALOG_SHOWN");
            if (isShown) {
                AlertDialog dialog = new AlertDialog.Builder(this).setTitle("Dialog")
                        .setMessage("thisis a dialog").show();
            }
        }
    ……
}
複製代碼

使用DialogFragment來管理對話框 就不會有這種問題了,代碼也少了很多的邏輯處理。當你旋轉屏幕的時候,fragmentManager會自定管理DialogFragment的生命周 期,如果當前已經顯示在屏幕上了,那麼旋轉屏幕後夜會自動顯示,下麵就是在屏幕旋轉時的log輸出。

4-1917:45:41.289: D/==========(16156): MyDialogFragment : onAttach

04-1917:45:41.299: D/==========(16156): MyDialogFragment : onCreate

04-1917:45:41.299: D/==========(16156): MyDialogFragment : onCreateView

04-1917:45:41.309: D/==========(16156): MyDialogFragment : onStart

04-1917:45:50.619: D/==========(16156): MyDialogFragment : onStop

04-1917:45:50.619: D/==========(16156): third activity on destroy

04-1917:45:50.619:D/==========(16156): MyDialogFragment : onDestroyView

04-1917:45:50.619: D/==========(16156): MyDialogFragment : onDetach

04-1917:45:50.639: D/==========(16156): MyDialogFragment : onAttach

04-1917:45:50.639: D/==========(16156): MyDialogFragment : onCreate

04-1917:45:50.659: D/==========(16156): MyDialogFragment : onCreateView

04-1917:45:50.659: D/==========(16156): MyDialogFragment : onStart

Ok,當然你可以不以為然,你說我的應用就是豎著用的,旋轉屏幕畢竟是小概率事件,誰會開著對話框旋轉來旋轉去啊。那麼相信下麵的好處你一定不能否定吧。

我們之前用Dialog的時候,在activity中必須要建立這個對象,而且一般我們都是需要給它放監聽器的,比如下麵的代碼:

複製代碼
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.guid_main);new AlertDialog.Builder(GuideActivity.this).setTitle("用戶申明")
        .setMessage(getResources().getString(R.string.statement))
        .setPositiveButton("我同意", new positiveListener())
        .setNegativeButton("不同意", new negativeListener())
        .setCancelable(false)
        .show();
    }private class positiveListener implements DialogInterface.OnClickListener {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            prefs.setIsFirstTime(false);
        }
    }
    
    private class negativeListener implements DialogInterface.OnClickListener {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Util.virtualHome(GuideActivity.this);
        }
    }
複製代碼

 

你會發現這麼長的代碼很破壞activity中 的邏輯性,有木有!!!在activity中我們處理的各種控制項的顯示和邏輯,但對於dialog這種不屬於activity並且建立和處理邏輯都自成一 體的東西,我們為什麼要在activity中建立呢?而且為了方便重用,我們在實際過程中基本都會建立一個dialog的工具類來做處理,所以為什麼不用 DialogFragment來實現呢?如果通過它來實現,我們就能很方便的進行管理對話框。

此外,當 旋轉屏幕和按下後退鍵時可以更好的管理其聲明周期,它和Fragment有著基本一致的聲明周期。且DialogFragment也允許開發者把 Dialog作為內嵌的組件進行重用,類似Fragment(可以在大屏幕和小屏幕顯示出不同的效果)。有可能我們在大屏幕上就不需要彈出一個對話框了, 直接內嵌在activity界面中顯示即可。這點也很贊!

 

二、DialogFragment的最簡單用法

使用DialogFragment很簡單,甚至比用Fragment還簡單,因為在api中已經實現了fragment切換對象了。

1.建立一個fragment對象

複製代碼
package com.kale.dialogfragmenttest;

import android.app.DialogFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyDialogFragment extends DialogFragment{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        System.out.println("tag = "+getTag()); // tag which is from acitivity which started this fragment

        return inflater.inflate(R.layout.dialog, null);
    }
}
複製代碼

我們建立了一個fragment,讓他繼承了DialogFragment,在onCreatView中通過佈局文件建立了一個view,這和fragment完全一致。

佈局文件如下:

複製代碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:padding="16dp">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <EditText
        android:layout_marginTop="10dp"
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10">
        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/button1"
        android:layout_marginTop="10dp"
        android:layout_gravity="center_horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>
複製代碼

 

2.在activity中啟用這個dialog

複製代碼
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        new DialogFragmentTest()
        .show(getFragmentManager(), "dialog_fragment");
    }
複製代碼

很像Dialog吧,也是支持鏈式編程的。這裡面的參數:① 一個fragmentManager,在低版本中用getSupport來獲取;② 一個tag(String)通過這個tag可以告訴fragment是誰啟動了它,當然這僅僅是這個tag的一種使用方式啦。在fragment中可以通 過getTag()方法來獲取這個tag

這裡多說一句,在 一年前我還一直說要相容要相容,不相容的demo是很不負責任的,但是現在來看,低版本的用戶真的很少很少了,而且這些低版本的用戶已經不能是我們的主流 用戶了,所以在2014年末,我可以負責任的說,可以不用相容2.x的系統了。我之前寫過很多相容的文章,actionbar啊,對話框的相容啊,但現在 都變得無所謂了,其實任何事物的發展都是如此。很多之前很重要的技術,在新的發展中已經慢慢變得無足輕重了,但我們之前為之付出的東西卻不是無價值的。一 個原因是為自己之前的工作找到價值,一種是在那段時光中我們慢慢體會到了很多東西,這些東西就是我們的閱歷也是一種談資。

好,閑話少敘,下麵是運行效果:

有人會說,上面的那個空白的title好醜,我想去掉。當然可以,這就是fragment的好處,用這個方法:

getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);

複製代碼
public class MyDialogFragment extends DialogFragment{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        System.out.println("tag = "+getTag());
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        return inflater.inflate(R.layout.dialog, null);
    }
}
複製代碼

 

現在它變成了這個樣子:

所以你可以看到,任何改變都是要付出代價的,如 果你還是像之前一樣用match_parent來制定控制項的寬度,那麼就是這種結果。可以說那個title欄就是一個房梁,支撐著對話框的寬度,沒了它就 只能自適應了。解決辦法就是自定義控制項的寬度,寫個幾百dp啥的,沒任何技術難度。

註意:

如果你的DialogFragment是Activity的內部類,必須將DialogFragment定義為靜態的。否則會報錯!!!

複製代碼
    public static class DialogFragmentTest extends DialogFragment {

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            // TODO 自動生成的方法存根
            return inflater.inflate(R.layout.dialog, null);
        }
    }
複製代碼

 

三、DialogFragment啟動、終止過程分析

之前說了,我們沒有像fragment那樣建立一個fragment載入對象進行fragment的載入,也沒有commit,但卻能使用dialogFragment對象,這是為什麼呢?

我們先來回顧下fragment是怎麼使用的。

① 建立FragmentManager對象,用來管理fragment

② 建立fragmentTransaction對象,用來添加和fragment

③ 提交fragment切換(commit)

複製代碼
    FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction ft = fragmentManager.beginTransaction();  
    
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.add(R.id.container_fragment, new MyFragment())
        .commit();
複製代碼

替換fragment的方法

複製代碼
  getSupportFragmentManager().beginTransaction().
        replace(R.id.container_fragment02, fragment)
        .addToBackStack(null)
        .commit();
複製代碼

 

現在,我們通過源碼來分析下DialogFragment的啟動方式

我們在使用它的時候沒有去用fragmentTransaction對象,也沒有執行add,也沒有執行commit,僅僅提供了一個fragmentManager,那麼它是怎麼被添加的呢?我們知道這個對話框是用show方法顯示的,那麼就來看看這個方法吧。

DialogFragment源碼:

1. show()

複製代碼
    public void show(FragmentManager manager, String tag){
        mDismissed = false; 
        mShownByMe = true; 
        FragmentTransaction ft = manager.beginTransaction(); // creat a fragmentTransaction
        ft.add(this, tag); // add fragment with tag
        ft.commit(); 
    } 
複製代碼

真相大白,api自動給你實現了一個fragment切換的對象,而且在show的時候就已經add了fragment,所以沒有任何問題~

在add方法中沒有提供容器的id,所以表示是載入到當前activity中的,在添加後也的確調用了commit方法

2. show()的另一種形式

複製代碼
    public int show(FragmentTransaction transaction, String tag) {
        mDismissed = false; 
        mShownByMe = true; 
        transaction.add(this, tag); 
        mViewDestroyed = false; 
        mBackStackId = transaction.commit();
        return mBackStackId; 
    }  
複製代碼

上面的show方法傳入的是一個fragmentTransaction對象,這個也很容易理解。我們之前傳入fragmentManager對象的目的就是生成這個fragmentTransaction對象,這回我們可以在傳入一個已經配置好的fragmentTransaction對象,大大增加了可定製性。所以api的制訂也是大神們心血的結晶啊。

3.dimiss()

源碼:

複製代碼
** 
 * Dismiss the fragment and its dialog.  If the fragment was added to the 
 * back stack, all back stack state up to and including this entry will 
 * be popped.  Otherwise, a new transaction will be committed to remove 
 * the fragment. 
 */  
public void dismiss() {  
    dismissInternal(false);  
}  
  
void dismissInternal(boolean allowStateLoss) {  
    if (mDismissed) {  
        return;  
    }  
    mDismissed = true;  
    mShownByMe = false;  
    if (mDialog != null) {  
        mDialog.dismiss();  
        mDialog = null;  
    }  
    mViewDestroyed = true;  
    if (mBackStackId >= 0) {  
        getFragmentManager().popBackStack(mBackStackId,  
                FragmentManager.POP_BACK_STACK_INCLUSIVE);  
        mBackStackId = -1;  
    } else {  
        FragmentTransaction ft = getFragmentManager().beginTransaction();  
        ft.remove(this);   
        if (allowStateLoss) {  
            ft.commitAllowingStateLoss();  
        } else {  
            ft.commit();  
        }  
    }  
}  
複製代碼

我們知道瞭如果一個DialogFragment關閉的時候會檢查堆棧裡面有沒有其他的對象,如果有就pop出來,如果沒有就直接remove和commit。也就是說:如果back stack堆棧有該Dialog,將其pop出來,否則ft.remove(this); ft.commit();。估計pop的操作也包含ft.remove()和ft.commit()。調用dismiss()會觸發onDismiss()回調函數。

跟蹤狀態,如下:

 

四、通過onCreateView()來建立對話框佈局

上面的例子中我們已經在onCreateView()建立的對話框佈局,這時fragment中建立佈局的傳統寫法,很適合用於自定義的對話框,我們可以修改任何的東西,包括對話框的style。上面的例子中我們已經幹掉了對話框上面title的區域,而我們也沒發現可以設置標題的方法,感覺上面那個標題欄就是個標題黨,毫無意義(之後會說到這塊區域的用處)。

我們在onCreat中可以設置對話框的風格和各種屬性,但是千萬別設置關於view的東西,因為這時候對話框還沒建立呢,有關於view的東西在onCreatView中去設置吧,這裡我簡單設置了一個button的點擊事件——關閉對話框

複製代碼
public class MyDialogFragment extends DialogFragment{

    @Override 
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState); 
        //如果setCancelable()中參數為true,若點擊dialog覆蓋不到的activity的空白或者按返回鍵,
        //則進行cancel,狀態檢測依次onCancel()和onDismiss()。如參數為false,則按空白處或返回鍵無反應。預設為true 
        setCancelable(true); 
        //可以設置dialog的顯示風格
        //setStyle(style,theme);  
    }  
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        //getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        View rootView = inflater.inflate(R.layout.dialog, null);
        Button btn = (Button)rootView.findViewById(R.id.button);
        btn.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO 自動生成的方法存根
                dismiss();
            }
        });
        return rootView;
    }

}
複製代碼

 

補充:實現信息保存

在activity橫豎屏切換的時候,dialog現在可以自動重建了,如果你在editText中輸入了信息,在重建的時候會不會保留之前的呢?在4.2和4.4中對話框人性化的自定保存了之前輸入的內容,我們無須手動處理。但如果你測試的手機被奇葩的定製了,那就乖乖的保存數據吧。

複製代碼
public class MyDialogFragment extends DialogFragment {

    EditText editText;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.dialog, null);

        editText = (EditText) rootView.findViewById(R.id.editText);
        if (savedInstanceState != null) {
            CharSequence text = savedInstanceState.getCharSequence("input data");
            editText.setText(text == null ? "" : text);
        }
        return rootView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putCharSequence("input data", editText.getText());
        super.onSaveInstanceState(outState);
    }

}
複製代碼

 

五、通過onCreateDialog()來快捷的建立對話框

我們上面建立的對話框都是用自定義佈局的,難道我們之前學過的dialog知識都沒用了麽?我們如果沒自定義對話框的需求,怎麼辦?就沒有一種快一點的方式來建立對話框麽?快用onCreatDialog吧!!!這個回調方法是DialogFragment獨有的,通過它返回的是一個Dialog對象,這個對象就會被顯示到屏幕上。千萬別同時使用onCreatView和onCreatDialog方法,他們僅僅是為了完成同樣一個目的的兩條路而已。

PS:從生命周期的順序而言,先執行onCreateDialog(),後執行onCreateView()

我在onCreatDialog建立一個警告對話框的builder,通過這個builder的create()方法來生成一個AlertDialog對象,因為AlertDialog是Dialog的子類,所以可以直接返回給Dialog。這裡可以用其他不同對話框的builder,代碼類似,只不過就是通過builder的creat()方法返回的是不同的對象而已。builder模式也是蠻巧妙的~

複製代碼
public class MyDialogFragment extends DialogFragment implements android.content.DialogInterface.OnClickListener{
    
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("用戶申明")
            .setMessage(getResources().getString(R.string.hello_world))
            .setPositiveButton("我同意", this)
            .setNegativeButton("不同意", this)
            .setCancelable(false);
            //.show(); // show cann't be use here
        
        return builder.create();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // TODO 自動生成的方法存根
    }
}
複製代碼

 

顯示效果:

看到了麽,這裡的標題欄終於有用了,原來那個標題欄是為了給我們在這裡用的啊~

註意:

① 因為這裡創建的是一個dialog,所以用的onclickListener自然是對話框中的listener了。

② 千萬別在構建對話框對象的時候順手寫了show()方法,我們現在是在fragment中初始化一個對話框,真正讓他顯示的時候是在activity中用這個dialogFragment對象顯示的。如果這裡寫了show方法不會報錯,但是會出現兩個對話框!

 

那麼,我們能不能在這裡自定義對話框呢?當然可以啦,本身Dialog.builder就提供了自定義view的方法,和之前用Dialog一樣自定義下viwe就搞定了。

複製代碼
 @Override  
    public Dialog onCreateDialog(Bundle savedInstanceState)  
    {  
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());  
        // Get the layout inflater  
        LayoutInflater inflater = getActivity().getLayoutInflater();  
        View view = inflater.inflate(R.layout.fragment_login_dialog, null);  
        // Inflate and set the layout for the dialog  
        // Pass null as the parent view because its going in the dialog layout  
        builder.setView(view)  // set your own view
                // Add action buttons  
                .setPositiveButton("Sign in",  
                        new DialogInterface.OnClickListener()  
                        {  
                            @Override  
                            public void onClick(DialogInterface dialog, int id)  
                            {  
                            }  
                        }).setNegativeButton("Cancel", null);  
        return builder.create();  
    }  
複製代碼

 

這裡貼下我在另一篇文章的自定義對話框view的代碼片段:

詳細看這裡:http://www.cnblogs.com/tianzhijiexian/p/3867731.html

複製代碼
/**
     * 自定義視圖對話框
     * 
     * @param title
     */
    public void viewDialog(String title) {
        // LayoutInflater是用來找layout文件夾下的xml佈局文件,並且實例化
        LayoutInflater factory = LayoutInflater.from(mContext);
        // 把activity_login中的控制項定義在View中
        View view = factory.inflate(R.layout.dialog_layout, null);
        // 將LoginActivity中的控制項顯示在對話框中

        // 獲取用戶輸入的“用戶名”,“密碼”
        // 註意:view.findViewById很重要,因為上面factory.inflate(R.layout.activity_login,
        // null)將頁面佈局賦值給了view了
        TextView titleTv = (TextView) view
                .findViewById(R.id.dialog_textView_id);
        titleTv.setText(title);
        Button btn = (Button) view.findViewById(R.id.dialog_logout_button_id);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                showToast("按下自定義視圖的按鈕了~");
            }
        });

        builder
        // 設定顯示的View
        .setView(view);
        // 設置dialog是否為模態,false表示模態,true表示非模態
        // ab.setCancelable(false);
        // 對話框的創建、顯示,這裡顯示的位置是在屏幕的最下麵,但是很不推薦這個種做法,因為距底部有一段空隙
        AlertDialog dialog = builder.create();
        Window window = dialog.getWindow();
        window.setGravity(Gravity.BOTTOM); // 此處可以設置dialog顯示的位置
        window.setWindowAnimations(R.style.myAnimationstyle); // 添加動畫
        dialog.show();
    }
複製代碼

 

六、DialogFragment與Activity之前進行通信

思路很簡單,就是定義一個傳輸數據的介面,強制activity實現這個介面,在fragment需要傳遞數據的時候去調用這個介面的方法,activity就能在這個方法中得到相應的數據了。這點在之前的fragment傳遞數據中已經介紹過了,可以參考這篇文章:

http://www.cnblogs.com/tianzhijiexian/p/3888330.html

在真正項目中,fragment的編寫並不需要瞭解activity的各類方法,好的編程風格是將fragment所涉及的方法以介面的方式封裝起來,我在此寫一個例子來說明一下。

1. 寫一個介面——DataCallback

複製代碼
package com.kale.dialogfragmenttest;

public interface DataCallback {

    public void getData(String data);
}
複製代碼

 

2.activity實現這個介面

複製代碼
public class MainActivity extends Activity implements DataCallback{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        new MyDialogFragment()
        .show(getFragmentManager(), "dialog_fragment");
    }

    @Override
    public void getData(String data) {
        // TODO 自動生成的方法存根
        System.out.println("data = "+ data);
    }

}
複製代碼

 

3.在DialogFragment中使用這個介面,並且用instanceof來看啟動它的activity是否實現了這個介面,如果沒實現就拋出異常。這樣我們就能保證在大型項目中不會出現忘記實現這個介面的問題了。

複製代碼
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;

public class MyDialogFragment extends DialogFragment implements
        android.content.DialogInterface.OnClickListener {

    @Override
    public void onAttach(Activity activity) {
        // onAttach()是合適的早期階段進行檢查MyActivity是否真的實現了介面。
        // 採用介面的方式,dialog無需詳細瞭解MyActivity,只需瞭解其所需的介面函數,這是真正項目中應採用的方式。
        if (!(activity instanceof DataCallback)) {
            throw new IllegalStateException("fragment所在的Activity必須實現Callbacks介面");
        }
        super.onAttach(activity);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("用戶申明")
                .setMessage(getResources().getString(R.string.hello_world))
                .setPositiveButton("我同意", this).setNegativeButton("不同意", this)
                .setCancelable(false);
        // show();

        return builder.create();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // TODO 自動生成的方法存根
        DataCallback callback = (DataCallback) getActivity();
        callback.getData("test");
    }

}
複製代碼

 

此外fragment也可以通過fragment管理器,通過tag,獲取其他fragment實例,從而進行fragment之間的通信。當然從編程思想的角度看,fragment之間的過多進行交叉調用,不利於程式的管控。

 

七、用DialogFragment實現再次彈窗

有時候我們可能有這樣的需求,點擊對話框中的一個按鈕後又彈出一個對話框,這個該怎麼做呢?首先在點擊事件中將這個對話框在屏幕上移除,然後把這個fragment壓棧,最後建立一個新的dialogFragment對象,show出來。我們雖然讓這個fragment在屏幕上消失,但還是可以通過fragment管理器到回退棧中找到它。

二次彈窗的代碼:

複製代碼
     FragmentTransaction ft = getFragmentManager().beginTransaction();

        /*
         * 如果不執行remove(),對話框即不會進入onDismiss()狀態。會被顯示在新的對話框下方,是可見的。
         * 主要考慮美觀的問題,如果下麵的對話框大於上面的對話框就很難看了。 對於Dialog,container為0或者null。
         */
        ft.remove(this);
        /*
         * 將當前的PromptDialogFragment加入到回退堆棧,當用戶按返回鍵,或者通過按幫助框的Close按鈕dismiss幫助框是,
         * 重新顯示提示框。 對於back stack的處理,系統具有一定的智能。例如:執行兩次addToStackStack(),實際不會重覆壓棧。
         * 有例如:註釋掉remove()語句,即提示框不消失,而是在幫助框的下麵。
         * 但是在實驗中發現是否有addToBackStack()都不會結果有影響,系統能夠分析到對象存在,不需要壓棧。沒有去查源代碼,
         * 猜測通過mBackStackId比對來進行智能處理。
         */
        ft.addToBackStack(null);
        
        new OhterDialogFragment()
        .show(getFragmentManager(), "dialog_fragment");

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • RegexKitLite是什麼? RegexKitLite是一個非常方便的處理正則表達式的第三方類庫。 本身只有一個RegexKitLite.h和RegexKitLite.m 導入RegexKitLite可能會出現什麼錯誤? 我們在arc的情況下直接導入會報20個錯誤,錯誤如下: 我們需要在Buil ...
  • 設置嚮導頁面,通過SharedPreferences來判斷是否已經設置過了,跳轉到不同的頁面 自定義樣式 在res/values/styles.xml中 添加節點<style name=””>,設置名稱屬性 在<style>節點裡面,添加節點<item name=””>設置名稱屬性,就是佈局的各種參 ...
  • 技術渣,大家將就著看 首先我們需要一個xml數據保存到資料庫,這裡我從QQ下麵找到一個loclist.xml文件 <CountryRegion Name="中國" Code="1"> <State Name="北京" Code="11"> <City Name="東城" Code="1" /> <C ...
  • 演示效果如下: 利用NSURLSession實現斷點下載 1.屬性 2.初始化屬性 3.實現點擊事件 4.實現代理方法 //保存恢複數據 self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData]; 怎麼實現的斷點下載? ...
  • 元組(tuples) tuples是將多個單一的值組合為一個複合的值。它可以包含任何類型的值,而不需要都是相同類型。 一.元組的創建 1。 2. 輸出結果 二.元組數據的訪問 1.statusCode,statusMessage就像字典中的key一樣,我們可以通過它們來訪問元祖的元素。 2.還可以使 ...
  • 1.先看看網上流傳的他們的繼承圖: 從上面可以看出CABasicAnimation與CAKeyframeAnimation都繼承於CAPropertyAnimation。而CAPropertyAnimation提供了基本的動畫屬性設置keyPath、additive、cumulative。 2.詳解 ...
  • 一般的手機沒有root許可權,進不去data/data目錄,當手機刷機了後,擁有root許可權,就可以進入data/data目錄,查看我們保存的密碼文件,因此我們需要對存入的密碼進行MD5加密 獲取MessageDigest信息摘要器對象,調用MessageDigest.getInstance(“md5 ...
  • 一直以來,我們把所有的段描述符都放在GDT中,而不管它屬於內核還是用戶程式,為了有效地在任務之間實施隔離,處理器建議每個任務都應當具有自己的描述符表,稱為局部描述符表LDT,並且把專屬於自己的那些段放到LDT中。 和GDT一樣,LDT也是用來存放描述符的。不同之處在於,LDT只屬於某個任務。或者說, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...