androidkotlinmobilesnackbar

Text is not displayed when I remove and reapply the layout in Snackbar


When I am trying to remove the snackbar's parent layout and recreate the snackbar with the same layout, the value of textView comes out blank.

I have also tried debugging the same, while using the debugger I am getting the text string but it is not displayed in the snackbar at runtime.

Here is the XML file of my custom SnackBar :

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@drawable/dr_toast_round_corners_10dp_red"
    android:id="@+id/toast_container">
    
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_error"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/ic_error_red"
        android:padding="@dimen/dimen_4dp"
        android:layout_marginStart="@dimen/dimen_16dp"
        android:layout_marginVertical="@dimen/dimen_12dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_invalid_credentials"
        android:layout_width="@dimen/dimen_0dp"
        android:layout_height="wrap_content"
        android:paddingHorizontal="@dimen/dimen_16dp"
        android:fontFamily="@font/inter_regular"
        tools:text="@string/invalid_credentials_login"
        android:textColor="@color/text_body_light"
        android:textSize="@dimen/dimen_13sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/iv_close"
        app:layout_constraintStart_toEndOf="@+id/iv_error"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/ic_close"
        android:padding="@dimen/dimen_4dp"
        android:layout_marginEnd="@dimen/dimen_24dp"
        android:layout_marginVertical="@dimen/dimen_12dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

And here is the SnackBar's code :

fun showCustomErrorSnackbar(view: ViewGroup, activity: Activity, message: String) {
    snackbar = Snackbar.make(view, "", Snackbar.LENGTH_INDEFINITE)
    snackbar.duration = 2000

    val customLayout =
        activity.layoutInflater.inflate(
            R.layout.layout_custom_toast,
            activity.findViewById(R.id.toast_container)
        )
    snackbar.view.setBackgroundColor(Color.TRANSPARENT)
    val textview = customLayout.findViewById<AppCompatTextView>(R.id.tv_invalid_credentials)
    textview.text = message

    // now change the layout of the snackbar
    val snackbarLayout = snackbar.view as Snackbar.SnackbarLayout
    snackbarLayout.removeAllViewsInLayout()
    // set padding of the all corners as 0
    customLayout.setPadding(0, 0, 0, 0)
    val layoutParameters = FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.WRAP_CONTENT,
        FrameLayout.LayoutParams.WRAP_CONTENT
    )
    layoutParameters.gravity = Gravity.CENTER_HORIZONTAL
    layoutParameters.setMargins(24, 16, 24, 0) //left = 24, top = 16, right = 24, bottom = 0
    snackbarLayout.layoutParams = layoutParameters
    val closeSnackbar = customLayout.findViewById<AppCompatImageView>(R.id.iv_close)
    closeSnackbar.setOnClickListener {
        snackbar.dismiss()
    }
    val parent = customLayout.parent as? ViewGroup
    parent?.removeView(customLayout)

    snackbarLayout.addView(customLayout, 0)
    snackbar.show()
}

For the first time the snackbar is shown like this :

Snackbar for the first time

But soon after it is displayed like this :

Second Time Snackbar(Actual issue)

Can anyone help me in solving this?

I want the snackbar to perform perfectly without skipping the texts or the layout. And Also I want to close the snackbar at the cross button click listener.


Solution

  • I have tried the code snippet you shared. A few refactorings and it was working perfectly for me. Below is the code that worked for me.

        fun showCustomErrorSnackbar(
        view: View,
        message: String,
        isFragmentView: Boolean
    ) {
        if (!::customLayout.isInitialized) {
            customLayout = layoutInflater.inflate(
                R.layout.layout_custom_toast,
                requireActivity().findViewById(R.id.toast_container)
            )
        }
        val snackbar = Snackbar.make(view, "", Snackbar.LENGTH_INDEFINITE).apply { duration = 1500 }
        (customLayout.parent as? ViewGroup)?.removeView(customLayout)
    
        snackbar.view.setBackgroundColor(Color.TRANSPARENT)
        customLayout.findViewById<AppCompatTextView>(R.id.tv_invalid_credentials).text = message
        customLayout.findViewById<AppCompatImageView>(R.id.iv_close)
            .setOnClickListener { snackbar.dismiss() }
    
        val snackbarLayout = snackbar.view as Snackbar.SnackbarLayout
        snackbarLayout.setPadding(0, 0, 0, 0)
        (snackbarLayout.layoutParams as FrameLayout.LayoutParams).apply {
            width = ViewGroup.LayoutParams.MATCH_PARENT
            gravity = Gravity.TOP
        }
    
        val layoutParameters = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            gravity = Gravity.CENTER_HORIZONTAL
            setMargins(24, if (isFragmentView) 16 else 128, 24, 0)
        }
        customLayout.layoutParams = layoutParameters
    
        snackbarLayout.addView(customLayout, 0)
        snackbar.show()
    }
    

    Only a small refactoring is required in your code, instead of removing the view at the bottom before showing the snackbar. Try removing it at the top, once you initialize the snackbar using the Snackbar.make() method.