1. 程式人生 > >Pro Android學習筆記(四五):Dialog(2):DialogFragment

Pro Android學習筆記(四五):Dialog(2):DialogFragment

DialogFragment的例項newInstance()已經在上一次學習筆記中實現。我們建立dialog的UI,可以通過重寫DialogFragment的兩個函式當中的一個來實現,這兩個函式是onCreateView()和onCreateDialog(),前者返回view,後者返回dialog,如同通過AlertDialog.Builder構造一樣。

重寫onCreateView()

重寫onCreateView()是fragment的傳統方式,適合自定義的對話方塊,本例適合用於提示框,如下圖所示。通過按選單彈出提示框,提示框由一個TextView,一個EditText和三個Button組成UI。按不同的按鈕觸發不同的處理。小例子自作範例,按Save和Dismiss按鈕,都會呼叫Activity的onDialogDone()函式,根據使用者的實際操作,顯示不同的資訊。按Help按鈕,則彈出一個幫助框。再彈框在稍後學習筆記中實現。

通過onCreateView()設定UI和按鍵反饋

利用Fragment的onCreateView()來實現對話方塊的UI和Fragment學習中沒有差別,在本例中,我們增加了按鈕點選的觸發,程式碼如下:

public class PromptDialogFragment extends DialogFragment implements OnClickListener
    public static PromptDialogFragment newInstance(String prompt){
        ...略...
    }

    @Override //通過重寫Fragment的onCreateView()實現dialog的UI


    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
        //1、通過inflate,根據layout XML定義,建立view
        View v = inflater.inflate(R.layout.prompt_dialog, container,false);
        TextView tv = (TextView)v.findViewById(R.id.prompt_message);
        tv.setText(getPrompt());
        //2、註冊三個button的按鍵監聽listener

        Button dismissBtn = (Button)v.findViewById(R.id.button_dismiss);
        dismissBtn.setOnClickListener(this);         
        Button saveBtn = (Button)v.findViewById(R.id.button_save);
        saveBtn.setOnClickListener(this);         
        Button helpBtn = (Button)v.findViewById(R.id.button_help);
        helpBtn.setOnClickListener(this);
        return v;
    }
    
    private String getPrompt(){
        Bundle b = getArguments();
        return b.getString("prompt-message");
    }
 

    @Override //在onCreate中設定對話方塊的風格、屬性等
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);
        //如果setCancelable()中引數為true,若點選dialog覆蓋不到的activity的空白或者按返回鍵,則進行cancel,狀態檢測依次onCancel()和onDismiss()。如引數為false,則按空白處或返回鍵無反應。預設為true 
        setCancelable(true);
        //可以設定dialog的顯示風格,如style為STYLE_NO_TITLE,將被顯示title。遺憾的是,我沒有在DialogFragment中找到設定title內容的方法。theme為0,表示由系統選擇合適的theme。
        int style = DialogFragment.STYLE_NO_NORMAL, theme = 0;
        setStyle(style,theme); 
    } 

    @Override //僅用於狀態跟蹤
    public void onCancel(DialogInterface dialog) { 
        showInfo("onCancel() is called");
        super.onCancel(dialog);
    }     

    @Override  //僅使用者狀態跟蹤
    public void onDismiss(DialogInterface dialog) { 
        showInfo("onDismiss() is called");
        super.onDismiss(dialog);
    }

    @Override //Button按鍵觸發的回撥函式
    public void onClick(View v) { 
        MainActivity act = (MainActivity)getActivity();
        switch(v.getId()){
        case R.id.button_dismiss:
            act.onDialogDone(getTag(), true, null);  //呼叫activity的onDialogDone(),通過Toast顯示相關資訊
            dismiss();  //關閉對話方塊,並觸發onDismiss()回撥函式。
            break;
        case R.id.button_help: 
            … 略:以後實現 …
            break;
        case R.id.button_save:
            TextView tv = (TextView)getView().findViewById(R.id.input_text);
            act.onDialogDone(getTag(), false, "[save]" + tv.getText()); //呼叫activity的onDialogDone(),通過Toast顯示相關資訊
            dismiss(); //關閉對話方塊,並觸發onDismiss()回撥函式
            break;
        default:
            break;
        }
    }
    
    private void showInfo(String s){
        Log.d("PromptDialogFragment",s);   
    }
}

資訊儲存

如果使用者在輸入框中填入text,然後進行螢幕的橫屏和豎屏切換,這涉及到填入內容的儲存,可以通過onSaveInstanceState(),將之儲存到fragment的Bundle savedInstanceState中,並在onCreateView()中將之恢復。但是在Android 4.2版本的測試中,系統已經能夠自動儲存和恢復,無需加入程式碼。當然,安全地我們仍建議進行以下處理。

public class PromptDialogFragment extends DialogFragment implements OnClickListener{
    private EditText et = null;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ......    
        et = (EditText)v.findViewById(R.id.input_text);
        if(savedInstanceState != null){
            CharSequence text = savedInstanceState.getCharSequence("input");
            et.setText(text == null ? "" : text);
        }
        ......        
    }   
    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putCharSequence("input", et.getText());
        super.onSaveInstanceState(outState);
    }
}

重寫onCreateDialog()

對於簡單的對話方塊,可以通過AlterDialog.Builder直接建立對話方塊的UI,本例用於告警框,如下圖。AlertDialog.Builder在Android 3.0版本之前的建立對話方塊方式,在之後的版本中,可用在DialogFragment中,適用於建立簡單對話方塊。

程式碼如下。雖然都是OnClickListener介面,但提示框的是View.OnClickListener,這裡是DialogInterface.OnClickListener。

public class AlterDialogFragment extends DialogFragment implements DialogInterface.OnClickListener
    /*【步驟1】:通過newInstance()建立例項並返回* */
    public static AlterDialogFragment newInstance(String title,String message){
        … 略 … 
    }
 
        
    private String getTitle(){
        return getArguments().getString("alert-title");
    }
   
    private String getMessage(){
        return getArguments().getString("alert-message");
    }

   
    /* 【步驟2】建立view可以通過兩個途徑,一是fragment中的onCreateView(),二是DialogFragment中的onCreateDialog()。
     * 前者適合對自定義的layout進行設定,具有更大的靈活性
     * 而後者適合對簡單dialog進行處理,可以利用Dialog.Builder直接返回Dialog物件
     * 從生命週期的順序而言,先執行onCreateDialog(),後執行oonCreateView(),我們不應同時使用兩者。
     * */

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
        AlertDialog.Builder b = new AlertDialog.Builder(getActivity())
                                    .setTitle(getTitle())
                                    .setMessage(getMessage())
                                    .setPositiveButton("OK", this)
  //設定回撥函式
                                    .setNegativeButton("Cancel",this); //設定回撥函式
        return b.create();
   }
 

    @Override //按鍵觸發的回撥函式
    public void onClick(DialogInterface dialog, int which) { 
        boolean isCancel = false;
        if(which == AlertDialog.BUTTON_NEGATIVE){ //判斷使用者所按何鍵
            isCancel = true;
        } 
        MyActivity act = (MyActivity) getActivity();
        act.onDialogDone(getTag(), isCancel, "CLick OK, Alert dismissed");
    }

}