Dialog 可不可以传Application

2,788 阅读2分钟

自定义Dialog

  1. 继承Dialog
class SourceDialog(context: Context, themeResId: Int) : Dialog(context, themeResId) {  
  
    constructor(context: Context) : this(context, R.style.CustomDialogTheme)  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.source_layout)   
    }  
}
  1. 创建自己的主题样式
<style name="CustomDialogTheme" parent="@android:style/Theme.Dialog">  
    <item name="android:windowBackground">@android:color/transparent</item>  //透明背景
    <item name="android:windowNoTitle">true</item>  //没有标题
    <item name="android:windowFullscreen">true</item>   //是否全屏
    <item name="android:backgroundDimEnabled">true</item>  //背景黑暗
    <item name="android:backgroundDimAmount">0.5</item>  //背景黑暗透明度
</style>

可以传入自己创建的主题,也可以不传,Android 会有默认的主题

Dialog(@UiContext @NonNull Context context, @StyleRes int themeResId,  
        boolean createContextThemeWrapper) {  
    if (createContextThemeWrapper) {  
        if (themeResId == Resources.ID_NULL) {  
            final TypedValue outValue = new TypedValue();  
            //这里会指定默认的主题,如果不传主题
            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);  
            themeResId = outValue.resourceId;  
        }  
        mContext = new ContextThemeWrapper(context, themeResId);  
    } else {  
        mContext = context;  
    }  
  
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
  
    final Window w = new PhoneWindow(mContext);  
    mWindow = w;  
    w.setCallback(this);  
    w.setOnWindowDismissedCallback(this);  
    w.setOnWindowSwipeDismissedCallback(() -> {  
        if (mCancelable) {  
            cancel();  
        }  
    });  
    w.setWindowManager(mWindowManager, null, null);  
    w.setGravity(Gravity.CENTER);  
    mListenersHandler = new ListenersHandler(this);  
}
  1. 使用
val dialog=SourceDialog(context) 
dialog.show()

遇到的问题

  1. Dialog 可不可以传Application ?

背景:这几天接到一个需求,收到动作需要在任何界面上弹出信号源选择器页面(铺满整个屏幕),我一开始是选择了Service+WindowManager 添加View显示的。之前也看了一下公司的CommonUI (展示一下亮度条,音量条之类的全局UI) 用到的是Dialog 弹出界面的。我也跟着写一个,才发现一个一个坑接着来。

答案是可以的,是要window 传一个 type

这是我的Dialog

class SourceDialog(context: Context, themeResId: Int) : Dialog(context, themeResId) {  
      
    constructor(context: Context) : this(context, R.style.CustomDialogTheme)  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.source_layout)  
    }  
}

//传入Application
val dialog=SourceDialog(MyApplication.CONTEXT)  
dialog.show()

我就很疑惑,提示Activity需要运行

屏幕截图 2024-05-24 151913.png

我后来换成了,正常运行

//传入activity
val dialog=SourceDialog(this@MainActivity)  
dialog.show()

很疑惑,对比同事负责的项目发现我需要给window设置了一些东西

//Dialog 

 override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.source_layout) 
        window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
    } 

//这样就可以正常展示
val dialog=SourceDialog(MyApplication.CONTEXT)  
dialog.show()
    
  1. 设置Dialog 全屏宽高不成功

我的布局文件 最外层是线性布局

<LinearLayout android:id="@+id/group_source"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:focusable="false"  
    android:gravity="center"  
    android:orientation="horizontal"  
    xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:app="http://schemas.android.com/apk/res-auto">  
    
</LinearLayout>

但是我发现 设置主题样式<item name="android:windowFullscreen">true</item> 不起作用。 我在这里参考了 三句代码创建全屏Dialog或者DialogFragment

  • 粗暴一点直接设置window 大小 ==需要在setContentView 之后设置window的大小才会生效,如果在setContentView 之前设置,此时window的dectorView为空不会更新布局
class SourceDialog(context: Context, themeResId: Int) : Dialog(context, themeResId) {  
  
    constructor(context: Context) : this(context, R.style.CustomDialogTheme)  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.source_layout)  
        window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)  
        window?.setLayout(WindowManager.LayoutParams.MATCH_PARENT,        WindowManager.LayoutParams.MATCH_PARENT)  //也可以换成具体数值宽高
    }  
}
  • 主题样式增加一样 <item name="android:windowIsFloating">false</item> 因为很多默认的Dialog 主题这个属性一般为true。
<style name="CustomDialogTheme" parent="@android:style/Theme.Dialog">  
    <item name="android:windowBackground">@android:color/transparent</item>  
    <item name="android:windowNoTitle">true</item>  
    <item name="android:windowFullscreen">true</item>  
    <item name="android:windowIsFloating">false</item>   
    <item name="android:backgroundDimEnabled">true</item>  
    <item name="android:backgroundDimAmount">0.5</item>  
</style>

Dialog setContentView 会走到PhoneWindow 的这个方法 走到installDecor -> generateLayout

屏幕截图 2024-05-24 160441.png

屏幕截图 2024-05-24 160620.png

屏幕截图 2024-05-24 160736.png 会发现这个属性为true的话,会根据内容展示。我们给这个属性设置为false这样就不用给Window设置大小了。