編輯:關於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:VoiceCommandDialogFragmentprivate 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;
這就可以了.
Android 5.0 是 Google 於 2014 年 10 月 15 日(美國太平洋時間)發布的全新 Android 操作系統,英文名為Lollipop,翻譯過來就
Android提供了實現按照秒計時的API,今天就是用這個API實現簡單的倒計時。來個布局: 對應活動中的代碼如下: pa
又兩周沒寫博客了,不是不想寫而是不知道該寫點什麼,總不能為了寫博客而寫博客,前兩天項目裡要加個購物車功能,看了下別人APP的效果覺得不錯,雖然我項目裡沒用上不過畢竟還算
其實適配器模式在Android源碼中非常多,而從整體的源碼角度上來看Activity的結構就是一種適配器模式。從這個角度上面看Activity,對Activity和應用層