El código principal de SmartDialog puede realizar el cuadro de diálogo de posicionamiento del punto de anclaje, ubicar el cuadro de diálogo de contenido calculando las coordenadas de la Vista del punto de anclaje en la pantalla, modificar la posición de visualización del cuadro de diálogo configurando la dirección y el margen, y admitir la configuración dinámica de la anchura y la altura máximas.
/**
* 作者 我的安卓之路
* CreateTime 2022/12/2814:38
* 任意位置弹出的dialog
*/
class SmartDialog private constructor(val build: DialogBuild) : DialogFragment() {
private val rootView by lazy {
val linearLayout = RelativeLayout(context!!)
val layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
linearLayout.layoutParams = layoutParams
linearLayout
}
private var anchorPointView: View? = null//锚点View
private val anchorPoint by lazy {
val anchorLocation = intArrayOf(0, 0)
anchorPointView?.getLocationInWindow(anchorLocation)
anchorLocation
}
private val background by lazy { //背景
val v = View(context)
val layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
)
v.setBackgroundResource(build.backgroundRes)
v.alpha = build.dimAmount
v.layoutParams = layoutParams
v.visibility = View.INVISIBLE
v
}
private val windowHeight by lazy { rootView.height+titleBarHeight }
private val windowWith by lazy { Utils.getScreenWidth(context) }
private var gravity: Int = RelativeLayout.ALIGN_PARENT_TOP
var dismissListener:(()->Unit)?=null
private val titleBarHeight by lazy {
Utils.getStatusBarHeight(context)
}
fun showDialog(anchorPoint: View?, manager: FragmentManager, tag: String) {
this.anchorPointView = anchorPoint
show(manager, tag)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
setStyle(STYLE_NORMAL, build.themeStyle)
rootView.removeAllViews()
rootView.addView(background)
rootView.addView(build.layoutView)
initView()
return rootView
}
private fun initView() {
build.layoutView.visibility = View.INVISIBLE
build.layoutView.post {
measure(build.layoutView.height)
}
build.layoutView.setOnClickListener {}
rootView.setOnClickListener {
if (build.cancelable) {
dismiss()
}
}
val layoutParams = build.layoutView.layoutParams
layoutParams.height = build.height
layoutParams.width = build.width
build.layoutView.layoutParams = layoutParams
}
private fun measure(height: Int) {
measureHeight(height)
setVertical()
setHorizontal()
build.layoutView.visibility = View.VISIBLE
}
//计算布局的高度
private fun measureHeight(height: Int) {
val layoutParams = build.layoutView.layoutParams
if (build.maxHeight != ViewGroup.LayoutParams.MATCH_PARENT && build.maxHeight < height) {
layoutParams.height = build.maxHeight
}
if (build.minHeight != ViewGroup.LayoutParams.WRAP_CONTENT && build.minHeight > height) {
layoutParams.height = build.minHeight
}
build.layoutView.layoutParams = layoutParams
}
private fun setVertical() {
val y =anchorPoint[1]
val viewHeight: Int = anchorPointView?.height ?: 0
val layoutHeight = build.layoutView.height
val layoutParams = build.layoutView.layoutParams as RelativeLayout.LayoutParams
rootView.gravity
when (build.vertical) {
VerticalPosition.ABOVE -> {//在上方
layoutParams.bottomMargin = windowHeight - y - build.y
gravity = RelativeLayout.ALIGN_PARENT_BOTTOM
}
VerticalPosition.BELOW -> {//在下方
val mY =if (build.isHaveTitleBar) y - titleBarHeight else y
layoutParams.topMargin = mY + viewHeight + build.y
gravity = RelativeLayout.ALIGN_PARENT_TOP
}
VerticalPosition.CENTER -> {//居中对齐
val mY =if (build.isHaveTitleBar) y - titleBarHeight else y
layoutParams.topMargin = (viewHeight - layoutHeight) / 2 + mY + build.y
gravity = RelativeLayout.ALIGN_PARENT_TOP
}
VerticalPosition.ALIGN_TOP -> {//顶部对齐
val mY =if (build.isHaveTitleBar) y - titleBarHeight else y
layoutParams.topMargin = mY + build.y
gravity = RelativeLayout.ALIGN_PARENT_TOP
}
VerticalPosition.ALIGN_BOTTOM -> {//底部对齐
layoutParams.bottomMargin = windowHeight - viewHeight - y - build.y
gravity = RelativeLayout.ALIGN_PARENT_BOTTOM
}
}
if (layoutParams.bottomMargin < 0)
layoutParams.bottomMargin = 0
if (layoutParams.topMargin < 0)
layoutParams.topMargin = 0
layoutParams.addRule(gravity)
build.layoutView.layoutParams = layoutParams
setBackground(layoutParams.topMargin, layoutParams.bottomMargin)
}
private fun setHorizontal() {
val x = anchorPoint[0]
val viewWidth: Int = anchorPointView?.width ?: 0
val layoutWith = build.layoutView.width
val layoutParams = build.layoutView.layoutParams as RelativeLayout.LayoutParams
when (build.horizontal) {
HorizontalPosition.CENTER -> {
layoutParams.leftMargin = (viewWidth - layoutWith) / 2 + x + build.x
gravity = RelativeLayout.ALIGN_PARENT_LEFT
}
HorizontalPosition.LEFT -> {
layoutParams.rightMargin = windowWith - x - build.x
gravity = RelativeLayout.ALIGN_PARENT_RIGHT
}
HorizontalPosition.RIGHT -> {
layoutParams.leftMargin = x + viewWidth + build.x
gravity = RelativeLayout.ALIGN_PARENT_LEFT
}
HorizontalPosition.ALIGN_LEFT -> {
layoutParams.leftMargin = x + build.x
gravity = RelativeLayout.ALIGN_PARENT_LEFT
}
HorizontalPosition.ALIGN_RIGHT -> {
layoutParams.rightMargin = windowWith - viewWidth - x - build.x
gravity = RelativeLayout.ALIGN_PARENT_RIGHT
}
}
if (layoutParams.leftMargin < 0)
layoutParams.leftMargin = 0
if (layoutParams.rightMargin < 0)
layoutParams.rightMargin = 0
layoutParams.addRule(gravity)
build.layoutView.layoutParams = layoutParams
}
//设置背景所在参数
private fun setBackground(topMargin: Int, botMargin: Int) {
background.visibility = View.VISIBLE
if (build.isBackgroundFullScreen)return
val layoutParams = background.layoutParams as RelativeLayout.LayoutParams
layoutParams.topMargin = topMargin
layoutParams.bottomMargin = botMargin
background.layoutParams = layoutParams
}
class DialogBuild(
val layoutView: View,
var horizontal: Int,
var vertical: Int,
var height: Int = ViewGroup.LayoutParams.WRAP_CONTENT,
var width: Int = ViewGroup.LayoutParams.MATCH_PARENT,
var minHeight: Int = ViewGroup.LayoutParams.WRAP_CONTENT,
var maxHeight: Int = ViewGroup.LayoutParams.MATCH_PARENT,
var themeStyle: Int = R.style.DialogThemeStyle,
var animStyle: Int = 0,
var x: Int = 0,
var y: Int = 0,
var dimAmount: Float = 0f,
var cancelable: Boolean = false,
var isHaveTitleBar:Boolean = true,
var isBackgroundFullScreen:Boolean = false,
var backgroundRes:Int = R.color.black
) {
fun build(): SmartDialog {
return SmartDialog(this)
}
}
override fun onStart() {
val dialog = dialog
if (dialog != null) {
val win = dialog.window
// 一定要设置Background,如果不设置,window属性设置无效
win!!.setBackgroundDrawable(ColorDrawable(0x00000000))
val params = win.attributes
params.gravity = Gravity.CENTER
params.dimAmount = 0f
// 使用ViewGroup.LayoutParams,以便Dialog 宽度充满整个屏幕
params.width = ViewGroup.LayoutParams.MATCH_PARENT
params.height = Utils.getWindowContentHeight(activity)
// params.height = Utils.getScreenHeight(context)+Utils.getStatusBarHeight(context)
win.setWindowAnimations(build.animStyle)
win.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
win.attributes = params
}
super.onStart()
}
override fun dismiss() {
super.dismiss()
dismissListener?.invoke()
}
}
Modo paisaje
int CENTER = 0; alineación central
int LEFT = 1; a la izquierda
int RIGHT = 2; a la derecha
int ALIGN_LEFT = 3; alineación a la izquierda
int ALIGN_RIGHT = 4; alineación a la derecha
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@IntDef({
HorizontalPosition.CENTER,
HorizontalPosition.LEFT,
HorizontalPosition.RIGHT,
HorizontalPosition.ALIGN_LEFT,
HorizontalPosition.ALIGN_RIGHT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface HorizontalPosition {
int CENTER = 0;
int LEFT = 1;
int RIGHT = 2;
int ALIGN_LEFT = 3;
int ALIGN_RIGHT = 4;
}
modo retrato
int CENTRO = 0; alinear al centro
int ARRIBA = 1; alinear arriba
int ABAJO = 2; alinear abajo
int ALIGN_TOP = 3; alinear arriba
int ALIGN_BOTTOM = 4; alinear abajo
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@IntDef({
VerticalPosition.CENTER,
VerticalPosition.ABOVE,
VerticalPosition.BELOW,
VerticalPosition.ALIGN_TOP,
VerticalPosition.ALIGN_BOTTOM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface VerticalPosition {
int CENTER = 0;
int ABOVE = 1;
int BELOW = 2;
int ALIGN_TOP = 3;
int ALIGN_BOTTOM = 4;
}
estilo de estilo
<style name="DialogThemeStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowSoftInputMode">adjustPan</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>