Search code examples
androidandroid-fragmentsnavigationandroid-custom-view

Android <include> layout change after navigateUp()


I have an activity that has two fragments. I have created a layout that is reusable as following:

Here is the xml of custom layout

<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<androidx.appcompat.widget.AppCompatTextView
    android:id="@+id/tvFormTitle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@color/black1"
    android:textSize="@dimen/ssp_10"
    android:visibility="visible"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:text="Email" />

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/clForm"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/sdp_4"
    android:background="@drawable/sh_all_round_form"
    android:paddingStart="@dimen/sdp_12"
    android:paddingEnd="@dimen/sdp_12"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/tvFormTitle">

    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/etForm"
        style="@style/et_form"
        android:layout_width="0dp"
        android:layout_weight="1"
        app:layout_constraintEnd_toStartOf="@+id/ivSwitchPassword"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/ivSwitchPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:paddingStart="@dimen/sdp_8"
        android:paddingTop="@dimen/sdp_8"
        android:paddingEnd="@dimen/sdp_2"
        android:paddingBottom="@dimen/sdp_8"
        android:src="@drawable/ic_eye"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/etForm"
        app:layout_constraintEnd_toStartOf="@+id/ivCancel"
        app:layout_constraintTop_toTopOf="@+id/etForm" />

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/ivCancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:paddingStart="@dimen/sdp_12"
        android:paddingTop="@dimen/sdp_12"
        android:paddingBottom="@dimen/sdp_12"
        android:src="@drawable/white_close"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/etForm"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@+id/etForm"
        app:tint="@color/grey_shade5" />
</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.appcompat.widget.AppCompatTextView
    android:id="@+id/tvMessage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/sdp_5"
    android:textColor="@color/colorError"
    android:textSize="@dimen/ssp_10"
    android:visibility="visible"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/clForm"
    tools:text="This field is required" />

</androidx.constraintlayout.widget.ConstraintLayout>

I am using this custom la in Fragment A as follow:

                <include
                    android:id="@+id/email"
                    layout="@layout/layout_form_edittext"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/sdp_36"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/tvDescription" />

                <include
                    android:id="@+id/password"
                    layout="@layout/layout_form_edittext"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/sdp_12"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/email" />

I am using Fragment navigation to move from Fragment A to B. Entering values in email and password and move to Fragment B and get back to Fragment A by pressing back or navigateUp(). Email field contains the value of password field.

Edit:

Same layout it used in Custom Compound class as following getting same issue.

class FormView @JvmOverloads
constructor(
private val ctx: Context,
private val attributeSet: AttributeSet? = null,
private val defStyleAttr: Int = 0
) : ConstraintLayout(ctx, attributeSet, defStyleAttr) {

private var formTitle = ""
private var passwordType = false
private var showMessage = false

init {
    val inflater = ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
    val attributes = ctx.obtainStyledAttributes(attributeSet, R.styleable.FormView)
    attributes.getString(R.styleable.FormView_formTitle)?.let { formTitle = it }
    passwordType = attributes.getBoolean(R.styleable.FormView_passwordType, passwordType)
    showMessage = attributes.getBoolean(R.styleable.FormView_showErrorMessage, false)
    attributes.recycle()

    inflater.inflate(R.layout.layout_form_edittext, this)

    setTitle(formTitle)
    setPasswordType(passwordType)
    errorMessageVisibility(showMessage)
    borderVisibility(false)


    ivSwitchPassword.setOnClickListener {
        updatePasswordVisibility()
    }

    ivCancel.setOnClickListener {
        etForm.text?.clear()
        clearError()
    }
}

fun showError(msg: String) {
    msg?.let {
        tvMessage.text = it
        cancelBtnVisibility(true)
        errorMessageVisibility(true)
        borderVisibility(true)
    }
}

fun clearError() {
    tvMessage.text = ""
    cancelBtnVisibility(false)
    errorMessageVisibility(false)
    borderVisibility(false)
}

private fun errorMessageVisibility(flag: Boolean) {
    if (flag) tvMessage.visible() else tvMessage.gone()
}

private fun cancelBtnVisibility(flag: Boolean) {
    if (flag) ivCancel.visible() else ivCancel.gone()
}

private fun borderVisibility(flag: Boolean) {
    val drawable = clForm.background as GradientDrawable
    if(flag) drawable.setStroke(1, Color.parseColor("#c32329"))
    else drawable.setStroke(0, Color.TRANSPARENT)
}

fun setPasswordType(passwordType: Boolean) {
    if(!passwordType) ivSwitchPassword.gone()
    else {
        ivSwitchPassword.visible()
        etForm.inputType = InputTypeUtils.getInputTypeByProperty("textPassword")
    }
}

fun setTitle(title: String) {
    tvFormTitle.text = title
}

private fun updatePasswordVisibility() {
    if (etForm.transformationMethod is PasswordTransformationMethod) {
        etForm.transformationMethod = null
    } else {
        etForm.transformationMethod = PasswordTransformationMethod()
    }
    etForm.setSelection(etForm.length())
}
}

Solution

  • This is because include edit text has one id for both field, solution is to generate new id add this code on your onViewCreated method:

    email.etForm.id = View.generateViewId()
    password.etForm.id = View.generateViewId()