Android - 让视图文件显示到状态栏下,且不受底部导航虚拟按键影响

Android - 让视图文件显示到状态栏下,且不受底部导航虚拟按键影响

最新需要实现这个功能,琢磨了半天,网上也查找了一些资料,不过都是零零散散的,而且大部分是xml实现的,现在我用代码大致来实现一下这个功能。

效果图

第一步,获取状态栏高度,底部虚拟导航按键高度

/**
     * 获取状态栏高度
     *
     * @param context 上下文对象
     */
    @SuppressLint("PrivateApi")
    @JvmStatic
    fun getStatusBarHeight(context: Context): Int = try {
        val c: Class<*>? = Class.forName("com.android.internal.R\$dimen")
        val obj: Any? = c?.newInstance()
        val field: Field? = c?.getField("status_bar_height")
        if (obj != null && field != null) {
            field.isAccessible = true
            val x = Integer.parseInt(field.get(obj).toString())
            context.resources.getDimensionPixelSize(x)
        } else {
            75
        }
    } catch (e1: Exception) {
        Log.d("ApkUtil", "get status bar height fail")
        e1.printStackTrace()
        75
    }

    /**
     * 获取底部导航栏高度
     *
     * @param context
     * @return
     */
    @JvmStatic
    fun getNavigationBarHeight(context: Context): Int {
        if (!isHasNavBar(context)) return 0
        val resourceId: Int
        val rid = context.resources.getIdentifier("config_showNavigationBar", "bool", "android")
        return if (rid != 0) {
            resourceId = context.resources.getIdentifier("navigation_bar_height", "dimen", "android")
            context.resources.getDimensionPixelSize(resourceId)
        } else {
            0
        }
    }

    /**
     * 判断虚拟按键栏是否重写
     *
     * @return
     */
    private val navBarOverride: String?
        @SuppressLint("PrivateApi")
        get() {
            var sNavBarOverride: String? = null
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                try {
                    val c = Class.forName("android.os.SystemProperties")
                    val m = c.getDeclaredMethod("get", String::class.java)
                    m.isAccessible = true
                    sNavBarOverride = m.invoke(null, "qemu.hw.mainkeys") as String
                } catch (e: Throwable) {
                }

            }
            return sNavBarOverride
        }

    /**
     * 检查是否存在虚拟按键栏
     *
     * @param context
     * @return
     */
    @JvmStatic
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    fun isHasNavBar(context: Context): Boolean {
        val res = context.resources
        val resourceId = res.getIdentifier("config_showNavigationBar", "bool", "android")
        return when {
            resourceId != 0 -> {
                var hasNav = res.getBoolean(resourceId)
                // check override flag
                val sNavBarOverride = navBarOverride
                if ("1" == sNavBarOverride) {
                    hasNav = false
                } else if ("0" == sNavBarOverride) {
                    hasNav = true
                }
                hasNav
            }
            else -> // fallback
                !ViewConfiguration.get(context).hasPermanentMenuKey()
        }
    }

第二步,处理Activity的Window的Flag属性

override fun onCreate(...){
    window.requestFeature(Window.FEATURE_NO_TITLE)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                   
  window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
                window.statusBarColor = Color.TRANSPARENT
                window.navigationBarColor = Color.BLACK
            }
}

第三步,添加视图到Activity的RootView(DecorView)中

  setContentView(
                        contentView,
                        WindowManager.LayoutParams(
                            WindowManager.LayoutParams.MATCH_PARENT,
                            WindowManager.LayoutParams.MATCH_PARENT
                        )
                    )

第四步,动态监听布局变化,动态设置导航栏高度

/** 根据导航栏高度判断页面布局 **/
    private fun setContentViewLayoutParamsByNavigationBarHeight() {
        val navigationBarHeight by lazy { ApkUtil.getNavigationBarHeight(this@PActivity) }
        val lp: FrameLayout.LayoutParams?
        if (navigationBarHolderView == null) {
            navigationBarHolderView = View(this)
            navigationBarHolderView?.setBackgroundColor(Color.BLACK)
            lp = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, navigationBarHeight)
            lp.gravity = Gravity.BOTTOM
            [email protected](navigationBarHolderView, lp)
        } else {
            lp = navigationBarHolderView?.layoutParams as? FrameLayout.LayoutParams
            lp?.height = navigationBarHeight
            navigationBarHolderView?.layoutParams = lp
        }
        binder.root.setPadding(0, 0, 0, navigationBarHeight)
    }

    override fun onGlobalLayout() {
        if (isContentViewAttachScreenTop)
            setContentViewLayoutParamsByNavigationBarHeight()
    }

    override fun onResume() {
        if (isContentViewAttachScreenTop)
            rootView.viewTreeObserver.addOnGlobalLayoutListener(this)
        super.onResume()
    }

    override fun onStop() {
        super.onStop()
        if (isContentViewAttachScreenTop) {
            @Suppress("DEPRECATION")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
            else
                rootView.viewTreeObserver.removeGlobalOnLayoutListener(this)
        }
    }

先写成这个样子,后面我有时间会再来修改的!经过一段时间的使用,发现以上的代码并不能很好的解决问题,于是又改进了一套,这次使用的经过了考验,感觉不错!

open class PopcornActivity : AppCompatActivity() {

    protected lateinit var ctx: Context
    protected lateinit var activity: Activity
    protected lateinit var rootView: FrameLayout
    protected lateinit var binder: B
    protected var imgBackground: ImageView? = null

    /** 状态栏的高度 **/
    protected val statusBarHeight by lazy { ApkUtil.getStatusBarHeight(this) }

    /** ActionBar的高度 **/
    protected val actionBarHeight by lazy { resources.getDimensionPixelSize(R.dimen.abc_action_bar_default_height_material) }

