Android动态创建多个AutoCompleteTextView下拉锚点错位问题
问题描述
- 需求:动态创建多个输入项视图,每个视图包含一个
AutoCompleteTextView用于展示下拉选择列表。提前定义子项布局item_new_product.xml,运行时inflate后添加到主布局activity_main.xml的viewContainer容器中。 - 预期:每个
AutoCompleteTextView的下拉列表在其父级TextInputLayout位置弹出,XML中已配置dropDownAnchor属性指向对应父级TextInputLayout的ID。 - 实际现象:第一个添加的子项下拉弹出位置正常,后续新增的所有子项下拉列表全部锚定到第一个
AutoCompleteTextView对应的TextInputLayout位置。
根本原因
所有动态inflate的布局内视图ID完全重复,AutoCompleteTextView在解析XML配置的dropDownAnchor属性时,会在全局视图树中查找第一个匹配该ID的视图,最终所有后续子项的锚点都指向第一个加载的同IDTextInputLayout,导致位置错位。
实现方案
步骤1:修改子项布局XML
删除item_new_product.xml中AutoCompleteTextView节点上静态配置的android:dropDownAnchor属性,避免XML加载阶段提前绑定错误锚点。
修改后的item_new_product.xml中AutoCompleteTextView部分代码:
<AutoCompleteTextView android:id="@+id/productTypeAutoCompleteTv" android:layout_width="match_parent" android:layout_height="52dp" android:dropDownHeight="120dp" android:inputType="none" android:maxLines="1" android:textSize="12sp" android:singleLine="true" android:paddingStart="16dp" android:paddingEnd="16dp"/>
步骤2:动态加载时手动绑定唯一锚点
在addView方法中,inflate子布局后,先获取当前子项内部的TextInputLayout实例,为其分配全局唯一ID,再调用setDropDownAnchor()方法给同子项的AutoCompleteTextView绑定该唯一ID,确保锚点是当前子项内的对应视图,完全绕开ID重复导致的全局查找错误。
修改后的核心逻辑如下:
private fun addView() { val rootView = binding.viewContainer val inflater = LayoutInflater.from(this) val child: View = inflater.inflate(R.layout.item_new_product, rootView, false) // 生成子项唯一ID用于后续移除操作 val childId = View.generateViewId() child.id = childId IDs.add(childId) val items: List<String> = listOf("Beverages", "Cocktails", "Pasta", "Pizza", "Salads") val adapter = ArrayAdapter(this, R.layout.item_product_dropdown, items) // 获取当前子项内的TextInputLayout实例,分配唯一ID val productTypeLayout = child.findViewById<TextInputLayout>(R.id.productTypeLayout) val anchorId = View.generateViewId() productTypeLayout.id = anchorId // 获取当前子项内的AutoCompleteTextView,绑定对应唯一锚点ID val productAutoComplete = child.findViewById<AutoCompleteTextView>(R.id.productTypeAutoCompleteTv) productAutoComplete.setDropDownAnchor(anchorId) productAutoComplete.setAdapter(adapter) rootView.addView(child) }
其余参考代码
activity_main.xml
<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="match_parent" tools:context=".MainActivity"> <com.google.android.material.card.MaterialCardView android:id="@+id/cardView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" android:backgroundTint="@android:color/holo_blue_dark" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <com.google.android.material.button.MaterialButton android:id="@+id/addButton" android:layout_width="52dp" android:layout_height="52dp" android:layout_gravity="start" android:background="@color/holo_blue_bright" android:elevation="5dp" android:text="+" /> <com.google.android.material.button.MaterialButton android:id="@+id/removeButton" android:layout_width="52dp" android:layout_height="52dp" android:layout_gravity="end" android:background="@color/holo_blue_bright" android:elevation="5dp" android:text="-" /> </com.google.android.material.card.MaterialCardView> <LinearLayout android:id="@+id/viewContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_margin="16dp" android:animateLayoutChanges="true" android:background="@color/holo_blue_dark" android:orientation="vertical" android:padding="8dp" app:layout_constraintTop_toBottomOf="@id/cardView" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
styles.xml
<resources> <style name="DropdownLayoutStyle" parent="Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"> <item name="endIconTint">@color/holo_blue_dark</item> <item name="hintTextColor">@color/holo_blue_dark</item> </style> </resources>
item_product_dropdown.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="52dp" android:background="@color/holo_blue_bright" android:maxLines="1" android:padding="16dp" android:textColor="@color/white" />
MainActivity.kt 完整代码
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private var IDs = mutableListOf<Int>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) binding.addButton.setOnClickListener { addView() } binding.removeButton.setOnClickListener { removeView() } setContentView(binding.root) } private fun addView() { val rootView = binding.viewContainer val inflater = LayoutInflater.from(this) val child: View = inflater.inflate(R.layout.item_new_product, rootView, false) val childId = View.generateViewId() child.id = childId IDs.add(childId) val items: List<String> = listOf("Beverages", "Cocktails", "Pasta", "Pizza", "Salads") val adapter = ArrayAdapter(this, R.layout.item_product_dropdown, items) val productTypeLayout = child.findViewById<TextInputLayout>(R.id.productTypeLayout) val anchorId = View.generateViewId() productTypeLayout.id = anchorId val productAutoComplete = child.findViewById<AutoCompleteTextView>(R.id.productTypeAutoCompleteTv) productAutoComplete.setDropDownAnchor(anchorId) productAutoComplete.setAdapter(adapter) rootView.addView(child) } private fun removeView() { if (IDs.size == 0) return val view: View? = findViewById(IDs.last()) IDs.removeLast() view?.let { (it.parent as ViewGroup).removeView(it) } } }
内容的提问来源于stack exchange,提问作者AndroIdefix
相关产品推荐
相关产品推荐

