androidkotlinandroid-cardviewandroid-statusbar

Layout (Cardview) overlapping below Status bar


I'm trying to show notifications using Card view with animations but status bar closing it!

I don't know what the problem is. I have tried several ways to solve the problem (listed below).

I also tried correcting using fromDeltaX and toDeltaX in the animation to no avail

You can see an image of this behavior:

current result

I tried (not working):

1.

ViewCompat.setOnApplyWindowInsetsListener(notificationView) { view, insets ->
            val systemInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            view.setPadding(0, systemInsets.top, 0, 0)
            insets
        }

2.

@SuppressLint("InternalInsetResource")
    private fun getStatusBarHeight(activity: Activity): Int {
        val resourceId = activity.resources.getIdentifier("status_bar_height", "dimen", "android")
        return if (resourceId > 0) activity.resources.getDimensionPixelSize(resourceId) else 0
    }

3.

fitsSystemWindows = true

My Code:

import android.app.Activity
import android.os.Handler
import android.os.Looper
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.FrameLayout
import android.widget.TextView
import androidx.cardview.widget.CardView

object NotificationUtil {

    fun showSuccessMessage(activity: Activity, message: String,autoClose: Boolean = true) {
        showNotification(activity, message, R.layout.item_success_snackbar,autoClose)
    }

    fun showErrorMessage(activity: Activity, message: String,autoClose: Boolean = true) {
        showNotification(activity, message, R.layout.item_error_snackbar,autoClose)
    }

    private fun showNotification(activity: Activity, message: String, layoutId: Int,autoClose: Boolean) {
        val decorView = activity.window.decorView as ViewGroup

        val inflater = LayoutInflater.from(activity)
        val notificationView = inflater.inflate(layoutId, null) as CardView

        val layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
        }

        val tvMessage: TextView = notificationView.findViewById(if (layoutId == R.layout.item_success_snackbar) R.id.tv_success_message else R.id.tv_error_message)
        val tvOk: TextView = notificationView.findViewById(if (layoutId == R.layout.item_success_snackbar) R.id.tv_success_ok else R.id.tv_error_ok)

        notificationView.layoutParams = layoutParams
        tvMessage.text = message

        notificationView.setOnClickListener {
            hideNotification(decorView, notificationView)
        }

        tvOk.setOnClickListener {
            hideNotification(decorView, notificationView)
        }

        decorView.addView(notificationView)

        val slideDown = AnimationUtils.loadAnimation(activity, R.anim.slide_down)
        notificationView.startAnimation(slideDown)

        if (autoClose) {
            Handler(Looper.getMainLooper()).postDelayed({
                hideNotification(decorView, notificationView)
            }, 3000)
        }
    }

    private fun hideNotification(decorView: ViewGroup, notificationView: View) {
        val slideUp = AnimationUtils.loadAnimation(notificationView.context, R.anim.slide_up)
        slideUp.setAnimationListener(object : Animation.AnimationListener {
            override fun onAnimationStart(animation: Animation?) {}

            override fun onAnimationEnd(animation: Animation?) {
                decorView.removeView(notificationView)
            }

            override fun onAnimationRepeat(animation: Animation?) {}
        })
        notificationView.startAnimation(slideUp)
    }
}

I need to show this below status bar and top of all layouts. Thanks in advance for the answers.


Solution

  • To update the top margin of your notificationView to account for the system insets (like the status bar), you can use the WindowInsets API. Here are two solutions: one using the latest WindowInsets API and another if you already have the status bar size.

    Solution 1 - Using WindowInsets API

    Step 01: Create a method to fetch window insets

    First, create a method in your View extension to fetch the window insets:

    import android.view.View
    import androidx.core.graphics.Insets
    import androidx.core.view.ViewCompat
    import androidx.core.view.WindowInsetsCompat
    
    fun View.fetchWindowInsets(block: (Insets) -> Unit) {
        ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
            val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            block.invoke(inset)
            WindowInsetsCompat.CONSUMED
        }
    }
    

    Step 2: Update the showNotification method

    Now, update the showNotification method to adjust the top margin of notificationView using the insets:

    Put that code below of line notificationView.layoutParams = layoutParams

    import androidx.core.view.updateMargins
    
    notificationView.layoutParams = layoutParams
    // Updated Code...
    notificationView.fetchWindowInsets { inset ->
        layoutParams.updateMargins(0, inset.top, 0, 0)
    }
    // ...
    tvMessage.text = message
    

    Oh! Great, you successfully executed the code.


    Solution 2 - Using Predefined Status Bar Size

    If you already know the size of the status bar in pixels, you can directly update the top margin:

    - Update the showNotification method with a status bar size parameter.

    import androidx.core.view.updateMargins
    
    val layoutParams = FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.WRAP_CONTENT
    ).apply {
        gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
        // Updated Code...
        updateMargins(0, YOUR_STATUS_BAR_SIZE, 0, 0)
        // ...
    }
    

    This should help you correctly adjust the top margin of your notificationView based on the system insets or a predefined size, - Thankyou