Theory
Date Field जिसने 32-13-2026 Accept किया
FestConnect Mobile की booking screen ने एक plain EditText में visit date माँगी। Harvest, day 1: 32/13/2026, kal, Garba day, और एक emoji।
आप BCA404 के MaskedTextBox philosophy से cure जानते हैं: invalid input को type होना impossible बनाइए। Dates और times के लिए Android का version है users को बिल्कुल type न करने देना: एक calendar pop होता है, वे एक date TAP करते हैं, और सिर्फ़ real dates exist करती हैं। इस बीच जो save इसके बाद आता है उसे एक तीसरा widget चाहिए: waiting के बारे में कुछ honest।
Theory
DatePickerDialog: एक Box में Calendar
Standard pattern: date field पर एक tap एक DatePickerDialog खोलता है:
new DatePickerDialog(this, listener, year, month, day).show();
- आख़िरी 3 arguments एक date preselect करते हैं, usually आज की,
Calendar.getInstance()से fetched - listener (OnDateSetListener) वह callback है जो choice receive करता है:
(view, year, month, dayOfMonth)
और यहाँ Android का सबसे famous छोटा trap खड़ा है: month 0-based है: January 0 है, December 11। Chosen 15 March month = 2 की तरह आता है; इसे month + 1 की तरह display कीजिए।
Practical
Date पिक कीजिए, Time पिक कीजिए, दोनों दिखाइए
public class BookingActivity extends AppCompatActivity {
void pickDateTime() {
Calendar now = Calendar.getInstance();
new DatePickerDialog(this,
(view, year, month, day) -> {
// month is 0-BASED: March arrives as 2
txtDate.setText(day + "/" + (month + 1) + "/" + year);
},
now.get(Calendar.YEAR),
now.get(Calendar.MONTH),
now.get(Calendar.DAY_OF_MONTH)).show();
new TimePickerDialog(this,
(view, hour, minute) ->
txtTime.setText(String.format("%02d:%02d", hour, minute)),
now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE),
true).show(); // true = 24-hour clock
}
}
Theory
ProgressBar: Honest Waiting
Server को booking save होने में 2 seconds लगते हैं: 2 seconds जिनमें एक silent screen frozen जैसी feel होती है। ProgressBar 2 styles में patience खरीदता है:
- indeterminate (default): एक spinner: "working, duration unknown"
- determinate: measurable work के लिए एक filling bar: XML में
style="?android:attr/progressBarStyleHorizontal"set कीजिए, फिरsetMax(100)और work आगे बढ़ते हुएsetProgress(n)
Usual choreography: slow work से पहले progressBar.setVisibility(View.VISIBLE), इसकी completion callback में View.GONE: इस साल के हर callback जैसी वही async bookkeeping।
Quiz
User एक DatePickerDialog में 15 March 2026 पिक करता है। OnDateSetListener को (year, month, dayOfMonth) की तरह क्या मिलता है?
- (2026, 3, 15)
- (2026, 2, 15): month 0-based है, तो March 2 है
- (2026, 3, 14): days 0-based हैं
- एक single Date object, 3 numbers नहीं
Show the answer
(2026, 2, 15): month 0-based है, तो March 2 है
Calendar world में सिर्फ़ months 0-based हैं (पुराने java.util.Calendar से inheritance): January 0, March 2, December 11: जबकि year और dayOfMonth जैसा humans expect करते हैं वैसे आते हैं। हर Android developer ने एक बार off-by-one-month bug ship किया है; exams sure करती हैं आप production से पहले इससे मिल चुके हों। Option C एक symmetry invent करता है जो exist नहीं करती: days 1-based हैं। Option D बाद की Java की nicer APIs describe करता है; यह listener आपको 3 ints hand करता है, और display के लिए +1 आपका काम है।
Think first
कौन सा ProgressBar Style, और GONE कहाँ जाता है?
Booking flow में दो waits: (a) server को booking save करना (unknowable duration), (b) आपके fest pass की 5 photos upload करना, एक-एक करके। हर एक के लिए style पिक कीजिए, और precisely बताइए code में कहाँ bar को फिर hide होना चाहिए, tap करने से पहले।
Show the answer
(a) indeterminate spinner: एक network round-trip के लिए कोई meaningful percentage exist नहीं करता। (b) determinate horizontal bar: setMax(5), हर photo के बाद setProgress: real, measurable fractions। Hiding: work की completion callback में (success AND failure paths), इसे start करने के बाद वाली line पर कभी नहीं: एक async call के बाद वाला code तुरंत चलता है (jQuery effects, AJAX और Node ने सबने यह lesson सिखाया), तो वहाँ hide करना work शुरू होने से पहले bar blank कर देगा। Async model, चौथी appearance।
Watch out
Picker और Progress Traps
Raw Month Display करना: एक March visit के ticket पर 15/2/2026: humans के लिए हमेशा month + 1 (और stored text से एक picker feed करते समय minus 1)।
Midnight Formatting: %02d 9:05 को 09:05 में pad करता है; इसके बिना, times broken दिखते हैं।
एक Bar जो कभी नहीं जाती: हर outcome path में ProgressBar hide कीजिए; एक eternal spinner एक hung app जैसा पढ़ता है: users इसे kill करते हैं।
Widget बनाम Dialog: XML <DatePicker> layout में एक पूरा calendar embed करता है; dialog demand पर pop होता है: दोनों exist करते हैं जानिए, default से dialog इस्तेमाल कीजिए।
Theory
Constraints Corrections से बेहतर हैं
एक कदम पीछे हटकर वह design principle देखिए जो ये widgets share करते हैं: picker input को CONSTRAIN करता है (सिर्फ़ real dates exist करती हैं), progress bar state CONFESS करता है (working, frozen नहीं): दोनों बाद में scolding error messages की ज़रूरत हटाते हैं। BCA402-02 का UI/UX subject इन ideas को नाम देगा; आप already इन्हें practise कर रहे हैं। अगला lesson: जब content screen से बड़ा हो जाए: ScrollView, और इसका one-child law।
Summary
Key takeaways
- DatePickerDialog(context, listener, y, m, d).show(): calendar pop होता है, listener choice receive करता है।
- MONTH 0-BASED है: January 0, March 2: display month + 1। Year और day human-normal हैं।
- TimePickerDialog hour और minute return करता है; आख़िरी argument true मतलब 24-hour clock।
- Initial values Calendar.getInstance() से आती हैं।
- ProgressBar: default indeterminate spinner; measurable work के लिए horizontal style + setMax/setProgress।
- Slow work से पहले bar दिखाइए, हर completion path में इसे hide कीजिए।
- Memory hook: pickers nonsense forbid करते हैं, months zero से शुरू होते हैं, spinners को मरना ज़रूरी है।