浅析Android Dialog中setContentView()方法
2017-05-15
概述
Dialog在Android中是一个很优秀的工具。在使用Dialog时,我们一般都会自定义要显示的内容布局。Dialog自带了三个方法来支持自定义内容布局。
public void setContentView (int layoutResID); public void setContentView (View view); public void setContentView (View view, ViewGroup.LayoutParams params);
这三个方法内部的实现原理都是一样的,只是其封装深度不同而已。三个方法可以说分别照顾了不同定制深度的开发者。
setContentView()流程
直接查看Dialog的源代码,如下图1所示。

【图1】
上图中mWindow在Dialog类中的定义如下:
import android.view.Window; Window mWindow;
那么,它从何而来呢?如下图2所示。

【图2】
由上图2可知,在构造Dialog对象时,这个mWindow的值也被确定。它由PolicyManager提供。再往下跟系统代码。
makeNewWindow(Context)方法的实现如下:
// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
还得继续往下跟。
import com.andorid.policy.internal.policy.impl.Policy;
/**
* {@hide}
*/
public class Policy implements IPolicy {
//... public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
}
到这,貌似就差不多看到尽头了,原来我们调用的setContentView就是在这个PhoneWindow类中被实现的。继续跟进。
import com.android.internal.policy.impl.PhoneWindow;
/**
* Android-specific Window.
* <p>
* todo: need to pull the generic functionality out into a base class
* in android.widget.
*/
public class PhoneWindow extends Window implements MenuBuilder.Callback {
//...
}
setContentView(int)
这个方法的代码实现如下图3所示。

【图3】
整个的实现流程乍一看还算简单明了。我们传入的布局参数最后就是被加载到上图所示的那个 mContentParent 中的。这个mContentParent是一个ViewGroup类对象
。在上图所示的代码中第367行作了空判断,可见这个对象的实例的创建与installDecor()方法有关系。这个方法的实现较为复杂,这里我们只看mContentParent的实例化过程。

【图4】
这个generateLayout()方法的实现过程很繁杂。我们没有必要去把每一行的代码都看懂。只需要知道在它内部是这样创建mContentParent对象的就好了
,
。最后会把这个contentParent作为结果返回即可。然后再回到图3,在图中所示代码处第378行完成了将我们传入的布局加载进系统容器中的操作。
setContentView(View)与setContentView(View, ViewGroup.LayoutParams)
这种方式设置内容布局比较灵活。一般用于布局中有需要在Java代码中做特殊操作的布局。如设置监听等。其具体实现代码如下图5所示。

【图5】
这个代码,并没什么特别的,它的目的都已经明明白白的表现在代码上了,就不再赘述了。
setContentView(View)无法设置布局尺寸的问题
使用setContentView(int)的方式时,可以直接通过在layout的根容器中指定宽、高来设置布局的尺寸。这里得注意,我指的是在根视图中直接指定宽度多少像素,高度多少像素这种直白的写法才可以控制布局的尺寸。若直接设为MATCH_PARENT,那么它的效果等同于WRAP_CONTENT。为什么会是这样的结果呢?本人并没有深究它的原因,但本人猜测(且后续并未去证实)这与mContentParent加载了布局后重新确定整个视图的尺寸的过程脱不了干系。我们来翻翻ViewGroup的代码,在ViewGroup中,有如下图6所示的一段代码。

