Android 中的 AutoCompleteTextView 或 Spinner 数据绑定

AutoCompleteTextView or Spinner Data binding in Android

谁能指导我如何使用与AppCompatAutoCompleteTextView的数据绑定来设置适配器和获取值

最后我得到了这样的解决方案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>