Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android之Dialog自定義引發的血案

android之Dialog自定義引發的血案

編輯:關於Android編程

我仍然從實際工作中出發!最近需要在照相機裡面添加聲控拍照功能(語音拍照),在設置當中需要實現如下圖的效果:

\

其設置的"語音拍照"菜單功能描述如下:

(1)當點擊""語音拍照"菜單時候就會彈出如上圖所示的Dialog, 點擊Dialog裡面的"拍照"/"茄子"就會自動播放聲音.

(2)Dialog出現時候,只要點擊Dialog以外的區域,Dialog就會自動消失.

(3)當點擊"語音拍照"菜單最右邊的綠色switch按鈕時候,就會打開/關閉語音拍照功能.

上面效果圖的實現其實是一個PreferenceActivity, 所以我們新加的"語音拍照"菜單就是在這個PreferenceActivity(CameraSettingActivity)裡面添加.

首先,我們要解決的是如何創建一個自己定義的Dialog: 在Activity裡面有一個方法public Dialog onCreateDialog(int dialogId),用他就可以創建屬於自己的Dialog,然後調用Activity的public final void showDialog(int id)就可以顯示我們創建的Dialog. 這裡Activity是根據不同的dialogId來創建和顯示不同的Dialog,而 dialogId就是你自己定義的!在我們的CameraSettingActivity如下定義

private static final int DIALOG_ID_VOICE_COMMAND_SHOW_TONES = 111;
然後Override父activity的onCreateDialog方法來定義自己的Dialog,如下代碼:

    @Override
    public Dialog onCreateDialog(int dialogId) {
    	if(dialogId == DIALOG_ID_VOICE_COMMAND_SHOW_TONES){
    		Dialog dialog = new Dialog(this, R.style.transparent_dialog_them); 
			dialog.setContentView(R.layout.setting_switch_sublist_layout);
			VoiceManager voice_manager = ((CameraApp)CameraSettingActivity.this.getApplication()).getVoiceManager();
			SettingSwitchSublistLayout mVoiceSettingLayout =(SettingSwitchSublistLayout) dialog.findViewById(R.id.SettingSwitchSublistLayout_ID);
			mVoiceSettingLayout.initialize(voice_manager.getVoiceEntryValues());
			mVoiceSettingLayout.setSettingChangedListener(this);
			//dialog.getWindow().setCloseOnTouchOutside(true);
			View content =(View) dialog.getWindow().getDecorView().findViewById(com.android.internal.R.id.content);
			content.setOnClickListener(new OnClickListener() {
            	@Override
            	public void onClick(View v) {
					dismissDialog(DIALOG_ID_VOICE_COMMAND_SHOW_TONES);
            	}
        		});
			return dialog;

		}

		return super.onCreateDialog(dialogId);
    }
這裡一步步解釋一下上面的代碼:

(1)

Dialog dialog = new Dialog(this, R.style.transparent_dialog_them); 
第二這個參數:R.style.transparent_dialog_them是一個主題設置的參數, 這裡需要設置背景透明等,如下代碼所示:

    
這裡我把windowBackground設置為透明,並且還設置為NoActionBar和NoTitle的模式! 當然這裡windowBackground實際上可以設置成辦透明的背景!從這裡你是否看出什麼奇怪的呢? 為什麼創建一個Dialog又和window/action等有什麼關系呢? 如果你看了dialog實現的class實現類,你就會發現,其實創建一個dialog就等於創建一個window,而我們知道一個window就有action,title等屬性! 通過學習activity我們也知道創建一個activity其實也就創建了一個window, 實際上一個界面的顯示都是起源於一個window的! 一個window除了管理界面的顯示,其實所有設備輸入事件都是從這裡出發的!

(2)

dialog.setContentView(R.layout.setting_switch_sublist_layout);
這一行代碼其實就是我們這個顯示的dialog布局的配置! 裡面的詳細就很簡單,不以多說!

(3)

	View content =(View) dialog.getWindow().getDecorView().findViewById(com.android.internal.R.id.content);
		content.setOnClickListener(new OnClickListener() {
            	@Override
            	public void onClick(View v) {
			dismissDialog(DIALOG_ID_VOICE_COMMAND_SHOW_TONES);
            	}
        });