【图6】
对于一个ViewGroup及其子类来说,它的MeasureSpec要么是EXACTLY要么是AT_MOST,我记不清这里头的具体关系了。但上图6所示的代码也已经非常直白了。对于MATCH_PARENT,它确实是按照WRAP_CONTENT的方式来处理的。这也就解释了上面所说的“令人费解”的情况了。虽然这个只是本人的猜测,但我估计也是八九不离十了。
而使用setContentView(View)的方式时,无论layout中根容器的宽高是什么,都按照WRAP_CONTENT的方式来走。这是为什么?我们先回去看看图5所示代码中这个方法的实现。可以发现,PhoneWindow给了一个默认的ViewGroup.LayoutParams对象。并且宽、高的值不偏不倚,正好是MATCH_PARENT。因此,当使用这种方式时,无论layout中根容器的宽高如何设置,它都表现成按内容的尺寸来适配布局的效果。因此,想要能控制对话框布局的尺寸,还是老老实实自己建一个有指定宽高值的LayoutParams对象给PhoneWindow对象吧,不要指望人家帮你擦屁股,擦不干净的~~
那么,到了这里,我们再来简单探究一下,setContentView(int)又是如何做到可以直接设置尺寸的。我们回去图3,看看这个方法的实现代码中,第378行。它在映射xml时,第二个参数传的直接是mContentParent。比较一下我们平时使用映射布局函数时,讲道理,直接传一个null的比较多吧,或者说,或许我们平时都很少注意到这个参数。我们去LayoutInflater中转转。
import android.view.LayoutInflater;
inflate()方法的代码还是挺长的,这里就不详细贴了,我们只挑有代表性的来看。
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) // ... final AttributeSet attrs = Xml.asAttributeSet(parser); // ... // Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs); // ... // Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, attrs, false); // ... if (root != null && attachToRoot) {
root.addView(temp, params);
} // ...
够清晰了吧。在这里,LayoutInflater在映射xml布局时主动去解析了所有的属性。当然会包括外层容器的属性。然后根据解析的结果生成一个LayoutParams对象,最后,再将要内容布局联合这个即时创建的LayoutParams对象一同添加到mContentParent容器中去,其实就相当于调用setContentView(View, LayoutParams)方法。所以在文章开头我才说到Dialog的三个设置内容布局的方法本质是一样的,只是其封装深度不同而已。
设置Dialog的背景为完全透明
Dialog默认有一个灰色的背景,首先这个背景巨丑,其次背景的存在还影响我们对对话框UI的定制。

