高仿iOS的BlurDialog

文/BlackSwift(简书作者)
原文链接:http://www.jianshu.com/p/1e2d68183c3c
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

本文主要讨论Dialog的blur背景与泛谈window中的各种view。


Github上有许多仿iOS的dialog,但是都没有让背景模糊,第三方的view太坑,不如自己对着iOS模拟器的图与动画做出来。耗费了很久时间终于搞定了,如下图,左边是仿iOS的dialog,右边是SupportV7的dialog。


BlurDialog

Dialog 生命周期的简要分析

在构造函数时,创建了一个window,并设置了相关window属性。

当调用 show()时,首先调用了 onCreateonStart(都是一些无关紧要的方法),最后通过 WindowManager 添加了 window 中的 DecoView,从这里开始,view就要开始走绘制流程了。

当调用 dismiss() 时,通过 WindowManager 移除了 View,并调用
onStop() 执行可能的清理任务。

@see #Dialog (setUp window inside)

@see #show()

- @see #onCreate(Bundle) (call setContentView here)

- @see #onStart()

- mWindowManager.addView(mDecor, l);

@see #dismiss()

- mWindowManager.removeViewImmediate(mDecor);

- @see #onStop() (clean work)

如果我们需要自定义一个dialog,只需要在 show() 之前配置好 window 的属性,以及在 onCreate() 中将自定义的 xml 布局初始化即可。

mWindowManager.addview() 之前,除非指定了 view 的dp高宽,否则输出的view高度等数据将是0,-1之类的值。为了获得准确的值,可以使用 view.post() 将消息入队,这样可以获取到正确的数据。

BlurAlertDialog 的实现

通过post入队即可,入队主要是为了准确测量偏移量。

//初始化 BlurDrawable 的样式
//`mRvFragCard`表示在底层的将要被模糊的View
BlurDrawable drawable = new BlurDrawable(mRvFragCard);

dialog.getWindow().getDecorView().post(new Runnable() {
  @Override public void run() {
      //设置边缘圆弧的 dp
    drawable.setCornerRadius(xxDp);
    // 设置绘制偏移量
    // 坐标为相对整个屏幕,dialog最左边,最上面的点
    drawable.setDrawOffset(x,y);
    dialog.getWindow().setBackgroundDrawable(drawable);
  }
});
dialog.show();

模糊效果可以参考我之前写的 BlurDrawable

注意不能用系统自带的dialog,因为它的内部是不开放的(也就是红色的代码),window创建流程也不透明,可能会抛出异常。

示例地址

本文项目

本部分完,需要ui的直接clone即可。


以下为冗长的理论资料

Window 与 WindowManager

  • window是对整个view的一套管理,对开发者来说仅仅是接口。PhoneWindow 是对 window 的抽象方法的实现,手机ROM厂商可以修改或者实现它,而开发者只能反射调用PhoneWindow的一些功能。

  • WindowManager是系统服务,负责view绘制。它对开发中看来也只是一个接口,通过 getSystemService 即可获得。

接口由ROM实现

/system/framework/framework.jar

反编译jar后,可以获得对象 android.view.WindowManagerGlobal,具体流程可以看这里,我们只需要知道这个函数涉及到AIDL通信,并且它是异步回调的即可。

在国产ROM常见的状态栏字体变色,悬浮Activity等特色功能,都是由 WindowManager 负责绘制的,通过在 Window 中写入某些flag,之后重绘的时候就能显示出独有的样式。

Dialog 下的 Window 布局

此场景适用于最原始的 Activity 与 Dialog ,它们的 ViewTree 布局如下

DecoView 

DecorContentParent

ContentView(android.R.id.content)

你写的xml布局

通过 Dump 工具可以验证:


Dump

1. DecoView

PhoneWindow 中,它的变量名为 mDecor , 它是 window 中的 top-level 的view,通过 installDecor() 进行构造,它默认的大小是 wrap_content,我们通常所说的 window.setXXX() 本质上调用的就是 DecoView(与它的子view)

网上有部分文章说 DecoView 不是top-level 的 View,这个说法是错的,各位可以手动设置 id 测试

2. DecorContentParent

PhoneWindow 中,它的变量名为 mDecorContentParent,通过它可以设置 Window 中的Title,requestWindowFeature 等功能,它是一个宽泛的层,层次可能是一层(在NoActionBar,notitle的条件下),可能有多个层,上图就有3层。在实际开发中,我们一般也用不到(如果非要用的话,还是只能反射调用)。

举个例子,在最新的 AppCompatActivity 中,它重写的setContentView 中通过 ensureSubDecor() 方法手动填充几个夹层View,这个算一个应用案例

@Override
public void setContentView(int resId) {
    //对mWindow.setContentView()的一道包装
    //添加部分View, 比如Actionbar/Title/Toolbar
    ensureSubDecor();
    .....
}

3. ContentView

PhoneWindow 中,它的变量名为 mContentParent。我们日常接触的就是 ContentView 了,它包装着我们写的xml布局

contentView可以通过ID获得,这个id是内置的,如下两个是等价的

android.R.id.content
Window.ID_ANDROID_CONTENT

4. xml布局

我们自己写的xml布局,需要注意 inflate 方法中的boolean值,它控制当前view是否依附到 parent 中。

如果view本身需要被add时(比如dialog,activity),调用setContentView()即可,它将生成view并依附到parent中。

如果这个view并没有parent(比如viewholder), 选择不依附即可

inflater.inflate(ResId, null, false);

总结

  1. 尽量在setcontentView之前调用window属性
  2. 涉及到长宽等内容时,因为view的显示是异步的,所以需要post发送,以免获得到的结果为0
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以在dialog的后面造成模糊效果,模糊的实现是基于FastBlur算法实现的。项目地址:https://github.com/tvbarthel/BlurDialogFragment 效果图:使用说明:最简单的使用方式是继承BlurDialogFragment或者SupportBlurDialogFragment。如果你用的是android.app.DialogFragment,那么继承BlurDialogFragment/**  * Simple fragment with blurring effect behind.  */ public class SampleDialogFragment extends BlurDialogFragment {       @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         this.debug(true);         this.setBlurRadius(4);         this.setDownScaleFactor(5.0f);           ...     }       ... }如果是android.support.v4.app.DialogFragment 则继承 SupportBlurDialogFragment/**  * Simple fragment with blurring effect behind.  */ public class SampleDialogFragment extends SupportBlurDialogFragment {       @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         this.debug(true);         this.setBlurRadius(4);         this.setDownScaleFactor(5.0f);           ...     }       ... }如果你不想使用继承的方式可以直接使用BlurEngine自定义一个DialogFragment/**  * Your blur fragment directly using BlurEngine.  */ public class SampleDialogFragment extends MyCustomDialogFragment {        /**      * Engine used to blur.      */     private BlurDialogEngine mBlurEngine;       @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);           mBlurEngine = new BlurDialogEngine(getActivity());         mBlurEngine.debug(mDebugEnable);         mBlurEngine.setBlurRadius(8);         mBlurEngine.setDownScaleFactor(8f);     }       @Override     public void onResume() {         super.onResume();         mBlurEngine.onResume(getRetainInstance());     }        @Override     public void onDismiss(DialogInterface dialog) {         super.onDismiss(dialog);         mBlurEngine.onDismiss();     }       @Override     public void onDestroy() {         super.onDestroy();         mBlurEngine.onDestroy();     }       @Override     public void onDestroyView() {         if (getDialog() != null) {             getDialog().setDismissMessage(null);         }         super.onDestroyView();     }       ... }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值