Android はポップウィンドウ効果ダイアログを実装します

SmartDialog のメイン コードは、アンカー ポイントの位置決めダイアログを実現し、画面上のアンカー ポイント ビューの座標を計算してコンテンツ ダイアログの位置を特定し、方向とマージンを設定してダイアログの表示位置を変更し、動的設定をサポートします。最大の幅と高さ。

/**
 * 作者 我的安卓之路
 * 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 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>

おすすめ

転載: blog.csdn.net/qq_35644925/article/details/128496834