Android Studio+Firebase实现食材自动补全搜索功能技术问询
Hey there! Let's work through your recipe app's ingredient features together. I'll start by pointing out a few areas in your current code that can be improved, then walk you through a complete implementation for all three requirements you listed.
First, Fix the Firebase Data Fetching
Your current code loops through 39 nodes to fetch each ingredient one by one—this is inefficient and can cause unnecessary network calls. Instead, we can fetch the entire ingredients node in a single request and iterate through its children. We'll also store all ingredients in a global list so we can reuse it for filtering later.
Next, Implement Prefix-Matching Auto-Complete Search
The default listview.setFilterText() does a contains match (e.g., searching for "c" would show any ingredient with a "c" anywhere in the name), but you want a prefix match (only ingredients starting with the input). We'll handle this manually in the TextWatcher by filtering our global ingredient list and updating the adapter.
Finally, Add Selected Ingredients to "Disliked Ingredients" List
We'll set up the ListView to allow multiple selections, then add a button to save the selected items to Firebase (we'll assume you're storing user-specific data under a users node—adjust this if your database structure is different).
Full Updated Code
import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.SparseBooleanArray; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; import java.util.List; public class SearchActivity extends AppCompatActivity { private EditText search; private ListView listview; private Button addDislikeBtn; private List<String> allIngredients = new ArrayList<>(); private ArrayAdapter<String> adapter; private DatabaseReference userDislikeRef; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search); // Initialize views search = findViewById(R.id.search); listview = findViewById(R.id.listview1); addDislikeBtn = findViewById(R.id.add_dislike_btn); // Set up adapter with multi-choice list item layout adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_multiple_choice, allIngredients); listview.setAdapter(adapter); listview.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // Allow multiple selections // Initialize Firebase references FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ingredientsRef = database.getReference("ingredients"); // Check if user is logged in for disliked ingredients storage FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); if (currentUser != null) { userDislikeRef = database.getReference("users").child(currentUser.getUid()).child("dislikedIngredients"); } else { addDislikeBtn.setEnabled(false); Toast.makeText(this, "Please log in to save disliked ingredients", Toast.LENGTH_SHORT).show(); } // Fetch all ingredients from Firebase in one request ingredientsRef.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { allIngredients.clear(); for (DataSnapshot childSnapshot : dataSnapshot.getChildren()) { // Get ingredient name from the "ingredients" field of each child node String ingredientName = childSnapshot.child("ingredients").getValue(String.class); if (ingredientName != null) { allIngredients.add(ingredientName); } } adapter.notifyDataSetChanged(); // Update list with fetched data } @Override public void onCancelled(DatabaseError databaseError) { Toast.makeText(SearchActivity.this, "Failed to load ingredients", Toast.LENGTH_SHORT).show(); } }); // Handle search text changes for prefix matching search.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) {} @Override public void afterTextChanged(Editable s) { String filterText = s.toString().toLowerCase().trim(); List<String> filteredList = new ArrayList<>(); if (filterText.isEmpty()) { filteredList.addAll(allIngredients); } else { // Only add ingredients that start with the filter text (case-insensitive) for (String ingredient : allIngredients) { if (ingredient.toLowerCase().startsWith(filterText)) { filteredList.add(ingredient); } } } // Update adapter with filtered results adapter.clear(); adapter.addAll(filteredList); adapter.notifyDataSetChanged(); } }); // Handle adding selected ingredients to disliked list addDislikeBtn.setOnClickListener(v -> { if (currentUser == null) return; SparseBooleanArray selectedPositions = listview.getCheckedItemPositions(); List<String> selectedIngredients = new ArrayList<>(); // Collect all selected items for (int i = 0; i < selectedPositions.size(); i++) { int position = selectedPositions.keyAt(i); if (selectedPositions.valueAt(i)) { selectedIngredients.add(adapter.getItem(position)); } } if (!selectedIngredients.isEmpty()) { // Save selected ingredients to Firebase (push() ensures unique keys) for (String ingredient : selectedIngredients) { userDislikeRef.push().setValue(ingredient); } Toast.makeText(SearchActivity.this, "Added to disliked ingredients", Toast.LENGTH_SHORT).show(); listview.clearChoices(); // Clear selection after saving adapter.notifyDataSetChanged(); } else { Toast.makeText(SearchActivity.this, "No ingredients selected", Toast.LENGTH_SHORT).show(); } }); } }
Required Layout Update
Add this button to your activity_search.xml to let users save their disliked ingredients:
<Button android:id="@+id/add_dislike_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/listview1" android:text="Add Selected to Disliked Ingredients" />
Key Notes
- Firebase Structure: Adjust the paths (
ingredients,users/{userId}/dislikedIngredients) to match your actual database schema. - Auth Check: The code disables the save button if the user isn't logged in—make sure you've set up Firebase Authentication in your project.
- Prefix Matching: The search is case-insensitive and only matches ingredients starting with the input text, exactly as you requested.
内容的提问来源于stack exchange,提问作者Pearl Fall

