title: Handle Orientation Changed on CustomView Two Way Data Binding with Live Data in ViewModel
body: <p>I'm now creating customview that extends to ContraintLayout with EditText as child View. I have success the Two Way Data Binding on normal case, but when I try to rotate the screen, the edittext not auto set the value from the Live Data. I thought there was a problem with 2 Way Data Binding, but when I try with normal EditText is run normally like what I want to achieve.</p>
  <pre><code>    class MyEditField @JvmOverloads constructor(
          context: Context,
          attrs: AttributeSet,
          defStyleAttr: Int = 0,
      ) :
          ConstraintLayout(context, attrs, defStyleAttr) {
      
          private lateinit var mEditText: EditText
          private lateinit var mTextViewTitle: TextView
          private lateinit var mTextViewSubtitle: TextView
          private lateinit var mImageViewSubtitle: ImageView
          private lateinit var mTextViewRequired: TextView
          private lateinit var mTextViewError: TextView
          private lateinit var mImageViewError: ImageView
          private lateinit var mImageViewEndIcon: ImageView
          private lateinit var mImageViewStartIcon: ImageView
          private lateinit var mTextViewStart: TextView
          private lateinit var mProgressBar: ProgressBar
          private lateinit var mTextViewSuccessInformation: TextView
          private lateinit var mTextViewSuccess: TextView
          private lateinit var mViewSeparator: View
      
          var text: String = &quot;&quot;
              set(value) {
                  field = value
                  setTextEditable(value)
              }
      
          @ColorRes
          var textColor: Int = -1
              set(value) {
                  field = value
                  setEditTextFontColor(value)
              }
      
          var title: String = &quot;&quot;
              set(value) {
                  field = value
                  mTextViewTitle.text = value
              }
      
          var subtitle: String = &quot;&quot;
              set(value) {
                  field = value
                  setAndShowHideSubtitle(value)
              }
      
          var hint: String = &quot;&quot;
              set(value) {
                  field = value
                  setHintText(value)
              }
      
          var requiredText: String = &quot;&quot;
              set(value) {
                  field = value
                  setRequiredTextView(value)
              }
      
          var startText: String? = null
              set(value) {
                  field = value
                  setStartTextView(value)
                  showHideSeparator()
              }
      
          @ColorRes
          var startTextColor: Int = -1
              set(value) {
                  field = value
                  setStartTextFontColor(value)
              }
      
      
          var startTextPaddingStart: Int = -1
              set(value) {
                  field = value
                  setStartTextViewMargin()
              }
      
          var startTextPaddingEnd: Int = -1
              set(value) {
                  field = value
                  setStartTextViewMargin()
              }
      
          var isRequired: Boolean = false
              set(value) {
                  field = value
                  showHideTextRequired(value)
              }
      
          var errorText: String? = null
              set(value) {
                  field = value
                  setAndShowHideError(value)
              }
      
          var successText: String? = null
              set(value) {
                  field = value
                  setAndShowHideSuccessText(value)
              }
      
          var successInformationText: String? = null
              set(value) {
                  field = value
                  setAndShowHideSuccessInformationText(value)
              }
      
          var inputType: Int = EditorInfo.TYPE_TEXT_VARIATION_NORMAL
              set(value) {
                  field = value
                  setInputTypeEditText(value)
              }
      
          @DrawableRes
          var endIcon: Int = -1
              set(value) {
                  field = value
                  setAndShowImageEditText(value)
              }
      
          var endIconTint: ColorStateList? = null
              set(value) {
                  field = value
                  setImageViewEndIconTint(value)
              }
      
          @DrawableRes
          var startIcon: Int = -1
              set(value) {
                  field = value
                  setAndShowImageStart(value)
                  showHideSeparator()
              }
      
          var startIconTint: ColorStateList? = null
              set(value) {
                  field = value
                  setImageViewStartIconTint(value)
              }
      
      
          var isLoading: Boolean = false
              set(value) {
                  field = value
                  showLoading(value)
              }
      
          var enabledInput: Boolean = true
              set(value) {
                  field = value
                  setEnabledEditText(value)
              }
      
          var imeOptions: Int = EditorInfo.IME_NULL
              set(value) {
                  field = value
                  setImeOptionEditText(value)
              }
      
          var isAllCaps: Boolean = false
              set(value) {
                  field = value
                  setFiltersInternal()
              }
      
          var maxLength: Int = -1
              set(value) {
                  field = value
                  setFiltersInternal()
              }
      
          var isPassword: Boolean = false
              set(value) {
                  field = value
                  setInputAsPassword(value)
              }
          init {
              val v = inflate(context, R.layout.layout_my_edit_field, this)
              v?.let {
                  assignView(it)
              }
              context.theme.obtainStyledAttributes(
                  attrs,
                  R.styleable.MyEditField,
                  defStyleAttr,
                  0
              ).apply {
                  title = getString(R.styleable.MyEditField_title) ?: &quot;&quot;
      
                  subtitle = getString(R.styleable.MyEditField_subtitle) ?: &quot;&quot;
      
                  hint = getString(R.styleable.MyEditField_hint) ?: &quot;&quot;
      
                  requiredText = getString(R.styleable.MyEditField_requiredText) ?: &quot;*&quot;
      
                  errorText = getString(R.styleable.MyEditField_error)
      
                  endIcon = getResourceId(R.styleable.MyEditField_end_icon, -1)
      
                  endIconTint = getColorStateList(R.styleable.MyEditField_end_icon_tint)
      
                  isLoading = getBoolean(R.styleable.MyEditField_is_loading, false)
      
                  successText = getString(R.styleable.MyEditField_success)
      
                  successInformationText = getString(R.styleable.MyEditField_success_information)
      
                  text = getString(R.styleable.MyEditField_text) ?: &quot;&quot;
      
                  textColor = getResourceId(R.styleable.MyEditField_text_color, -1)
      
                  startText = getString(R.styleable.MyEditField_start_text)
      
                  startTextPaddingStart =
                      getDimensionPixelOffset(R.styleable.MyEditField_start_text_padding_start, -1)
      
                  startTextPaddingEnd =
                      getDimensionPixelOffset(R.styleable.MyEditField_start_text_padding_end, -1)
      
                  startTextColor = getResourceId(R.styleable.MyEditField_start_text, -1)
      
                  startIcon = getResourceId(R.styleable.MyEditField_start_icon, -1)
      
                  startIconTint = getColorStateList(R.styleable.MyEditField_start_icon_tint)
      
                  enabledInput = getBoolean(R.styleable.MyEditField_enabled, true)
      
                  inputType = getInt(
                      R.styleable.MyEditField_android_inputType,
                      EditorInfo.TYPE_TEXT_VARIATION_NORMAL
                  )
                  imeOptions =
                      getInt(R.styleable.MyEditField_android_imeOptions, EditorInfo.IME_NULL)
                  isPassword = getBoolean(R.styleable.MyEditField_is_password, false)
                  this.recycle()
              }
      
      
          }
      
      
          private fun setStartTextFontColor(value: Int) {
              mTextViewStart.setTextColor(
                  ContextCompat.getColor(
                      context,
                      if (value == -1) R.color.black_text else value
                  )
              )
          }
      
          private fun setEditTextFontColor(value: Int) {
              mEditText.setTextColor(
                  ContextCompat.getColor(
                      context,
                      if (value == -1) R.color.black_text else value
                  )
              )
      
          }
      
          private fun setFiltersInternal() {
              val arrayOfInputFilters = arrayListOf&lt;InputFilter&gt;()
              if (isAllCaps) arrayOfInputFilters.add(InputFilter.AllCaps())
              if (maxLength &gt; -1) arrayOfInputFilters.add(InputFilter.LengthFilter(maxLength))
              if (arrayOfInputFilters.isNotEmpty()) mEditText.filters = arrayOfInputFilters.toTypedArray()
          }
      
          private fun setEnabledEditText(value: Boolean) {
              mEditText.enableView(value)
              (mEditText.parent as LinearLayoutCompat).enableView(value)
          }
      
      
          private fun setAndShowHideSubtitle(value: String) {
              mTextViewSubtitle.text = value
              if (value.isEmpty()) {
                  mTextViewSubtitle.gone()
                  mImageViewSubtitle.gone()
              } else {
                  mTextViewSubtitle.visible()
                  mImageViewSubtitle.visible()
              }
          }
      
          private fun showHideTextRequired(value: Boolean) {
              mTextViewRequired.visibility = if (value) View.VISIBLE else View.GONE
          }
      
          private fun setStartTextViewMargin() {
              var paddingStart = mTextViewStart.paddingStart
              var paddingEnd = mTextViewStart.paddingEnd
              if (startTextPaddingStart != -1) {
                  paddingStart = startTextPaddingStart
              }
              if (startTextPaddingEnd != -1) {
                  paddingEnd = startTextPaddingEnd
              }
              mTextViewStart.setPadding(
                  paddingStart,
                  mTextViewStart.paddingTop,
                  paddingEnd,
                  mTextViewStart.paddingBottom
              )
          }
      
      
          private fun setAndShowHideError(value: String?) {
              mTextViewError.text = value
              if (value.isNullOrEmpty()) {
                  mTextViewError.gone()
                  mImageViewError.gone()
              } else {
                  mTextViewError.visible()
                  mImageViewError.visible()
              }
          }
      
          private fun setAndShowImageEditText(@DrawableRes value: Int) {
              if (value != -1) {
                  mImageViewEndIcon.setImageDrawable(ContextCompat.getDrawable(context, value))
                  mImageViewEndIcon.visible()
              } else {
                  mImageViewEndIcon.gone()
              }
          }
      
          private fun setAndShowImageStart(@DrawableRes value: Int) {
              if (value != -1) {
                  mImageViewStartIcon.setImageDrawable(ContextCompat.getDrawable(context, value))
                  mImageViewStartIcon.visible()
              } else {
                  mImageViewStartIcon.gone()
              }
          }
      
          private fun setImageViewEndIconTint(value: ColorStateList?) {
              mImageViewEndIcon.imageTintList = value
          }
      
          private fun setImageViewStartIconTint(value: ColorStateList?) {
              mImageViewStartIcon.imageTintList = value
          }
      
          private fun showLoading(value: Boolean) {
              if (value) {
                  mProgressBar.visible()
              } else {
                  mProgressBar.gone()
              }
          }
      
          private fun setAndShowHideSuccessText(value: String?) {
              mTextViewSuccess.text = value
              if (value.isNullOrEmpty()) {
                  mTextViewSuccess.gone()
              } else {
                  mTextViewSuccess.visible()
              }
          }
      
          private fun setAndShowHideSuccessInformationText(value: String?) {
              mTextViewSuccessInformation.text = value
              if (value.isNullOrEmpty()) {
                  mTextViewSuccessInformation.gone()
              } else {
                  mTextViewSuccessInformation.visible()
                  mTextViewSuccessInformation.updateLayoutParams&lt;LayoutParams&gt; {
                      marginStart = if (value.isNullOrEmpty()) {
                          0.pxTodp()
                      } else {
                          context.resources.getDimensionPixelOffset(R.dimen.dimens_4dp).pxTodp()
                      }
                  }
              }
          }
      
          private fun showHideSeparator() {
              if (startIcon != -1 || !startText.isNullOrEmpty()) {
                  mViewSeparator.visible()
              } else {
                  mViewSeparator.gone()
              }
          }
      
      
          private fun setRequiredTextView(value: String) {
              mTextViewRequired.text = value
          }
      
          private fun setStartTextView(value: String?) {
              mTextViewStart.text = value
              if (value.isNullOrEmpty()) {
                  mTextViewStart.gone()
              } else {
                  mTextViewStart.visible()
              }
          }
      
      
          private fun setHintText(value: String) {
              mEditText.hint = value
          }
      
      
          private fun setTextEditable(value: String) {
              mEditText.setText(value)
          }
      
          private fun setInputAsPassword(value: Boolean) {
              mEditText.transformationMethod =
                  if (value) PasswordTransformationMethod.getInstance() else null
          }
      
          private fun setInputTypeEditText(inputType: Int) {
              mEditText.inputType = inputType
          }
      
      
          private fun setImeOptionEditText(value: Int) {
              mEditText.imeOptions = value
          }
      
      
          private fun assignView(v: View) {
              mEditText = v.findViewById(R.id.my_et)
              mTextViewTitle = v.findViewById(R.id.tv_title)
              mTextViewSubtitle = v.findViewById(R.id.tv_subtitle)
              mImageViewSubtitle = v.findViewById(R.id.iv_subtitle)
              mTextViewRequired = v.findViewById(R.id.tv_required)
              mTextViewError = v.findViewById(R.id.tv_error_edit_text)
              mImageViewError = v.findViewById(R.id.iv_error_edit_text)
              mImageViewEndIcon = v.findViewById(R.id.iv_edit_text)
              mProgressBar = v.findViewById(R.id.progress_loading)
              mTextViewSuccess = v.findViewById(R.id.tv_success_edit_text)
              mTextViewSuccessInformation = v.findViewById(R.id.tv_information_success_edit_text)
              mImageViewStartIcon = v.findViewById(R.id.iv_left_et)
              mTextViewStart = v.findViewById(R.id.tv_left_et)
              mViewSeparator = v.findViewById(R.id.v_et_split)
          }
      
          fun getEditText(): EditText {
              return mEditText
          }
      
      
      }
  </code></pre>
  <p>Here my code the BindingAdapter</p>
  <pre><code>object MyEditFieldBindingAdapter {
      @JvmStatic
      @BindingAdapter(value = [&quot;app:textAttrChanged&quot;])
      fun MyEditField.setListener(
          textAttrChanged: InverseBindingListener?
      ) {
          if (textAttrChanged != null) {
              getEditText().addTextChangedListener(object : TextWatcher {
                  override fun beforeTextChanged(
                      charSequence: CharSequence,
                      i: Int,
                      i1: Int,
                      i2: Int
                  ) {
                  }
  
                  override fun onTextChanged(
                      charSequence: CharSequence,
                      i: Int,
                      i1: Int,
                      i2: Int
                  ) {
                  }
  
                  override fun afterTextChanged(editable: Editable) {
                      textAttrChanged.onChange()
                  }
              })
          }
      }
  
      @JvmStatic
      @BindingAdapter(&quot;app:text&quot;)
      fun MyEditField.setText(value: String) {
          val old = this.getEditText().text.toString()
          if (old != value) {
              this.getEditText().setText(value)
          }
      }
  
      @JvmStatic
      @InverseBindingAdapter(attribute = &quot;app:text&quot;, event = &quot;app:textAttrChanged&quot;)
      fun MyEditField.getText(): String {
          return getEditText().text.toString()
      }
  }
  </code></pre>
  <p>For the implement
  <em>activity.xml</em></p>
  <pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
  &lt;layout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
      xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
      xmlns:tools=&quot;http://schemas.android.com/tools&quot;&gt;
  
      &lt;data&gt;
  
          &lt;variable
              name=&quot;viewModel&quot;
              type=&quot;com.android.example.TestViewModel&quot; /&gt;
      &lt;/data&gt;
  
      &lt;androidx.appcompat.widget.LinearLayoutCompat
          android:layout_width=&quot;match_parent&quot;
          android:layout_height=&quot;match_parent&quot;
          android:orientation=&quot;vertical&quot;
          tools:context=&quot;.pages.nonfasoprospect.NonFasoProspectActivity&quot;&gt;
  
          &lt;com.android.example.MyEditField
              android:id=&quot;@+id/test&quot;
              android:layout_width=&quot;match_parent&quot;
              android:layout_height=&quot;wrap_content&quot;
              app:end_icon=&quot;@drawable/ic_calendar&quot;
              app:error=&quot;@string/lunas&quot;
              app:required=&quot;true&quot;
              app:requiredText=&quot;*&quot;
              android:inputType=&quot;number&quot;
              android:imeOptions=&quot;actionDone&quot;
              app:start_text_padding_end=&quot;@dimen/dimens_16dp&quot;
              app:start_text_padding_start=&quot;@dimen/dimens_16dp&quot;
              app:start_text=&quot;+62&quot;
              app:subtitle=&quot;@{viewModel.editLiveData1}&quot;
              app:text=&quot;@={viewModel.editLiveData1}&quot;
              app:title=&quot;Test 1&quot; /&gt;
  
          &lt;com.android.example.MyEditField
              android:id=&quot;@+id/test2&quot;
              android:layout_width=&quot;match_parent&quot;
              android:layout_height=&quot;wrap_content&quot;
              app:error=&quot;@string/lunas&quot;
              app:required=&quot;true&quot;
              app:requiredText=&quot;*&quot;
              app:start_icon=&quot;@drawable/ic_calendar&quot;
              app:enabled=&quot;false&quot;
              app:subtitle=&quot;@{viewModel.editLiveData2}&quot;
              app:title=&quot;Test 2&quot; /&gt;
  
          &lt;EditText
              android:layout_width=&quot;match_parent&quot;
              android:layout_height=&quot;wrap_content&quot;
              android:text=&quot;@={viewModel.editLiveData2}&quot;/&gt;
  
          &lt;TextView
              android:id=&quot;@+id/tv_test&quot;
              android:layout_width=&quot;match_parent&quot;
              android:layout_height=&quot;wrap_content&quot;
              android:text=&quot;@{viewModel.editLiveData1 +` ` +viewModel.editLiveData2}&quot; /&gt;
      &lt;/androidx.appcompat.widget.LinearLayoutCompat&gt;
  &lt;/layout&gt;
  </code></pre>
  <p>MyActivity.kt</p>
  <pre><code>
  @AndroidEntryPoint
  class MyActivity : BaseActivity&lt;ActivityMyBinding&gt;() {
  
      private val viewModel by viewModels&lt;MyViewModel&gt;()
  
      override fun setBindingViewModel() {
          super.setBindingViewModel()
          getViewBinder().viewModel = viewModel
      }
  
      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          getViewBinder().test.getEditText().addTextChangedListener(object : TextWatcher{
              override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
  
              }
  
              override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
              }
  
              override fun afterTextChanged(p0: Editable?) {
                  if (p0.toString()
                          .startsWith(&quot;0&quot;) &amp;&amp; (p0?.length ?: 0) &gt; 1
                  ) {
                      try {
                          if (p0.toString().toDoubleOrNull() == 0.0) {
                              getViewBinder().test.setText(&quot;0&quot;)
                          } else {
                              getViewBinder().test.setText(p0.toString().substring(1))
                          }
                      } catch (_: Exception) {
  
                      }
                  }
              }
  
          })
  
          viewModel.editLiveData1.observe(this){
              Log.d(&quot;OBSERVE&quot;, it)
          }
          Log.d(&quot;TEST&quot;, viewModel.editLiveData1.value.toString())
      }
  
      override fun getLayoutId(): Int {
          return R.layout.activity_my
      }
  }
  </code></pre>
  <p>Before rotate
  <a href="https://i.stack.imgur.com/fT4Oa.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/fT4Oa.png" alt="before rotate" /></a></p>
  <p>After rotate (only the EditText that set value as live data)
  <a href="https://i.stack.imgur.com/eIX3R.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/eIX3R.png" alt="enter image description here" /></a>
  Is there any wrong with my customview or binding adapter?</p>