安卓(kotlin)自定义弹出框

在安卓开发中,我们经常会遇到这种情况,就是可爱的UI们设计了一套属于我们自己风格的弹出框,为了彰显我们自己的风格,使用自动的dialog当然满足不了我们的需求,所以还是得这基础上写出我们自己的提示框,以后UI再变化,我们也只需要修改样式就好了。

1.首先我们先写好dialog的样式

custom_dialog.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:paddingRight="24dp"
android:paddingLeft="24dp"
android:paddingTop="21dp"
android:paddingBottom="21dp"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="#deffffff"
android:letterSpacing="0.03"
android:lineSpacingExtra="8sp"
android:text="提示"
android:layout_marginTop="6dp"
android:layout_marginBottom="15dp"
android:visibility="gone"/>
<TextView
android:id="@+id/message"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#99ffffff"
android:letterSpacing="0.02"
android:lineSpacingExtra="6sp"
android:text="这是自定义弹出框"
/>
<RelativeLayout
android:id="@+id/twoButtonLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="31dp">
<TextView
android:id="@+id/negativeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#138ef0"
android:letterSpacing="0.09"
android:lineSpacingExtra="2sp"
android:text="取消"
android:layout_alignParentRight="true"
android:layout_marginRight="89dp"
android:padding="10dp"
android:focusableInTouchMode="false"
/>
<TextView
android:id="@+id/positiveBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#138ef0"
android:letterSpacing="0.09"
android:lineSpacingExtra="2sp"
android:text="满意"
android:layout_alignParentRight="true"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="5dp"
/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/singleButtonLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="41dp">
<TextView
android:id="@+id/singleBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#138ef0"
android:letterSpacing="0.09"
android:lineSpacingExtra="2sp"
android:text="确定"
android:layout_alignParentRight="true"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="5dp"
/>
</RelativeLayout>
</LinearLayout>

2.然后定义一个CustomDialog继承Dialog,在这里定义我们自己要调用的方法等。

CustomDialog.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package com.xindong.rocket

import android.app.Dialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView

