preface

PopWindow toolsCopy the code

import android.app.Activity
import android.content.Context
import android.graphics.drawable.BitmapDrawable
import android.os.Build
import android.view.Display
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.widget.PopupWindow

/*** * PopWindowUtil * Created by zxjie on 2021/5/17. * address:bailingkeji * Describe: When using it, please call init first and then call showPopWindow to display it. This tool class uses singleton design mode * link:https://blog.csdn.net/weixin_46603990/article/details/116987681 * https://juejin.cn/post/6963513638783205406/ * example: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * | | - instantiation objects - private val popWindowUtil | | = popWindowUtil * * - initialize PopWindow - * * | | - set the fixed size popWindowUtil. The init (this, R.l ayout. Pop_alert_two, 295 f, 135 f, null) * * | | - adapt to maximize screen popWindowUtil.init(this,R.layout.pop_alert_two,PopWindowUtil.MATCH,PopWindowUtil.MATCH,null) * | - based on set high, wide screen, the parameters in the Restraint only for when the CHANGE will only take effect in the former, formula for high Restraint. The wide screen width/height * | PopWindowUtil. Init (this, R.l ayout. Pop_alert_two, popWindowUtil. CHANGE, popWindowUtil. CHANGE, Restraint (20, 60)) * | - use a combination of, filling the wide, based on the screen Settings * | PopWindowUtil. Init (this, R.l ayout. Pop_alert_two, popWindowUtil. MATCH, popWindowUtil. CHANGE, Restraint (0 ')) * * | | - access to all controls required * | val btn_ok = popWindowUtil. GetViewById (R.i db tn_ok) as TextView set * * | | - click on the window, the window disappears, the default is false * | PopWindowUtil. OutSideTouchable (true) * * | | - get focus, default to false | popWindowUtil. ItemClickable (true) * * | | - use the default shadows, default to false | popWindowUtil. ShadowShowAble | (true) * * * | | - set PopWindow disappear to monitor popWindowUtil. DismissListener = | | - custom background *} {* * * | | - pop-up PopWindow note: pop-up PopWindow never leap * | - based on the entire screen center pop-up screen, 10 dp * | downward migration popWindowUtil.showPopWindow(toolbar,true, Excursion (Gravity CENTER, 0, 10), null) * | -- based on the whole in the upper left screen pop up, and shift to the left ten dp note: at this time the PopWindow have shown in the most on the left side of the screen, so the * | - 10 is invalid popWindowUtil.showPopWindow(toolbar,true, Excursion (Gravity. LEFT or Gravity. TOP, to 10, 0), null) * | - based on the entire screen sliding pop up from below, and above the tabLayout * | popWindowUtil.showPopWindow(tab_layout, true, Excursion(Gravity.BOTTOM, 0, DensityUtil.px2dip(this, (tab_layout.layoutParams.height+nbHeight-4) * 1F)), * | - pay attention to the direction of the offset is not right, upper left, it is determined according to the position constraint, position constraint for the BOTTOM, then is positive, negative, if for the TOP, is positive, negative, on the x axis in the same way * | - not recommended based on the lower left of the toolbar controls the popup Note: to false position constraint (Gravity CENTER) will be failure * | popWindowUtil. ShowPopWindow (the toolbar, false, Excursion (Gravity CENTER, 0, 0), * * | | null) - suggest setting PopWindow pop-up when press the return key to close PopWindow, rather than close Activity, note: onBackPressed () method can only be rewritten in the Activity * | override fun onBackPressed () {*  | if (! PopWindowUtil. DisMiss () finish () * * * | | |} - closed PopWindow * | popWindowUtil. DisMiss () * * | | - destroy PopWindow used by objects in onDestroy call * | popWindowUtil. Destroy () * * | | - class provides the tools of method does not meet your demand can call the following method to get * | PopWindow object popWindowUtil.getPopWindow() * |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
object PopWindowUtil {
    private var winHeight: Int = 0
    private var winWidth: Int = 0
    private var view: View? = null
    private var popupWindow: PopupWindow? = null
    private var context: Context? = null
    private var canDown: Boolean = false
    private var canClick: Boolean = false
    private var canShow: Boolean = false
    private var ERROR: Boolean = false
    const val MATCH = -100F
    const val CHANGE = -1000F


    / * * * *@paramContext *@paramLayoutId Resource file *@paramWidth PopWindow the required width /dp is set to the distance from the screen * when the full screen is set to CHANGE@paramHeight PopWindow The required height /dp is set to the distance from the screen * when the full screen is set to CHANGE@paramParameter restraint can be empty and invalid if the width and height are not "CHANGE", but valid if width is "CHANGE" (PopWindow /dp). Height applies when height is CHANGE (PopWindow is the distance above and below the screen /dp */)
    fun init(context: Context, layoutId: Int, width: Float, height: Float, restraint: Restraint?). {
        this.context = context
        if(isExist()) { popupWindow!! .dismiss() popupWindow =null
        }
        val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val display: Display = wm.defaultDisplay
        winHeight = display.height
        winWidth = display.width
        view = View.inflate(context, layoutId, null)
        val useWidth = when(width) { MATCH -> winWidth CHANGE -> (winWidth - dip2px(restraint!! .width))else -> dip2px(width)
        }
        ERROR = height == MATCH
        val useHeight = when{ height == MATCH -> winHeight width == CHANGE -> (winHeight - dip2px(restraint!! .height))else -> dip2px(height)
        }
        popupWindow = PopupWindow(view, useWidth, useHeight)
    }

    / * * *@paramId Id * to be obtained@returnReturns a View object that needs to be cast to the desired control type */
    fun getViewById(id: Int): View? =
        if(isExist()) view!! .findViewById(id)else throw NullPointerException("Failed to find control, please initialize")

    / * * *@paramLocation A View object used to constrain the PopWindow position *@paramGlobal ejects based on the screen True Ejects based on the screen false ejects based on the lower left of the Location control. False * is not recommended@paramExcursion parameter 1 is the location constraint. If global is false, the location constraint will fail. Parameter 2 passes the offset /dp of the X axis, and parameter 3 passes the offset /dp of the Y axis. * it's determined by the position constraint, the position constraint is BOTTOM, so it's plus up, minus down, and if it's TOP, it's plus down, minus up, and the same thing with the X-axis *@paramAnim animation effect, optionally passed in, empty */ when not needed
    fun showPopWindow(location: View, global: Boolean, excursion: Excursion, anim: Int?). {
        val disPose = disPose(location)
        val x = dip2px(excursion.x.toFloat())
        val y = dip2px(excursion.y.toFloat())
        if(isExist()) { popupWindow? .let {if(anim ! =null) it.animationStyle = anim
                it.isFocusable = canClick
                if (canDown) it.setBackgroundDrawable(BitmapDrawable())
                it.isOutsideTouchable = canDown
                if (canShow) setBackgroundAlpha(0.5 F) popupWindow!! .setOnDismissListener {if (canShow) setBackgroundAlpha(1F) dismissListener? .invoke() }if (global) {
                    if (ERROR) it.height = winHeight - disPose
                    it.showAtLocation(location, excursion.gravity, x, y)
                } else {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ERROR) {
                        val height = location.layoutParams.height
                        it.height = winHeight - (disPose + height)
                        it.showAtLocation(location, Gravity.NO_GRAVITY, 0, disPose + height)
                    } else it.showAsDropDown(location, x, y)
                }
            }
        } else throw NullPointerException("PopWindow failed to display, please initialize")}/** * handle the issue of Match override control on 7.1 */
    fun disPose(location: View): Int {
        val a = IntArray(2)
        location.getLocationOnScreen(a)
        return a[1]}/** * Close PopWindow *@returnTrue on success, false */ otherwise
    fun disMiss(a): Boolean =
        if(isExist() && isShowing()) { popupWindow!! .dismiss()true
        } else false

    /** * gets the current PopWindow object *@returnIsExist () returns a PopWindow object when it is true and raises an exception */ when it is false
    fun getPopWindow(a) =
        if (isExist()) popupWindow
        else throw NullPointerException("Failed to get PopWindow object,PopWindow sample object is empty")

    /** * sets the background */
    fun setBackgroundAlpha(bgAlpha: Float) {
        val activity: Activity = context as Activity
        val lp = activity.window.attributes
        lp.alpha = bgAlpha
        if (bgAlpha == 1f) activity.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
        else activity.window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
        activity.window.attributes = lp
    }

    /** * Sets whether to use the built-in shadow effect *@paramCanShow false Do not use this command. True use */
    fun shadowShowAble(canShow: Boolean) {
        this.canShow = canShow
    }

    /** * Sets whether you can click the window outside the window to disappear *@paramCanDown false cannot be hit to disappear, true can be hit to disappear */
    fun outSideTouchable(canDown: Boolean) {
        this.canDown = canDown
    }

    /** * Sets whether to get focus *@paramCanClick false */ canClick false */ canClick false
    fun itemClickable(canClick: Boolean) {
        this.canClick = canClick
    }

    /** * whether PopWindow is displayed *@returnFalse */ is not displayed
    fun isShowing(a)= popupWindow!! .isShowing/** * PopWindow whether to instantiate *@returnTrue is instantiated, false is null */
    fun isExist(a)= popupWindow ! =null

    /** * to empty the used object, we recommend calling */
    fun destroy(a) {
        dismissListener = nullpopupWindow? .dismiss() popupWindow =null
        context = null
        view = null
    }

    /** * unit convert dp to px *@returnConverted to px */
    fun dip2px(dpValue: Float): Int =
        (dpValue * this.context!! .resources.displayMetrics.density +0.5 f).toInt()

    /** * interface callback */ via method variables
    var dismissListener: (() -> Unit)? = null
}

/ * * *@paramGravity position constraint parameters for: gravity. TOP, gravity. BOTTOM, gravity. LEFT, gravity. RIGHT, gravity. CENTER *@param x
 * @param y
 */
data class Excursion(val gravity: Int.val x: Int.val y: Int)

/ * * *@paramWidth PopWindow Is the distance to the left and right of the screen *@paramHeight PopWindow The distance between the top and bottom of the screen */
data class Restraint(val width: Float.val height: Float)
Copy the code