android-studioandroid-layoutdata-bindingandroid-databindingautocompletetextview

AutoCompleteTextView or Spinner Data binding in Android


Can anyone guide me about how to use Data binding with AppCompatAutoCompleteTextView for setting adapter and getting value


Solution

  • Finally I got the solutions like this image

    BindingAdapter.kt

          @JvmStatic
          @BindingAdapter("valueAttrChanged")
          fun MyAutoCompleteSpinner.setListener(listener: InverseBindingListener?) {
              this.onItemSelectedListener = if (listener != null) {
                  object : AdapterView.OnItemSelectedListener {
                      override fun onNothingSelected(parent: AdapterView<*>?) {
                          listener.onChange()
                      }
        
                      override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                          listener.onChange()
                      }
                  }
              } else {
                  null
              }
          }
        
        
          @JvmStatic
          @get:InverseBindingAdapter(attribute = "value")
          @set:BindingAdapter("value")
          var MyAutoCompleteSpinner.selectedValue: String?
              get() {
                  return if (listSelection != ListView.INVALID_POSITION) {
                      adapter.getItem(listSelection).toString()
                  } else {
                      null
                  }
              }
              set(value) {
                  val newValue = value ?: adapter.getItem(0).toString()
                  setText(newValue, true)
                  if (adapter is ArrayAdapter<*>) {
                      val position = (adapter as ArrayAdapter<String?>).getPosition(newValue)
                      listSelection = position
                  }
              }
        
        
          @JvmStatic
          @BindingAdapter("entries", "itemLayout", "textViewId", requireAll = false)
          fun MyAutoCompleteSpinner.bindAdapter(entries: Array<String>, @LayoutRes itemLayout: Int?, @IdRes textViewId: Int?) {
              val adapter = when {
                  itemLayout == null -> {
                      ArrayAdapter(context, android.R.layout.simple_list_item_1, android.R.id.text1, entries)
                  }
                  textViewId == null -> {
                      ArrayAdapter(context, itemLayout, entries)
                  }
                  else -> {
                      ArrayAdapter(context, itemLayout, textViewId, entries)
                  }
              }
              setAdapter(adapter)
          }
    

    layout.xml

        ....
        <data>
            <variable
                name="viewmodel"
                type="com.example.vm.ViewModel" />
        </data>
        .......
        .......
        <com.google.android.material.textfield.TextInputLayout
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Gender">
        
            <com.example.widget.MyAutoCompleteSpinner
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                entries="@{@stringArray/genders}"
                value="@{viewmodel.gender}"/>
        
        </com.google.android.material.textfield.TextInputLayout>
        ....
    

    MyAutoCompleteSpinner.kt

    package com.example.widget
        
    import android.content.Context
    import android.graphics.Color
    import android.graphics.Rect
    import android.util.AttributeSet
    import android.view.MotionEvent
    import com.google.android.material.textview.MaterialAutoCompleteTextView
    import timber.log.Timber
        
    class MyAutoCompleteSpinner : MaterialAutoCompleteTextView {
        constructor(context: Context) : this(context, null)
    
        constructor(arg0: Context, arg1: AttributeSet?) : super(arg0, arg1)
    
        constructor(arg0: Context, arg1: AttributeSet, arg2: Int) : super(arg0, arg1, arg2)
    
        init {
            isCursorVisible = false
            setEnableSpinner(false)
    
            setTextColor(Color.BLACK)
        }
    
        override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
            super.onFocusChanged(focused, direction, previouslyFocusedRect)
            if (focused && filter != null) {
                performFiltering(null, 0)
            }
            setEnableSpinner(false)
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            Timber.d(event?.action.toString())
            when {
                event?.action == MotionEvent.ACTION_MOVE -> {
                    setEnableSpinner(true)
                }
                event?.action == MotionEvent.ACTION_UP -> {
                    setEnableSpinner(true)
                }
                event?.action == MotionEvent.ACTION_DOWN -> {
                    setEnableSpinner(true)
                }
            }
    
            if (event?.action == MotionEvent.ACTION_UP) {
                if (event.rawX <= totalPaddingLeft) {
                    setEnableSpinner(true)
                    return true
                }
            }
    
            return super.onTouchEvent(event)
        }
    
        fun setEnableSpinner(enable: Boolean){
            this.isEnabled = enable
        }
    
        override fun performFiltering(text: CharSequence?, keyCode: Int) {
            super.performFiltering(null, keyCode)
        }
    }
    

    string.xml

        <string-array name="genders">
            <item>"Male"</item>
            <item>"Female"</item>
        </string-array>