在Activity中,可以通过在创建Dialog时传入一个无背景对话框的风格样式给构造器,以构造出无灰色背景的对话框出来。也可以通过Java代码控制对话框的背景色为透明色。还可以先show()对话框,然后再给它setContentView()来达到无背景色的对话框的目的。
1、 通过风格样式
<style name="transBg" parent="@android:Theme.Dialog">
<item name="android:windowBackground">@android:color/transparent"</item>
</style>
AlertDialog dialog = new AlertDialog.Builder(mContext, R.style.transBg).create();
//或
Dialog dlg = new Dialog(mContext, R.style.transBg);
//等
2、通过Java代码控制
所谓背景,其实就是PhoneWindow的背景。我们只需要设置PhoneWindow的背景为透明,就能达到我们想要的结果了。
// ...
AlertDialog dialog = builder.create();
dialog.show();
dialog.setContentView(view);
//方式1,使用透明的ColorDrawable对象。
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(0));
//方式2,使用一张透明的Drawable图片。
dialog.getWindow().setBackgroundDrawableResource(R.drawable.transparent);
3、先显示对话框再设置布局
这种方式只在Activity中有效果。
AlertDialog dialog = builder.create();
dialog.show();
dialog.setContentView(view);
至于Service中为什么没有效果,本人怀疑是由于在Service中要想弹出对话框,只能将它设为系统级对话框,需要加多的一段代码导致的。但其具体原理还没有去研究过。
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
在Service中。只能通过上述第1、第2种方式来实现背景透明的目的。
此至,祝顺!
浅析Android Dialog中setContentView()方法的更多相关文章
- 浅析android系统设计中的回调思想
一.为何写作本文 在慢慢深入接触android开发的过程中,我越来越发现android中(至少应用曾的开发)用到了很多回调的思想.比如activity的生命周期,fragment的生命周期,皆是回调 ...
- 浅析Android设备中grep命令处理流程
2017-04-18 概述 在TV开发板中,可以在串口中直接使用grep命令.这是因为在/system/bin/下有一个'grep'链接.这个链接指向'/system/bin/toolbo ...
- Android webView 中loadData方法加载 带中文时出现乱码
WebView出现乱码用LoadData方法来解析html的,但是据说这是官方的一个BUG,不能用来解析中文. 采用loadDataWithBaseURL的方法,其中codeingType设置为utf ...
- android编程中setLayoutParams方法设置
第一篇 private LinearLayout generateHeadOfControl() { LinearLayout LayoutHead = createLayout(LinearLayo ...
- Android WebView中的JavaScript代码使用
在WebView中使用JavaScript 如果你想要载入的页面中用了JavaScript,你必须为你的WebView使能JavaScript. 一旦使能之后,你也可以自己创建接口在你的应用和Java ...
- Android Dialog 创建上下文菜单
Android Dialog中的listview创建上下文菜单 listView.setOnCreateContextMenuListener(new OnCreateContextMenuListe ...
- Spinner在Dialog中的使用效果
版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/91 背景: 记得很久以前,碰到一个需求场景,需要在Andr ...
- Android开发中,那些让您觉得相见恨晚的方法、类或接口
Android开发中,那些让你觉得相见恨晚的方法.类或接口本篇文章内容提取自知乎Android开发中,有哪些让你觉得相见恨晚的方法.类或接口?,其实有一部是JAVA的,但是在android开发中也算常 ...
- Android开发中,那些让你觉得相见恨晚的方法、类或接口
Throwable类中的getStackTrace()方法,根据这个方法可以得到函数的逐层调用地址,其返回值为StackTraceElement[],而在StackTraceElement类中有四个方 ...
随机推荐
- Java性能优化之编程技巧总结
程序的性能受代码质量的直接影响.在本文中,主要介绍一些代码编写的小技巧和惯例,这些技巧有助于在代码级别上提升系统性能. 1.慎用异常 在Java软件开发中,经常使用 try-catch 进行错误捕获, ...
- A2D Framework - 看如何精简业务逻辑 - 缓存子系统
A2D中一项功能是关于Cache的,能够将判断.获取.删除cache的代码缩减到最少量,如下是Order业务逻辑的demo示范: interface IOrder { [Cachable()] str ...
- mybatis源码- 反射模块一(跟着MyBatis学反射):类级别信息的封装
目录 1 JavaBean 规范 2 Reflector和ReflectorFactory 2.1 Reflector 属性 2.1.1 属性 2.1.2 Invoker 接口 2.2 Reflect ...
- SQLite 实现删除表中前一天的数据
注意点1 要注意SQLite datatime()函数为何获取不到系统本地时间?这个问题,坑死我了. 解决方法详见这篇文章:SQLite datatime()函数为何获取不到系统本地时间? 注意点2: ...
- COMCMS_CORE 起步篇,如何运行和部署
前言:关于最近开源后,不少朋友问,怎么我下载下来,运行不了.或者怎么没有左边菜单.货不对板?还是我吃了数据? 感言:开源不容易,更不容易的是,明明毫无保留,还这么大误会,真是泪奔..... 好了.步入 ...
- myeclipse使用hibernate5框架load延迟装载对象报错_$$_javassist_0 cannot be cast to javassist.util.proxy.Proxy
jar包问题,将hibernate-core-5.0.12.Final.jar删除,换为hibernate-core-4.2.3.final.jar搞定.注意项目运行过后可能删不掉jar包,只需关闭m ...
- iOS基于B站的IJKPlayer框架的流媒体探究
阅读数:6555 学习交流及技术讨论可新浪微博关注:极客James 一.流媒体 流媒体技术从传输形式上可以分为:渐进式下载和实施流媒体. 1.渐进式下载 它是介于实时播放和本地播放之间的一种播放方式, ...
- 运行Maven项目时出现invalid LOC header (bad signature)错误,Tomcat不能正常启动
作为Maven小白,今天这问题困扰了我好久,经过多次在网上查询,终于找到了原因.明明一个小问题却耗费很多时间,着实不应该,所以必须记录一下. 报错信息如下: 对话框: 控制台: <span st ...
- Python_架构、同一台电脑上两个py文件通信、两台电脑如何通信、几十台电脑如何通信、更多电脑之间的通信、库、端口号
1.架构 C/S架构(鼻祖) C:client 客户端 S:server 服务器 早期使用的一种架构,目前的各种app使用的就是这种架构,它的表现形式就是拥有专门的app. B/S架构(隶属于C/ ...
- 软件工程(FZU2015) 助教总结
SE_FZU目录:1 2 3 4 5 6 7 8 9 10 11 12 13 本次构建之法-SE助教工作,和福州大学张老师协作,福大学生基本发挥出了一定水平,在此做个小结. 教师 张老师本身的SE教学 ...