一、自定义Dialog
已加入:样品工具库:kirikaTowa/AndroidUtils: Common tool library (github.com)
1、 参考文章:[android]我是这样写自定义Dialog的
- 改版原因:缺少部分代码,进行补齐,代码精简;使用kotlion+lambda进行yes,no选项的优化,使用Databinding与stateCanceled(可否点击外部关闭)。
2、 相关代码
- BaseDialog
abstract class BaseDialog<VB : ViewDataBinding>(
var context: Context
) {
val DIALOG_COMMON_STYLE:Int= R.style.common_dialog_style
private val display: Display
private var dialog : Dialog? = null
abstract val layoutId: Int
protected abstract val dialogStyleId: Int
protected abstract val stateCanceled: Boolean
protected lateinit var binding: VB
init {
binding = DataBindingUtil.inflate(LayoutInflater.from(context),layoutId, null, false)
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
display = windowManager.defaultDisplay
dialog = if (dialogStyleId == 0) {
Dialog(context, DIALOG_COMMON_STYLE)
} else {
Dialog(context, dialogStyleId)
}
// 调整dialog背景大小
binding.root.layoutParams = FrameLayout.LayoutParams(
(display.width * 0.8).toInt(),
LinearLayout.LayoutParams.WRAP_CONTENT
)
dialog?.setContentView(binding.root)
//隐藏系统输入盘
dialog?.setCanceledOnTouchOutside(stateCanceled)
dialog?.window!!
.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
initViewEvent()
}
protected open fun initViewEvent() {}
fun show() {
dialog!!.show()
}
fun dismiss() {
dialog!!.dismiss()
}
val isShowing: Boolean
get() = dialog!!.isShowing
}
- Theme
<style name="common_dialog_style" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
- 子类Demo(附带了常规的string设置颜色和相应点击事件:spannableBuilder.setSpan)
class StatementDialog
(context: Context?, var callback: ((Boolean) -> Unit)?) :
BaseDialog<DialogStatementBinding>(context!!) {
override val dialogStyleId: Int
get() = DIALOG_COMMON_STYLE
override val stateCanceled: Boolean
get() = false
override val layoutId: Int
get() = R.layout.dialog_statement
//View的事件
override fun initViewEvent() {
//设置对话框那个叉叉的方法,点击关闭对话框
binding.apply {
tvYes.setOnClickListener {
callback?.invoke(true)
}
tvNo.setOnClickListener {
callback?.invoke(false)
}
val stringContext = context.resources.getString(R.string.str_statement_content)
/* val colorSpan =
ForegroundColorSpan(ContextCompat.getColor(context, R.color.color_effect_180))*/
val stringProtocolOne = context.resources.getString(R.string.str_protocol_one)
val positionOne = stringContext.indexOf(stringProtocolOne)
val spannableBuilder = SpannableStringBuilder(stringContext)
/* // 单独设置字体颜色
spannableBuilder.setSpan(
colorSpan,
positionOne,
positionOne+stringProtocolOne.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)*/
// 单独设置点击事件
val clickableSpanOne: ClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
WebViewActivity.launch(context as Activity,WebViewActivity.URL_PROTOCOL_ONE)
(context as Activity).overridePendingTransition(R.anim.anim_in_up, R.anim.anim_origin)
}
override fun updateDrawState(ds: TextPaint) {
/**set textColor**/
ds.color = ds.linkColor
/**Remove the underline**/
ds.isUnderlineText = false
}
}
spannableBuilder.setSpan(clickableSpanOne, positionOne,
positionOne+stringProtocolOne.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
val stringProtocolTwo = context.resources.getString(R.string.str_protocol_two)
val positionTwo = stringContext.indexOf(stringProtocolTwo)
// 单独设置点击事件
val clickableSpanTwo: ClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
WebViewActivity.launch(context as Activity,WebViewActivity.URL_PROTOCOL_TWO)
(context as Activity).overridePendingTransition(R.anim.anim_in_up, R.anim.anim_origin)
}
override fun updateDrawState(ds: TextPaint) {
/**set textColor**/
ds.color = ds.linkColor
/**Remove the underline**/
ds.isUnderlineText = false
}
}
spannableBuilder.setSpan(clickableSpanOne, positionOne,
positionOne+stringProtocolOne.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannableBuilder.setSpan(clickableSpanTwo, positionTwo,
positionTwo+stringProtocolTwo.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tvContent.movementMethod = LinkMovementMethod.getInstance()
tvContent.highlightColor = Color.TRANSPARENT
tvContent.text = spannableBuilder;
}
}
}
- 子类对应layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/rl_dialog_pwd"
android:layout_width="match_parent"
android:layout_marginStart="@dimen/dp_50"
android:layout_marginEnd="@dimen/dp_50"
android:layout_height="wrap_content"
android:background="@drawable/shape_confirm_dialog_bg"
android:orientation="vertical"> <!-- 取消对话框按钮图标 -->
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_statement"
android:layout_width="@dimen/dp_48"
android:layout_height="@dimen/dp_48"
android:layout_marginTop="@dimen/dp_15"
android:src="@drawable/ic_statement"
app:layout_constraintEnd_toStartOf="@id/tv_dialog_statement_title"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_dialog_statement_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_10"
android:layout_marginTop="@dimen/dp_8"
android:text="个人信息保护提示"
android:textColor="@color/black"
android:textSize="@dimen/dp_20"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/iv_statement"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/iv_statement"
app:layout_constraintTop_toTopOf="@id/iv_statement" />
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/dp_15"
android:padding="@dimen/dp_15"
android:text="@string/str_statement_content"
app:layout_constraintTop_toBottomOf="@id/iv_statement" />
<View
android:id="@+id/view_line"
app:layout_constraintTop_toBottomOf="@id/tv_content"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:background="#aaaaaa" />
<TextView
android:id="@+id/tv_no"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/str_statement_no"
android:paddingTop="@dimen/dp_20"
app:layout_constraintStart_toStartOf="parent"
android:gravity="center"
app:layout_constraintEnd_toStartOf="@id/tv_yes"
android:paddingBottom="@dimen/dp_20"
app:layout_constraintTop_toBottomOf="@id/view_line"
app:layout_constraintBottom_toBottomOf="parent" />
<View
android:id="@+id/view_line_guide"
app:layout_constraintTop_toTopOf="@id/view_line"
app:layout_constraintBottom_toBottomOf="@id/tv_yes"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="@dimen/dp_1"
android:layout_height="0dp"
android:background="#aaaaaa" />
<TextView
android:id="@+id/tv_yes"
android:layout_width="0dp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
android:layout_height="wrap_content"
android:text="@string/str_statement_yes"
app:layout_constraintStart_toEndOf="@id/tv_no"
android:paddingTop="@dimen/dp_20"
android:paddingBottom="@dimen/dp_20"
app:layout_constraintTop_toBottomOf="@id/view_line"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- 图例
- Activity中调用方式
var stateDialog:StatementDialog?=null
stateDialog= StatementDialog(this@MainActivity) {
if (it) {//点击了同意
stateDialog?.dismiss()
} else {//点击了不同意
finish()
stateDialog?.dismiss()
}
}
stateDialog.show()
}
二、改版
1、改版原因
1、Dialog类移动到一个老项目里发现宽高受到了限制,像是有padding。
- Theme.Dialog中确实有listPreferredItemPadd,但是新项目不受影响,老项目受影响;
- 写了一个继承Dialog后的,代码中手动调整发现ok了,代码已更新,在仓库里就不贴出来水字数了
class DialogComplaintFullScreen(context: Context,var callback: ((Boolean) -> Unit)?) : Dialog(context) {
private lateinit var binding: DialogComplaintBinding
fun setIvComplaint(context: Context,url: String?) {
Glide.with(context).load(url).into(binding.ivComplaint)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.dialog_complaint, null, false)
setContentView(binding.root )
setCancelable(true) //按返回可以关闭
setCanceledOnTouchOutside(true) //按屏幕外面可以关闭
//设置背景 xml中设置无效
window?.decorView?.setBackgroundResource(R.color.transparent)
//设置尺寸 Base可提供个方法供子view实现
window?.setLayout(DeviceUtil.dip2px(context, 488f), DeviceUtil.dip2px(context, 800f))
window?.decorView?.setPadding(0, 0, 0, 0)
binding.btnDismiss.setOnClickListener {
callback?.invoke(true)
}
}
}
三、其他Dialog知识
1、多种Dialog样式
3、小技巧
- 去掉自定义dialog的白色背景_取消dialog的白色背景颜色
- 有些时候padding比margin更好用(可能会不生效)
- dismiss和cancel的:他们是类似的,区别在于调用cancel方法时不仅仅会调用dismiss方法,还会调用你实现的DialogInterface.OnCancelListener方法。
4、Dialog对于获取Activity还是蛮严格的:Context转Activity
5、Dialog使用Kotlin队列完成依次弹窗的责任链模式:
- Kotlin 技术月报 | 2023 年 9 月 (qq.com)
- 如果分开写也是OK的
lifecycleScope.launch {
showDialog("签到活动", "签到领10000币") // 直到dialog被关闭, 才会继续运行下一行
}
lifecycleScope.launch {
showDialog("新手任务", "做任务领20000币") // 直到dialog被关闭, 才会继续运行下一行
showDialog("首充奖励", "首充6元送神装")
}
四、DialogFragment
2、dialogfragment监听返回键_dialogfragment 返回键监听
- 在onCreateDialog的Dialog里要dialog.setOnKeyListener(this);
- Fragment必要有个无参构造方法
- 文章里的instance不是单例,提供个静态方法创建dialog;
4、生命周期 * DialogFragment 不会触发原fragment和Activity的onresume
5、关于全屏
- DialogFragment自身设定就不是全屏的,宽度受限 *宽度根据内容自适应,但最大不会超过某个限度,可能受屏幕比例影响
- 正常情况Margin和Padding都是有效的,但效果有限,超过最大限度的宽度则不生效
- 但有的机子可能只有padding会生效
总体来说DialogFragment还是和普通Fragment很像的 Dialog默认会把View居中,想跳位置的话,margin没啥卵用,加个透明VIEW补视差
- 方法一
- 简单居中
override fun onStart() { super.onStart() dialog?.window?.setLayout(-1,-2) }
- 方法二
- 将view置于底部
/**
* 设置弹框宽度全屏
*/
override fun onStart() {
super.onStart()
val win = dialog!!.window
// 一定要设置Background,如果不设置,window属性设置无效
win!!.setBackgroundDrawableResource(android.R.color.transparent)
val dm = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(dm)
val params = win.attributes
params.gravity = Gravity.BOTTOM
// 使用ViewGroup.LayoutParams,以便Dialog 宽度充满整个屏幕
params.width = ViewGroup.LayoutParams.MATCH_PARENT
params.height = ViewGroup.LayoutParams.WRAP_CONTENT
// params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
win.attributes = params
}
6、改变蒙层深度
override fun onStart() {
super.onStart()
dialog?.window?.apply {
// 设置背景蒙层深度,0.0f = 无蒙层,1.0f = 全黑
setDimAmount(0.6f)
}
}