我仍然从实际工作中出发!最近需要在照相机里面添加声控拍照功能(语音拍照),在设置当中需要实现如下图的效果:
其设置的"语音拍照"菜单功能描述如下:
(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" 如下代码所示:
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. 这样就解决来疑问.
来看看下一个疑问:
* @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;
这就可以了.