    /** 布局文件是否顶到屏幕的顶端,即:状态栏是透明可见的,能显示布局文件, 且@ContentView中statusBarColor参数失效。默认值为false **/
    protected open val isContentViewAttachScreenTop: Boolean = false

    /** 标识参数,方式ViewTree多次添加GlobalListener **/
    private val isSetContentViewAttachScreenTopTag: AtomicBoolean = AtomicBoolean(false)

    /** 导航栏的颜色资源ID,默认为黑色[android.R.color.black] **/
    protected var navigationBarColorResId: Int = android.R.color.black
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ctx = this; activity = this; rootView = window.decorView as FrameLayout
        if (isContentViewAttachScreenTop) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                window.statusBarColor = Color.TRANSPARENT
                rootView.systemUiVisibility =
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            }
        }
        initActivityUI() //初始化ActivityUI视图
        EventBus.getDefault().register(this) //注册Activity
    }

    /** 初始化ActivityUI视图 **/
    @SuppressLint("InlinedApi")
    private fun initActivityUI() {
        val value = ViewUtil.inflateLayoutId(this)
        when {
            value?.size == 3 -> {
                navigationBarColorResId = value[2] //获取导航栏的颜色资源ID
                binder = DataBindingUtil.setContentView(activity, value[0])
                setToolbar() //设置Toolbar的相关属性
                setImmersiveStatusBar(value[1]) //设置沉浸式状态栏
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP)
                    window.navigationBarColor =
                        ContextCompat.getColor(this, navigationBarColorResId)
            }
            else -> {
                throw Exception("错误:Cannot find layoutId & StatusBarResId")
            }
        }
    }

    /** 针对有Toolbar对象的Activity,点击Toolbar返回图标后的操作 **/
    protected open fun onNavigationBackClick() =
        Router.back(this, enterAnimRes = R.anim.slide_in_left, exitAnimRes = R.anim.slide_out_right)

    override fun onBackPressed() {
        with(javaClass.getAnnotation(BackHandler::class.java)) {
            if (this != null) {
                finish()
                overridePendingTransition(
                    when {
                        enterAnimResId < 0 || enterAnimResId == android.R.anim.slide_in_left -> R.anim.slide_in_left
                        else -> enterAnimResId
                    },
                    when {
                        exitAnimResId < 0 || exitAnimResId == android.R.anim.slide_out_right -> R.anim.slide_out_right
                        else -> exitAnimResId
                    }
                )
                return
            }
        }
        super.onBackPressed()
    }

    /** 接收到通知关闭当前的页面的事件,默认为关闭页面 **/
    @Subscribe
    open fun onReceivedActivityFinishEvent(event: ActivityFinishEvent) = finish()

    override fun onDestroy() {
        EventBus.getDefault().unregister(this)
        super.onDestroy()
    }

    /** 设置Toolbar对象 **/
    private fun setToolbar() {
        try {
            val toolbarId = ApkUtil.getIdentifierId(this, "toolbar", "id")
            when (toolbarId > 0) {
                true -> {
                    val toolbar = findViewById(toolbarId)
                    toolbar?.let {
                        it.title = ""
                        setSupportActionBar(it)
                        it.setNavigationOnClickListener { onNavigationBackClick() }
                    }
                }
                else -> Log.i(javaClass.simpleName, "Toolbar is not exists!")
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Log.i(
                javaClass.simpleName,
                "Something is erro, when the program execute a method called setToolbar!"
            )
        }
    }

    /**
     * 设置背景图片资源
     * @param imgUri 图片资源
     */
    protected fun setBackgroundImage(imgUri: Any): Unit = setBackgroundImage(imgUri, null)

    /**
     * 设置背景图片资源
     * @param imgUri          图片资源
     * @param onClickListener 点击事件
     */
    protected fun setBackgroundImage(imgUri: Any, onClickListener: View.OnClickListener?) {
        if (imgBackground == null) {
            imgBackground = ImageView(this)
            rootView.addView(
                imgBackground, 0,
                FrameLayout.LayoutParams(
                    FrameLayout.LayoutParams.MATCH_PARENT,
                    FrameLayout.LayoutParams.MATCH_PARENT
                )
            )
        }
        when (imgUri) {
            is Drawable -> imgBackground?.setImageDrawable(imgUri)
            is Int -> imgBackground?.setImageResource(imgUri)
            is File -> {
                imgBackground?.setImageURI(
                    when {
                        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> FileProvider.getUriForFile(
                            this,
                            "$packageName.FileProvider",
                            imgUri
                        )
                        else -> Uri.fromFile(imgUri)
                    }
                )
            }
            is Bitmap -> imgBackground?.setImageBitmap(imgUri)
            is String -> ImageLoader.displayImage(this, uri = imgUri, target = imgBackground!!)
        }
        imgBackground?.scaleType = ImageView.ScaleType.CENTER_CROP
        if (onClickListener != null)
            imgBackground?.setOnClickListener(onClickListener)
        }
    }

       /**謤
     * 设置沉浸式状态栏
     * @param colorResId 状态栏颜色
     */
    @SuppressLint("ObsoleteSdkInt")
    protected fun setImmersiveStatusBar(colorResId: Int) {
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
                val statusBarColor = ApkUtil.getColor(this, colorResId)
                window.statusBarColor = statusBarColor //设置状态栏颜色
                if (statusBarColor == Color.WHITE || statusBarColor == Color.TRANSPARENT) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) rootView.systemUiVisibility =
                        rootView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                    else window.setFlags(
                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                    )
                }
     }

}

你可能感兴趣的:(Android - 让视图文件显示到状态栏下,且不受底部导航虚拟按键影响)