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:
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.
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.
WindowInsets
APIFirst, 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
}
}
showNotification
methodNow, 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.
If you already know the size of the status bar in pixels, you can directly update the top margin:
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