Android中RecyclerView内AutoCompleteTextView搜索时下拉弹窗不显示
RecyclerView中Item内AutoCompleteTextView下拉弹窗无法显示的解决方案
问题描述
我被这个问题困扰了3天多。在RecyclerView的每个Item中都包含一个AutoCompleteTextView,但在AutoCompleteTextView中搜索时,下拉弹窗无法显示。需要指出修改代码的位置或方式,实现在RecyclerView每个Item中正常使用AutoCompleteTextView。
已尝试的方案
- 手动调用
autoCompleteTextView.showDropDown()显示弹窗 - 用户在AutoCompleteTextView中输入时通知适配器
- 用户输入时每次重置新的适配器
- 在AndroidManifest中尝试了
android:windowSoftInputMode=""的所有选项 - 在StackOverflow上查阅了20多个相关问题
- 在
onBindViewHolder()方法中设置ArrayAdapter
现有代码
Activity的XML文件
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewModel" type="com..job..i.viewModel.job.JobViewModel" /> <import type="android.view.View" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/screenBG" android:orientation="vertical"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rvSupervisor" android:layout_width="match_parent" android:layout_height="wrap_content" tools:itemCount="1" tools:listitem="@layout/job_filter_adapter" /> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.core.widget.NestedScrollView> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
适配器类
public class JobFilterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public final String tag = JobFilterAdapter.class.getSimpleName(); private final Activity _activity; private final LayoutInflater inflater; private final List<StaffInfo> _staffList; private final List<String> jobList; public JobFilterAdapter(Activity activity, List<StaffInfo> staffList, List<String> list) { jobStaffAdapter = this; _activity = activity; inflater = _activity.getLayoutInflater(); _staffList = staffList; jobList = list; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new AdapterHolder(DataBindingUtil.inflate(inflater, R.layout.row_job, parent, false), new JObWatcher()); } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, @SuppressLint("RecyclerView") int position) { AdapterHolder adapterHolder = (AdapterHolder) holder; StaffInfo model = _staffList.get(holder.getAdapterPosition()); adapterHolder.binding.autoCompleteTxtJob.setText(model.getStaffNo(), false); adapterHolder.binding.txtStaffName.setText(model.getStaffName()); adapterHolder.jobWatcher.updateState(position, adapterHolder); } @Override public int getItemCount() { return _staffList.size(); } class AdapterHolder extends RecyclerView.ViewHolder { private final RowJobBinding binding; JObWatcher jobWatcher; ArrayAdapter jobArrayAdapter; public AdapterHolder(@NonNull RowJobBinding _binding, JObWatcher _jobWatcher) { super(_binding.getRoot()); binding = _binding; jobWatcher = _jobWatcher; jobArrayAdapter = new ArrayAdapter(_activity, R.layout.row_job_item,jobList); binding.autoCompleteTxtJob.setThreshold(1); } } public class JObWatcher { int position; AdapterHolder holder; private void updateState(int pos, AdapterHolder viewHolder) { position = pos; holder = viewHolder; manageAutoText(); } private void manageAutoText() { holder.binding.autoCompleteTxtJob.setOnFocusChangeListener((view, b) -> { if (b) { holder.binding.autoCompleteTxtJob.setAdapter(holder.jobArrayAdapter); } else { holder.binding.autoCompleteTxtJob.setAdapter(null); } }); } } }
适配器Item的XML文件
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/row" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/_5sdp" android:padding="@dimen/_5sdp"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintTop_toTopOf="parent" tools:layout_editor_absoluteX="0dp"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="@dimen/_25sdp" android:orientation="horizontal"> <androidx.appcompat.widget.AppCompatTextView style="@style/txtJobDetailRowLeft" android:text="@string/lbl_job" /> <androidx.appcompat.widget.AppCompatAutoCompleteTextView android:id="@+id/autoCompleteTxtJob" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="6" android:background="@drawable/bg_et_first" android:gravity="center_vertical" android:inputType="text" android:paddingLeft="@dimen/_5sdp" android:singleLine="true" android:textColor="@color/black" android:textSize="@dimen/_9sdp" /> </androidx.appcompat.widget.LinearLayoutCompat> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="@dimen/_25sdp" android:layout_marginTop="@dimen/_5sdp" android:orientation="horizontal"> <androidx.appcompat.widget.AppCompatTextView style="@style/txtJobDetailRowLeft" android:text="@string/lbl_staff_name" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/txtStaffName" style="@style/txtJobDetailRowRight" /> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
解决方案
问题根源分析
- NestedScrollView与RecyclerView的嵌套冲突:RecyclerView被包裹在NestedScrollView中,会导致AutoCompleteTextView的下拉弹窗无法计算正确的显示位置,出现被遮挡或不显示的情况。
- 焦点监听逻辑的问题:失去焦点时清空适配器,会导致再次获取焦点时适配器重绑不及时;仅在获取焦点时设置适配器,无法保证输入时弹窗正常触发。
- RecyclerView复用机制的影响:ViewHolder复用可能导致AutoCompleteTextView的状态混乱,适配器没有正确绑定到当前Item。
修改步骤
1. 移除NestedScrollView对RecyclerView的包裹
直接让RecyclerView在ConstraintLayout中布局,避免滚动容器嵌套导致的布局计算错误:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewModel" type="com..job..i.viewModel.job.JobViewModel" /> <import type="android.view.View" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/screenBG" android:orientation="vertical" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rvSupervisor" android:layout_width="match_parent" android:layout_height="match_parent" tools:itemCount="1" tools:listitem="@layout/job_filter_adapter" /> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
2. 调整适配器中的AutoCompleteTextView绑定逻辑
去掉焦点监听中设置/清除适配器的逻辑,直接在ViewHolder初始化时绑定适配器,并添加文本变化监听确保弹窗触发:
class AdapterHolder extends RecyclerView.ViewHolder { private final RowJobBinding binding; JObWatcher jobWatcher; ArrayAdapter jobArrayAdapter; public AdapterHolder(@NonNull RowJobBinding _binding, JObWatcher _jobWatcher) { super(_binding.getRoot()); binding = _binding; jobWatcher = _jobWatcher; jobArrayAdapter = new ArrayAdapter(_activity, R.layout.row_job_item, jobList); binding.autoCompleteTxtJob.setThreshold(1); // 直接绑定适配器,无需在焦点变化时重复设置 binding.autoCompleteTxtJob.setAdapter(jobArrayAdapter); // 添加文本变化监听,确保输入达到阈值时显示弹窗 binding.autoCompleteTxtJob.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.length() >= binding.autoCompleteTxtJob.getThreshold()) { binding.autoCompleteTxtJob.showDropDown(); } } @Override public void afterTextChanged(Editable s) {} }); } }
同时删除JObWatcher中不必要的焦点监听逻辑:
public class JObWatcher { int position; AdapterHolder holder; private void updateState(int pos, AdapterHolder viewHolder) { position = pos; holder = viewHolder; // 移除manageAutoText调用及相关逻辑 } }
3. 处理RecyclerView复用的状态同步
在onBindViewHolder中更新适配器的过滤状态,避免复用旧数据导致的弹窗异常:
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, @SuppressLint("RecyclerView") int position) { AdapterHolder adapterHolder = (AdapterHolder) holder; StaffInfo model = _staffList.get(holder.getAdapterPosition()); adapterHolder.binding.autoCompleteTxtJob.setText(model.getStaffNo(), false); adapterHolder.binding.txtStaffName.setText(model.getStaffName()); // 同步当前文本的过滤状态,确保弹窗显示正确数据 adapterHolder.jobArrayAdapter.getFilter().filter(adapterHolder.binding.autoCompleteTxtJob.getText()); adapterHolder.jobWatcher.updateState(position, adapterHolder); }
额外优化建议
- 如果必须保留NestedScrollView,可给RecyclerView添加
android:nestedScrollingEnabled="false"属性,但这会禁用RecyclerView自身的滚动能力,仅适合特殊场景。 - 检查
row_job_item布局的有效性,确保下拉项布局没有尺寸异常或不可见的问题。
内容的提问来源于stack exchange,提问作者Ninja
相关产品推荐
相关产品推荐