class CustomDialog : Dialog {

constructor(context: Context) : super(context) {}
constructor(context: Context, theme: Int) : super(context, theme) {}

class Builder(context: Context) {
private var title: String? = null
private var message: String? = null
private var contentView: View? = null
private var positiveButtonText: String? = null
private var negativeButtonText: String? = null
private var singleButtonText: String? = null
private var positiveButtonClickListener: View.OnClickListener? = null
private var negativeButtonClickListener: View.OnClickListener? = null
private var singleButtonClickListener: View.OnClickListener? = null

private val layout: View
private val dialog: CustomDialog = CustomDialog(context, R.style.CustomDialog)

init {
//这里传入自定义的style,直接影响此Dialog的显示效果。style具体实现见style.xml
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
layout = inflater.inflate(R.layout.custom_dialog, null)
dialog.addContentView(layout, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
}

fun setTitle(title: String): Builder{
this.title = title
return this
}

fun setMessage(message: String): Builder {
this.message = message
return this
}

fun setContentView(v: View): Builder {
this.contentView = v
return this
}

fun setPositiveButton(positiveButtonText: String, listener: View.OnClickListener): Builder {
this.positiveButtonText = positiveButtonText
this.positiveButtonClickListener = listener
return this
}

fun setNegativeButton(negativeButtonText: String, listener: View.OnClickListener): Builder {
this.negativeButtonText = negativeButtonText
this.negativeButtonClickListener = listener
return this
}

fun setSingleButton(singleButtonText: String, listener: View.OnClickListener): Builder {
this.singleButtonText = singleButtonText
this.singleButtonClickListener = listener
return this
}

/**
* 创建单按钮对话框
* @return
*/
fun createSingleButtonDialog(): CustomDialog {
showSingleButton()
layout.findViewById<View>(R.id.singleBtn).setOnClickListener(singleButtonClickListener)
//如果传入的按钮文字为空,则使用默认的“知道了”
if (singleButtonText != null) {
(layout.findViewById<View>(R.id.singleBtn) as TextView).text = singleButtonText
} else {
(layout.findViewById<View>(R.id.singleBtn) as TextView).text = "知道了"
}
create()
return dialog
}

/**
* 创建双按钮对话框
* @return
*/
fun createTwoButtonDialog(): CustomDialog {
showTwoButton()
layout.findViewById<View>(R.id.positiveBtn).setOnClickListener(positiveButtonClickListener)
layout.findViewById<View>(R.id.negativeBtn).setOnClickListener(negativeButtonClickListener)
//如果传入的按钮文字为空,则使用默认的“确定”和“取消”
if (positiveButtonText != null) {
(layout.findViewById<View>(R.id.positiveBtn) as TextView).text = positiveButtonText
} else {
(layout.findViewById<View>(R.id.positiveBtn) as TextView).text = "确定"
}
if (negativeButtonText != null) {
(layout.findViewById<View>(R.id.negativeBtn) as TextView).text = negativeButtonText
} else {
(layout.findViewById<View>(R.id.negativeBtn) as TextView).text = "取消"
}
create()
return dialog
}

/**
* 单按钮对话框和双按钮对话框的公共部分在这里设置
*/
private fun create() {
if (message != null) { //设置提示内容
(layout.findViewById<View>(R.id.message) as TextView).text = message
} else if (contentView != null) { //如果使用Builder的setContentview()方法传入了布局,则使用传入的布局
(layout.findViewById<View>(R.id.content) as LinearLayout).removeAllViews()
(layout.findViewById<View>(R.id.content) as LinearLayout)
.addView(contentView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
}
if(title!=null && title!!.isNotEmpty()){
(layout.findViewById<View>(R.id.title) as TextView).text = title
showTitle()
}
dialog.setContentView(layout)
dialog.setCancelable(true) //用户可以点击手机Back键取消对话框显示
dialog.setCanceledOnTouchOutside(false) //用户不能通过点击对话框之外的地方取消对话框显示
}

/**
* 显示双按钮布局,隐藏单按钮
*/
private fun showTwoButton() {
layout.findViewById<View>(R.id.singleButtonLayout).visibility = View.GONE
layout.findViewById<View>(R.id.twoButtonLayout).visibility = View.VISIBLE
}

/**
* 显示单按钮布局,隐藏双按钮
*/
private fun showSingleButton() {
layout.findViewById<View>(R.id.singleButtonLayout).visibility = View.VISIBLE
layout.findViewById<View>(R.id.twoButtonLayout).visibility = View.GONE
}

private fun showTitle() {
layout.findViewById<View>(R.id.title).visibility = View.VISIBLE
}

}
}

3.设置dialog的背景

drawable/comment_dialog_bg.xml

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp"/>
<solid android:color="#282a34"/>
</shape>

4.将上面写好的dialog背景设置给一个style

style.xml

1
2
3
<style name="CustomDialog" parent="android:Theme.Dialog">
<item name="android:windowBackground">@drawable/comment_dialog_bg</item>
</style>

5.最后在activity里定义并调用自定义dialog的方法。

MainActivty.kt

1
2
private var builderForCustom: CustomDialog.Builder? = null
private var mDialog: CustomDialog? = null

然后在onCreate里

1
builderForCustom = CustomDialog.Builder(this)

定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private fun showSingleButtonDialog(title: String, alertText: String, btnText: String, onClickListener:View.OnClickListener) {
mDialog = builderForCustom!!.setTitle(title)
.setMessage(alertText)
.setSingleButton(btnText, onClickListener)
.createSingleButtonDialog()
mDialog!!.show()
}

private fun showTwoButtonDialog(title: String, alertText: String, confirmText: String, cancelText: String, conFirmListener: View.OnClickListener, cancelListener: View.OnClickListener) {
mDialog = builderForCustom!!.setTitle(title)
.setMessage(alertText)
.setPositiveButton(confirmText, conFirmListener)
.setNegativeButton(cancelText, cancelListener)
.createTwoButtonDialog()
mDialog!!.show()
}

调用方法

1
2
3
4
5
6
7
showTwoButtonDialog("", "这是自定义弹出框","确定", "取消", View.OnClickListener {
// 操作
mDialog!!.dismiss()
},View.OnClickListener {
// 操作
mDialog!!.dismiss()
})

调用单个按钮的方法也是同样的方式。

如果是在fragment调用activity里定义的弹出框方法,则可以单独定义一个关闭弹出框的方法,或者将mDialog设置为公共属性。

效果如下
be902c2e8e6aa2e10cd9c4d4c125814.jpg