這幾行代碼, 就可以實現,當用戶點擊dialog 以外的區域時候,dialog自動消失!

上面是關於dialog的創建! 你覺得有問題嗎?

然後,我們來看看如何實現點擊:當點擊""語音拍照"菜單時候就會彈出如上圖所示的Dialog,而當點擊"語音拍照"菜單最右邊的綠色switch按鈕時候,就會打開/關閉語音拍照功能.新看看如下代碼:

        SwitchPreference voicePref =(SwitchPreference) findPreference(getString(R.string.camera_setting_item_pref_voice_key));
	voicePref.setLayoutResource(R.layout.xunhu_voice_preference);//xunhu_ali_preference
	String key = getString(R.string.camera_setting_item_pref_voice_key);
	boolean value = getPreferenceManager().getSharedPreferences().getBoolean(key, false);
	//Log.d("pre_carmera", "CameraSettingActivity : initDefaultSavePath mValue="+value);
	VoiceManager voice_manager = ((CameraApp)this.getApplication()).getVoiceManager();
	String mValue = voice_manager.getVoiceValue();
從上面代碼可以清楚知道,實際上我采用的就是平常我們使用的SwitchPreference,只是自己去定義了他的布局吧了!SwitchPreference的方法setLayoutResource就可以配置自己的布局! 所以解決這個問題的重點就在這個布局的使用上面!

其實這個布局跟默認的 SwitchPreference的布局沒有什麼區別!只是在這個布局的父view上面加了一個屬性android:onClick="VoiceCommandClickListener" 如下代碼所示:


最後看看VoiceCommandClickListener的定義:

	public void VoiceCommandClickListener(View v) {   
    	 showDialog(DIALOG_ID_VOICE_COMMAND_SHOW_TONES);
	}
其實就是調用showDialog來顯示我自己的Dialog.


到了這裡後,會很快發現有兩個疑問:(1)我們實現的當點擊dialog 以外的區域時候,dialog自動消失,是否有簡單的辦法!(2)可以用DialogFragment來替換我們這裡的Dialog嗎?

先解決地一個疑問:

通過查看dialog.java可以發現,其實一個dialog的創建其實就會為這個dialog創建window,通過工具hierarchyviewer來查看時候,你會發現這個dialog其實占領了整個屏幕,而不是僅僅只是只有顯示區域的那麼多! 為什麼這樣呢! 這是你會很快發現,當要是一個activity成dialog模式的時候,你就需要給這個activity的主題配置為dialog模式:Theme.Dialog. 剛才說了,一個Dialog和activity的顯示都是起源與一個window,那麼Dialog創建時候是否制定了主題為Theme.Dialog就可以解決問題了呢! 實際結果的確和我推斷一樣!

把上面的代碼修改如下:

    @Override
    public Dialog onCreateDialog(int dialogId) {
    	if(dialogId == DIALOG_ID_VOICE_COMMAND_SHOW_TONES){
    		Dialog dialog = new Dialog(this, R.style.transparent_dialog_them); 
			dialog.setContentView(R.layout.setting_switch_sublist_layout);
			VoiceManager voice_manager = ((CameraApp)CameraSettingActivity.this.getApplication()).getVoiceManager();
			SettingSwitchSublistLayout mVoiceSettingLayout =(SettingSwitchSublistLayout) dialog.findViewById(R.id.SettingSwitchSublistLayout_ID);
			mVoiceSettingLayout.initialize(voice_manager.getVoiceEntryValues());
			mVoiceSettingLayout.setSettingChangedListener(this);
			dialog.getWindow().setCloseOnTouchOutside(true);
			/*
			View content =(View) dialog.getWindow().getDecorView().findViewById(com.android.internal.R.id.content);
			content.setOnClickListener(new OnClickListener() {
            	           @Override
            	           public void onClick(View v) {
					dismissDialog(DIALOG_ID_VOICE_COMMAND_SHOW_TONES);
            	           }
        		});
        	       */	
			dialog.setCanceledOnTouchOutside(true);
			return dialog;

		}

		return super.onCreateDialog(dialogId);
    }
