SmartDialog 主代码 可实现锚点定位dialog ,通过计算锚点View在屏幕中的坐标对内容dialog进行定位,通过设置方向与边距修改dialog的显示位置,并且支持最大宽高的动态设置。
/**
* 作者 我的安卓之路
* 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()
}
}
横向显示模式
int CENTER = 0; 居中对齐
int LEFT = 1; 在左侧
int RIGHT = 2; 在右侧
int ALIGN_LEFT = 3; 左对齐
int ALIGN_RIGHT = 4; 右对齐
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;
}
竖向显示模式
int CENTER = 0; 居中对齐
int ABOVE = 1; 在上方
int BELOW = 2; 在下方
int ALIGN_TOP = 3; 顶部对齐
int ALIGN_BOTTOM = 4;底部对齐
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;
}
Style的样式
<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>