android之Dialog自定义引发的血案

我仍然从实际工作中出发!最近需要在照相机里面添加声控拍照功能(语音拍照),在设置当中需要实现如下图的效果:

其设置的"语音拍照"菜单功能描述如下:

(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是一个主题设置的参数, 这里需要设置背景透明等,如下代码所示:

    <style name="transparent_dialog_them" parent="@style/Theme.HWDroid.Ali.NoActionBar">
    	<item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowIsTranslucent">true</item>
        <!-- Note that we use the base animation style here (that is no
             animations) because we really have no idea how this kind of
             activity will be used. -->
        <item name="android:windowAnimationStyle">@android:style/Animation</item>
    </style>

这里我把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" 如下代码所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:paddingEnd="?android:attr/scrollbarSize"
    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:改为如下:

    <style name="transparent_dialog_them" parent="@style/Theme.HWDroid.Ali.Dialog.NoActionBar">
    	<item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowIsTranslucent">true</item>
        <!-- Note that we use the base animation style here (that is no
             animations) because we really have no idea how this kind of
             activity will be used. -->
        <item name="android:windowAnimationStyle">@android:style/Animation</item>
    </style>
原来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;

这就可以了.

时间: 2024-08-28 01:06:13

android之Dialog自定义引发的血案的相关文章

android 如何让自定义dialog的宽度充满整个屏幕?

============问题描述============ android 如何让自定义dialog的宽度跟屏幕的宽度一样.求大神们指教下.. ============解决方案1============ 在你dialog.show();后面加上 WindowManager windowManager = getWindowManager(); Display display = windowManager.getDefaultDisplay(); WindowManager.LayoutParam

Android开发之自定义Dialog二次打开报错问题解决

之前自定义了一个AlertDialog对话框,第一次点击时正常,但第二次调用时会出现错误:java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. 关于这个错误纠结了我好久,在网上百度了也不少,但感觉解决效果都达不到自己想要的效果.网上的解释说是一个子视图指定了多个父视图.由此可以推断出,在第二

浅谈android中的自定义封装易用的Dialog

好久没写android的博客,最近在做一个android的项目,里面用到我们经常用的一个控件就是对话框,大家都知道android自带的对话框是很丑的,android5.x之后除外.所以就出现了自定义view,自己定义美观的对话框.好我们就来自定义对话框. 整体思路:定义一个类然后去继承Dialog类,然后重写相应的构造器方法.大家都知道一般的对话框的创建过程都是来一个AlertDialog.Builder对象,然后使用一些set方法来设置标题内容以及设置一些自定义的view和点击的Button以

Android中制作自定义dialog对话框的实例

http://www.jb51.net/article/83319.htm 这篇文章主要介绍了Android中制作自定义dialog对话框的实例分享,安卓自带的Dialog显然不够用,因而我们要继承Dialog类来制作自己的对话框,需要的朋友可以参考下 自定义dialog基础版很多时候,我们在使用android sdk提供的alerdialog的时候,会因为你的系统的不同而产生不同的效果,就好比如你刷的是MIUI的系统,弹出框都会在顶部显示!这里简单的介绍自定义弹出框的应用. 首先创建布局文件d

一个由内存泄漏引发的血案-Square

一个内存泄漏引发的血案-Square 原文链接 : A small leak will sink a great ship 原文作者 : Pierre-Yves Ricau 译文出自 : 开发技术前线 www.devtf.cn.未经允许,不得转载! 译者 : chaossss 校对者: 这里校对者的github用户名 状态 : 完成 在开发 LeakCanary 时我发现一处奇怪的内存泄漏,为了搞清楚到底是什么原因导致这个问题我一边 Debug,一边在邮件中和小伙伴们沟通,最后形成了这篇博文.

(Android review)dialog的使用

一.基本知识点 常见的dialog 基本代码:AlertDialog.Builder builder = new AlertDialog.Builder(this);AlertDialog dialog = builder.create();dialog.show(); 1)常见对话框builder.setMessage("浏览传智播客的网站");builder.setPositiveButton 2)选择对话框builder.setItems(items, new DialogInt

探讨:你真的会用Android的Dialog吗?

一个Bug前几日出现这样一个Bug是一个RuntimeException,详细信息是这样子的: 复制代码代码如下: java.lang.IllegalArgumentException: View not attached to window manager    at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:356)    at android.view.WindowManagerImpl.rem

Android中Dialog

在Android中,Dialog是一个非常重要的UI, 它可以方便的给用户提示,用最简洁的方式向用户展示信息, 以下的图片是Dialog的一个整体架构,通过它,可以总体对Dialog有一个很清晰的认识. 从这张图中可以看到,Dialog为父类, 其下有最重要的, 我们最常用的AlertDilog, 而AlertDialog的子类,则是由DatPicker, ProgressDialog,TimePick来组成. 这几个子类都是我们在程序开发中最常用的,因此要重点理解, 我们可以试着想像一下,如下

Android之Dialog详解

Android中的对话框形式大致可分为五种:分别是一般对话框形式,列表对话框形式,单选按钮对话框,多选按钮对话框,自定义对话框. 在实际开发中,用系统的对话框会很少,因为太丑了,美工不愿意,多是使用自定义对话框.当然学会系统的,自定义就简单了,所以我们先来学习系统的,后面在写一篇自定义对话框. 一般对话框: 不多说先上图: 代码: private void dialog1(){ AlertDialog.Builder builder=new AlertDialog.Builder(this);