上面的代碼我注釋了content.setOnClickListener這段的內容,新加了下面這行代碼:
dialog.getWindow().setCloseOnTouchOutside(true);
你會發現,當點擊dialog以外的區域的時候,dialog根本無法消失!難道是上面這行代碼沒有作用! 其實不然, 剛才我們說了,這個dialog其實是占據了整個屏幕的!只是我們把其背景設置為全透明的了! 所以此時你根本無法點擊到dialog區域 以外的區域! 要證明上面這行代碼是可用的! 你只需要修改一下主題的設置!把上面的R.style.transparent_dialog_them配置為:Theme.Dialog:改為如下:

    
原來transparent_dialog_them的父是:Theme.HWDroid.Ali.NoActionBar,現在改成:Theme.HWDroid.Ali.Dialog.NoActionBar. 這樣就解決來疑問.
來看看下一個疑問:
先打開Activity.java來看看與Dialog有關的源碼, 其中有關於Dialog的說明如下:

     * @deprecated Use the new {@link DialogFragment} class with
     * {@link FragmentManager} instead; this is also
     * available on older platforms through the Android compatibility package.
上面說的很明白, 現在已經不推薦使用Dialog, 而是推薦大家使用DialogFragment. 看一看DialogFragment源碼就知道實際上DialogFragment就是一個Fragment, 也就是說建議搭建用Fragment來解決這個問題!

所以大家會Fragment的,也就會了DialogFragment,其實DialogFragment就是封裝了Dialog的Fragment. 下面直接上代碼,看看采用DialogFragment如何實現:

	public void VoiceCommandClickListener(View v) {   
    	 //showDialog(DIALOG_ID_VOICE_COMMAND_SHOW_TONES);
		 FragmentTransaction ft = getFragmentManager().beginTransaction();
		 VoiceCommandDialogFragment prev =(VoiceCommandDialogFragment) getFragmentManager().findFragmentByTag("voice_dialog");
		 if (prev != null) {
			 ft.show(prev);
		 }else{
			prev = VoiceCommandDialogFragment.newInstance();	
			prev.show(ft, "voice_dialog");
		 }
	}
上面代碼時顯示DialogFragment時候調用.下面來看看自定義的DialogFragment:VoiceCommandDialogFragment

    private static final class VoiceCommandDialogFragment extends DialogFragment {
		//private static VoiceCommandDialogFragment f;
		//private static final Object mLock = new Object();

		private static class SingletonHolder {
			private static VoiceCommandDialogFragment f = new VoiceCommandDialogFragment();
		}
		/*
               static VoiceCommandDialogFragment newInstance() {
        	    if(null == f){
				synchronized (mLock){
					if(null == f){
						f = new VoiceCommandDialogFragment();
					}
				}
        	     }
                    return f;
               }
             */
        public static VoiceCommandDialogFragment newInstance() {
	     /*
        	if(null == f){
				f = new VoiceCommandDialogFragment();
        	}
            return f;
            */
            return SingletonHolder.f;
        }
        
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Pick a style based on the num.
            int style = DialogFragment.STYLE_NO_TITLE, theme = R.style.transparent_dialog_them;

            setStyle(style, theme);
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.setting_switch_sublist_layout, container, false);
			VoiceManager voice_manager = ((CameraApp)getActivity().getApplication()).getVoiceManager();
			SettingSwitchSublistLayout mVoiceSettingLayout =(SettingSwitchSublistLayout) v.findViewById(R.id.SettingSwitchSublistLayout_ID);
			mVoiceSettingLayout.initialize(voice_manager.getVoiceEntryValues());
			mVoiceSettingLayout.setSettingChangedListener((CameraSettingActivity)getActivity());
            return v;
        }

    }
上面的代碼需要注意以下三點:

(1)需要重復創建DialogFragment問題, 這裡使用單例模式.這裡我采用內部類來解決;

(2)顯示的時候,需要檢測當前FragmentManager裡面是否有存在的我需要顯示的DialogFragment;

(3)跟上面一樣,器主題設置一定要設置為dialog_them;

這就可以了.

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved