Theory
You have met this widget; now meet its machinery
The syllabus sends you back to a BCA305-02 acquaintance: AutoCompleteTextView and TextWatcher closed FestConnect Mobile-1. A revisit is not a rerun: last year you WIRED them; this year you should be able to explain the machinery and answer the deeper questions.
And you have new context: in BCA405-01 you built live search on the web with keyup and AJAX. Today's lesson is that same pattern's mobile twin, and the parallel is the fastest way to master both.
Theory
AutoCompleteTextView: the recap, tightened
An AutoCompleteTextView is an EditText subclass that drops down filtered suggestions as the user types:
- suggestions come from an ArrayAdapter (the ListView lesson's middleman again), stock row
simple_dropdown_item_1line - setThreshold(n): how many characters before suggestions appear; the default is 2, and values below 1 are treated as 1
- it suggests, never restricts: free text remains typeable, so validate
getText()if only listed values are legal
One string per suggestion = stock adapter; richer dropdown rows = custom adapter: the ListView decision rule, verbatim.
Theory
TextWatcher: the 3 moments of one keystroke
A TextWatcher (attached with addTextChangedListener) hears every text change through 3 methods, in strict order:
- beforeTextChanged(s, start, count, after): the OLD text, an instant before the change: s still reads "Garb"
- onTextChanged(s, start, before, count): the NEW text, just landed: s reads "Garba": the live-search moment
- afterTextChanged(Editable s): last, and the only one handed an Editable you may modify
Most real work lives in onTextChanged (react) or afterTextChanged (edit); before is for comparing old with new.
Practical
Search box with suggestions AND a live counter
public class SearchActivity extends AppCompatActivity {
String[] events = { "Garba Night", "Coding Contest", "Robo Race",
"Rangoli", "Quiz Bowl" };
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_search);
AutoCompleteTextView box = findViewById(R.id.searchBox);
box.setAdapter(new ArrayAdapter<>(this,
android.R.layout.simple_dropdown_item_1line, events));
box.setThreshold(1); // suggest from letter 1
box.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start,
int count, int after) { }
public void onTextChanged(CharSequence s, int start,
int before, int count) {
int hits = 0; // live results counter
for (String e : events)
if (e.toLowerCase().contains(s.toString().toLowerCase()))
hits++;
lblCount.setText(hits + " matching events");
}
public void afterTextChanged(Editable s) { }
});
}
}
Quiz
The box shows "Garb" and the user types a. In which TextWatcher method does s FIRST read "Garba", making it the right hook for live search?
- beforeTextChanged: it fires first, so it has the newest text
- onTextChanged: it fires as the new text lands; beforeTextChanged still saw "Garb"
- afterTextChanged: only it ever sees the new text
- All 3 receive "Garba"; only their timing differs
Show the answer
onTextChanged: it fires as the new text lands; beforeTextChanged still saw "Garb"
The 3 methods split one keystroke into old, new and settled: beforeTextChanged fires first BUT carries the pre-change text ("Garb"): firing order and text freshness are different axes, which is exactly the confusion this question hunts. onTextChanged delivers the new text the moment it lands: the live-search hook, playing the role keyup played in BCA405-01's web version. afterTextChanged also sees "Garba" (option C's half-truth) but fires last and exists for EDITING via its Editable, not for first reaction.
Think first
The watcher that watched itself forever
A classmate wants typed text auto-capitalised, so inside afterTextChanged he calls s.replace(...) with the capitalised version: and the app freezes. Trace why, using what a watcher watches, then name the guard.
Show the answer
His replace() CHANGES the text: which is a text change: which fires the watcher again: which replaces again: an infinite loop of self-triggered callbacks freezing the main thread. The guard: make the edit conditional so an already-capitalised text is left untouched (if the first letter is already uppercase, return): then the second pass changes nothing and the cycle dies. General law: any watcher that modifies what it watches must be idempotent: running it on its own output must be a no-op. This is THE classic TextWatcher exam trap.
Watch out
Second-pass traps (the deeper set)
All 3 methods must exist: TextWatcher is an interface; implement even the ones you leave empty, or it will not compile.
Editing outside afterTextChanged: only its Editable is yours to modify; the CharSequences of the other 2 are read-only views.
Suggestion is not validation: free text still enters an AutoCompleteTextView; check the final value against the list if it must match.
Heavy work per keystroke: onTextChanged fires constantly; slow code there makes typing lag: keep it light.
Theory
One pattern, two platforms
Set the twins side by side: web live search = keyup event + AJAX + repaint the list; mobile = onTextChanged + filter + update the label. Same architecture, different dialect: and when Flutter arrives next unit, TextField's onChanged will be the SAME hook a third time. Unit 1 has 2 widgets left: image showmanship (ImageSlider, ImageSwitcher, SearchView) and then the tabbed layout that reorganises the whole app.
Summary
Key takeaways
- AutoCompleteTextView = EditText + adapter-fed dropdown; setThreshold(n) gates when suggestions appear (minimum 1).
- It suggests, never restricts: validate getText() when only listed values are legal.
- TextWatcher's 3 moments: beforeTextChanged (old text), onTextChanged (new text: the live-search hook), afterTextChanged (Editable, may modify).
- Firing order and text freshness differ: before fires first but sees the old text.
- Watchers that edit their own text must be idempotent or they loop forever.
- Same pattern as web live search (keyup + AJAX): one architecture, two platforms.
- Memory hook: before sees old, on sees new, after may edit, edits must self-stop.