Spinner的setOnItemSelectedListener()未执行问题排查与解决
Spinner OnItemSelectedListener 不执行问题排查与解决
原始问题
布局中的Spinner能正常显示从Firebase加载的选项,但设置setOnItemSelectedListener()后,监听器的两个回调方法均未执行。
XML布局代码
<RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Spinner android:id="@+id/spinner_category_name_add_expense" android:layout_width="wrap_content" android:layout_height="24dp" android:layout_alignParentEnd="true" /> </RelativeLayout>
原始Java代码
public class AddExpense extends AppCompatActivity { Spinner spinner_category_name_add_expense; SharedPreferences sharedPreferences; String userKey, userName, selected_cat; DatabaseReference dbref; ArrayList<String> categoryModelArrayList; ArrayAdapter<String> spinnerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_expense); fetchSharedPreferences(); categoryModelArrayList = new ArrayList<String>(); spinner_category_name_add_expense = findViewById(R.id.spinner_category_name_add_expense); createCategoryList(); addCategoriesToSpinner(); spinner_category_name_add_expense.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { Toast.makeText(AddExpense.this, "Selected", Toast.LENGTH_SHORT).show(); // 未执行 } @Override public void onNothingSelected(AdapterView<?> adapterView) { Toast.makeText(AddExpense.this, "Not Selected", Toast.LENGTH_SHORT).show(); // 也未执行 } }); } private void fetchSharedPreferences() { sharedPreferences = this.getSharedPreferences("shared_prefs", Context.MODE_PRIVATE); userKey = sharedPreferences.getString("user_key", null); } // 从Firebase获取数据,ArrayList已被正确填充 public void createCategoryList() { String currentUserCategoriesPath = "AssignmentUsers/" + userKey + "/userCategories"; dbref = FirebaseDatabase.getInstance().getReference(currentUserCategoriesPath); dbref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { int i = 0; for (DataSnapshot snapshot : dataSnapshot.getChildren()) { String categoryModel = snapshot.getKey(); categoryModelArrayList.add(categoryModel); Log.d("firebase", String.valueOf(categoryModelArrayList.get(i))); i++; } } @Override public void onCancelled(@NonNull DatabaseError databaseError) { Log.d("firebase", "Error"); } }); } public void addCategoriesToSpinner() { spinnerAdapter = new ArrayAdapter<String>( this, android.R.layout.simple_spinner_dropdown_item, categoryModelArrayList ); spinner_category_name_add_expense.setAdapter(spinnerAdapter); spinnerAdapter.notifyDataSetChanged(); } }
问题原因分析
Firebase数据库的addListenerForSingleValueEvent()是异步操作,会在后台线程获取数据,主线程则会继续执行后续代码。这直接导致:
- 调用
addCategoriesToSpinner()和setOnItemSelectedListener()时,categoryModelArrayList还是空列表 - 后续Firebase加载完数据后,虽然更新了ArrayList,但适配器和监听器的绑定逻辑已经在空列表状态下完成,导致选项加载后监听器无法正常触发
解决方法
将适配器初始化、设置适配器以及监听器绑定的逻辑,全部移到Firebase的onDataChange()回调方法中,确保只有在数据加载完成、列表被填充后,再完成Spinner的配置。
修改后的Java代码
public class AddExpense extends AppCompatActivity { Spinner spinner_category_name_add_expense; SharedPreferences sharedPreferences; String userKey, userName, selected_cat; DatabaseReference dbref; ArrayList<String> categoryModelArrayList; ArrayAdapter<String> spinnerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_expense); fetchSharedPreferences(); categoryModelArrayList = new ArrayList<String>(); spinner_category_name_add_expense = findViewById(R.id.spinner_category_name_add_expense); createCategoryList(); } private void fetchSharedPreferences() { sharedPreferences = this.getSharedPreferences("shared_prefs", Context.MODE_PRIVATE); userKey = sharedPreferences.getString("user_key", null); } public void createCategoryList() { String currentUserCategoriesPath = "AssignmentUsers/" + userKey + "/userCategories"; dbref = FirebaseDatabase.getInstance().getReference(currentUserCategoriesPath); dbref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { int i = 0; for (DataSnapshot snapshot : dataSnapshot.getChildren()) { String categoryModel = snapshot.getKey(); categoryModelArrayList.add(categoryModel); Log.d("firebase", String.valueOf(categoryModelArrayList.get(i))); i++; } // 数据加载完成后,配置Spinner和监听器 addCategoriesToSpinner(); setupSpinnerListener(); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { Log.d("firebase", "Error"); } }); } public void addCategoriesToSpinner() { spinnerAdapter = new ArrayAdapter<String>( this, android.R.layout.simple_spinner_dropdown_item, categoryModelArrayList ); spinner_category_name_add_expense.setAdapter(spinnerAdapter); spinnerAdapter.notifyDataSetChanged(); } private void setupSpinnerListener() { spinner_category_name_add_expense.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { Toast.makeText(AddExpense.this, "Selected", Toast.LENGTH_SHORT).show(); selected_cat = categoryModelArrayList.get(i); // 这里可以处理选中后的逻辑 } @Override public void onNothingSelected(AdapterView<?> adapterView) { Toast.makeText(AddExpense.this, "Not Selected", Toast.LENGTH_SHORT).show(); } }); } }
补充优化建议
- 可以提前初始化适配器,在
onDataChange()中仅更新列表并调用notifyDataSetChanged(),避免重复创建适配器实例 - 若需要监听数据实时变化,可改用
addValueEventListener()替代addListenerForSingleValueEvent()
内容的提问来源于stack exchange,提问作者Arham Nadeem
相关产品推荐
相关产品推荐

