or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

buttons-and-selection.mdfeedback-and-communication.mdindex.mdinput-and-forms.mdlayout-and-containers.mdnavigation-components.mdpickers-and-selection.mdtheming-and-styling.md

pickers-and-selection.mddocs/

0

# Pickers and Selection

1

2

Date and time pickers for user input with Material Design styling and behavior.

3

4

## Core Imports

5

6

```java

7

import com.google.android.material.datepicker.MaterialDatePicker;

8

import com.google.android.material.datepicker.CalendarConstraints;

9

import com.google.android.material.datepicker.DateValidatorPointForward;

10

import com.google.android.material.datepicker.DateValidatorPointBackward;

11

import com.google.android.material.datepicker.CompositeDateValidator;

12

import com.google.android.material.timepicker.MaterialTimePicker;

13

import com.google.android.material.timepicker.TimeFormat;

14

import android.util.Pair;

15

```

16

17

## Material Date Picker

18

19

Material Design date picker dialog for single dates or date ranges.

20

21

```java { .api }

22

class MaterialDatePicker<S> extends DialogFragment {

23

// Factory methods for single date selection

24

static Builder<Long> todayInUtcMilliseconds();

25

static Builder<Long> datePicker();

26

27

// Factory method for date range selection

28

static Builder<Pair<Long, Long>> dateRangePicker();

29

30

// Selection access

31

S getSelection();

32

String getHeaderText();

33

34

// Positive button listeners

35

boolean addOnPositiveButtonClickListener(MaterialPickerOnPositiveButtonClickListener<S> listener);

36

boolean removeOnPositiveButtonClickListener(MaterialPickerOnPositiveButtonClickListener<S> listener);

37

void clearOnPositiveButtonClickListeners();

38

39

// Negative button listeners

40

boolean addOnNegativeButtonClickListener(View.OnClickListener listener);

41

boolean removeOnNegativeButtonClickListener(View.OnClickListener listener);

42

void clearOnNegativeButtonClickListeners();

43

44

// Cancel listeners

45

boolean addOnCancelListener(DialogInterface.OnCancelListener listener);

46

boolean removeOnCancelListener(DialogInterface.OnCancelListener listener);

47

void clearOnCancelListeners();

48

49

// Dismiss listeners

50

boolean addOnDismissListener(DialogInterface.OnDismissListener listener);

51

boolean removeOnDismissListener(DialogInterface.OnDismissListener listener);

52

void clearOnDismissListeners();

53

}

54

55

interface MaterialPickerOnPositiveButtonClickListener<S> {

56

void onPositiveButtonClick(S selection);

57

}

58

59

class MaterialDatePicker.Builder<S> {

60

Builder<S> setSelection(S selection);

61

Builder<S> setCalendarConstraints(CalendarConstraints calendarConstraints);

62

Builder<S> setTheme(@StyleRes int themeResId);

63

Builder<S> setTitleText(@StringRes int titleTextResId);

64

Builder<S> setTitleText(CharSequence charSequence);

65

Builder<S> setPositiveButtonText(@StringRes int positiveButtonTextResId);

66

Builder<S> setPositiveButtonText(CharSequence charSequence);

67

Builder<S> setNegativeButtonText(@StringRes int negativeButtonTextResId);

68

Builder<S> setNegativeButtonText(CharSequence charSequence);

69

Builder<S> setInputMode(int inputMode);

70

MaterialDatePicker<S> build();

71

}

72

```

73

74

### Date Picker Input Mode Constants

75

76

```java { .api }

77

public static final int INPUT_MODE_CALENDAR = 0;

78

public static final int INPUT_MODE_TEXT = 1;

79

```

80

81

### Usage Example

82

83

```java

84

// Single date picker

85

MaterialDatePicker<Long> datePicker = MaterialDatePicker.Builder.datePicker()

86

.setTitleText("Select date")

87

.setSelection(MaterialDatePicker.todayInUtcMilliseconds())

88

.build();

89

90

datePicker.addOnPositiveButtonClickListener(selection -> {

91

// Handle selected date (in UTC milliseconds)

92

SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy", Locale.getDefault());

93

String selectedDate = sdf.format(new Date(selection));

94

dateButton.setText(selectedDate);

95

});

96

97

datePicker.show(getSupportFragmentManager(), "date_picker");

98

99

// Date range picker

100

MaterialDatePicker<Pair<Long, Long>> dateRangePicker = MaterialDatePicker.Builder.dateRangePicker()

101

.setTitleText("Select dates")

102

.build();

103

104

dateRangePicker.addOnPositiveButtonClickListener(selection -> {

105

Long startDate = selection.first;

106

Long endDate = selection.second;

107

108

SimpleDateFormat sdf = new SimpleDateFormat("MMM dd", Locale.getDefault());

109

String dateRange = sdf.format(new Date(startDate)) + " - " + sdf.format(new Date(endDate));

110

dateRangeButton.setText(dateRange);

111

});

112

113

dateRangePicker.show(getSupportFragmentManager(), "date_range_picker");

114

115

// Date picker with constraints

116

CalendarConstraints.Builder constraintsBuilder = new CalendarConstraints.Builder();

117

constraintsBuilder.setStart(MaterialDatePicker.todayInUtcMilliseconds());

118

constraintsBuilder.setEnd(MaterialDatePicker.todayInUtcMilliseconds() + TimeUnit.DAYS.toMillis(365));

119

constraintsBuilder.setValidator(DateValidatorPointForward.now());

120

121

MaterialDatePicker<Long> constrainedPicker = MaterialDatePicker.Builder.datePicker()

122

.setCalendarConstraints(constraintsBuilder.build())

123

.setTitleText("Select future date")

124

.build();

125

```

126

127

## Calendar Constraints

128

129

Configuration for calendar navigation bounds and date validation.

130

131

```java { .api }

132

class CalendarConstraints {

133

Month getStart();

134

Month getEnd();

135

Month getOpenAt();

136

DateValidator getDateValidator();

137

int getYearSpan();

138

int getMonthSpan();

139

}

140

141

class CalendarConstraints.Builder {

142

Builder setStart(long month);

143

Builder setEnd(long month);

144

Builder setOpenAt(long month);

145

Builder setValidator(DateValidator validator);

146

CalendarConstraints build();

147

}

148

149

interface CalendarConstraints.DateValidator {

150

boolean isValid(long date);

151

}

152

```

153

154

### Date Validators

155

156

```java { .api }

157

// Forward from a specific date

158

class DateValidatorPointForward implements CalendarConstraints.DateValidator {

159

static DateValidatorPointForward from(long point);

160

static DateValidatorPointForward now();

161

boolean isValid(long date);

162

}

163

164

// Backward to a specific date

165

class DateValidatorPointBackward implements CalendarConstraints.DateValidator {

166

static DateValidatorPointBackward before(long point);

167

static DateValidatorPointBackward now();

168

boolean isValid(long date);

169

}

170

171

// Composite validator combining multiple validators

172

class CompositeDateValidator implements CalendarConstraints.DateValidator {

173

static DateValidator allOf(List<DateValidator> validators);

174

static DateValidator anyOf(List<DateValidator> validators);

175

boolean isValid(long date);

176

}

177

```

178

179

### Usage Example

180

181

```java

182

// Date picker with validation - only future dates

183

CalendarConstraints constraints = new CalendarConstraints.Builder()

184

.setStart(MaterialDatePicker.todayInUtcMilliseconds())

185

.setValidator(DateValidatorPointForward.now())

186

.build();

187

188

MaterialDatePicker<Long> futureDatePicker = MaterialDatePicker.Builder.datePicker()

189

.setCalendarConstraints(constraints)

190

.setTitleText("Select appointment date")

191

.build();

192

193

// Date picker with range constraints - only current year

194

Calendar calendar = Calendar.getInstance();

195

calendar.set(Calendar.MONTH, Calendar.JANUARY);

196

calendar.set(Calendar.DAY_OF_MONTH, 1);

197

long yearStart = calendar.getTimeInMillis();

198

199

calendar.set(Calendar.MONTH, Calendar.DECEMBER);

200

calendar.set(Calendar.DAY_OF_MONTH, 31);

201

long yearEnd = calendar.getTimeInMillis();

202

203

CalendarConstraints yearConstraints = new CalendarConstraints.Builder()

204

.setStart(yearStart)

205

.setEnd(yearEnd)

206

.setOpenAt(MaterialDatePicker.todayInUtcMilliseconds())

207

.build();

208

209

// Composite validator - weekdays only and future dates

210

DateValidator weekdaysOnly = new DateValidator() {

211

@Override

212

public boolean isValid(long date) {

213

Calendar cal = Calendar.getInstance();

214

cal.setTimeInMillis(date);

215

int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);

216

return dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY;

217

}

218

};

219

220

DateValidator compositeValidator = CompositeDateValidator.allOf(Arrays.asList(

221

DateValidatorPointForward.now(),

222

weekdaysOnly

223

));

224

225

CalendarConstraints businessDayConstraints = new CalendarConstraints.Builder()

226

.setValidator(compositeValidator)

227

.build();

228

```

229

230

## Material Time Picker

231

232

Material Design time picker dialog for selecting time values.

233

234

```java { .api }

235

class MaterialTimePicker extends DialogFragment {

236

// Factory method

237

static Builder builder();

238

239

// Time access

240

int getHour();

241

int getMinute();

242

int getInputMode();

243

244

// Positive button listeners

245

boolean addOnPositiveButtonClickListener(View.OnClickListener listener);

246

boolean removeOnPositiveButtonClickListener(View.OnClickListener listener);

247

void clearOnPositiveButtonClickListeners();

248

249

// Negative button listeners

250

boolean addOnNegativeButtonClickListener(View.OnClickListener listener);

251

boolean removeOnNegativeButtonClickListener(View.OnClickListener listener);

252

void clearOnNegativeButtonClickListeners();

253

254

// Cancel listeners

255

boolean addOnCancelListener(DialogInterface.OnCancelListener listener);

256

boolean removeOnCancelListener(DialogInterface.OnCancelListener listener);

257

void clearOnCancelListeners();

258

259

// Dismiss listeners

260

boolean addOnDismissListener(DialogInterface.OnDismissListener listener);

261

boolean removeOnDismissListener(DialogInterface.OnDismissListener listener);

262

void clearOnDismissListeners();

263

}

264

265

class MaterialTimePicker.Builder {

266

Builder setTimeFormat(@TimeFormat int format);

267

Builder setHour(@IntRange(from = 0, to = 23) int hour);

268

Builder setMinute(@IntRange(from = 0, to = 59) int minute);

269

Builder setTitleText(@StringRes int titleTextResId);

270

Builder setTitleText(CharSequence charSequence);

271

Builder setPositiveButtonText(@StringRes int positiveButtonTextResId);

272

Builder setPositiveButtonText(CharSequence charSequence);

273

Builder setNegativeButtonText(@StringRes int negativeButtonTextResId);

274

Builder setNegativeButtonText(CharSequence charSequence);

275

Builder setInputMode(int inputMode);

276

Builder setTheme(@StyleRes int themeResId);

277

MaterialTimePicker build();

278

}

279

```

280

281

### Time Picker Input Mode Constants

282

283

```java { .api }

284

public static final int INPUT_MODE_CLOCK = 0;

285

public static final int INPUT_MODE_KEYBOARD = 1;

286

```

287

288

### Time Format Constants

289

290

```java { .api }

291

class TimeFormat {

292

public static final int CLOCK_12H = 0;

293

public static final int CLOCK_24H = 1;

294

}

295

```

296

297

### Usage Example

298

299

```java

300

// Basic time picker

301

MaterialTimePicker timePicker = new MaterialTimePicker.Builder()

302

.setTimeFormat(TimeFormat.CLOCK_12H)

303

.setHour(12)

304

.setMinute(0)

305

.setTitleText("Select appointment time")

306

.build();

307

308

timePicker.addOnPositiveButtonClickListener(v -> {

309

int hour = timePicker.getHour();

310

int minute = timePicker.getMinute();

311

312

// Format time for display

313

String timeString = String.format(Locale.getDefault(), "%02d:%02d", hour, minute);

314

timeButton.setText(timeString);

315

316

// Convert to 12-hour format if needed

317

Calendar calendar = Calendar.getInstance();

318

calendar.set(Calendar.HOUR_OF_DAY, hour);

319

calendar.set(Calendar.MINUTE, minute);

320

321

SimpleDateFormat sdf = new SimpleDateFormat("h:mm a", Locale.getDefault());

322

String formattedTime = sdf.format(calendar.getTime());

323

timeButton.setText(formattedTime);

324

});

325

326

timePicker.show(getSupportFragmentManager(), "time_picker");

327

328

// 24-hour format time picker

329

MaterialTimePicker timePicker24h = new MaterialTimePicker.Builder()

330

.setTimeFormat(TimeFormat.CLOCK_24H)

331

.setHour(14)

332

.setMinute(30)

333

.setTitleText("Meeting time")

334

.setInputMode(MaterialTimePicker.INPUT_MODE_KEYBOARD)

335

.build();

336

337

timePicker24h.addOnPositiveButtonClickListener(v -> {

338

int hour = timePicker24h.getHour();

339

int minute = timePicker24h.getMinute();

340

341

String time24h = String.format(Locale.getDefault(), "%02d:%02d", hour, minute);

342

timeButton.setText(time24h);

343

});

344

345

// Time picker with validation

346

MaterialTimePicker businessHoursPicker = new MaterialTimePicker.Builder()

347

.setTimeFormat(TimeFormat.CLOCK_12H)

348

.setHour(9)

349

.setMinute(0)

350

.setTitleText("Business hours only")

351

.build();

352

353

businessHoursPicker.addOnPositiveButtonClickListener(v -> {

354

int hour = businessHoursPicker.getHour();

355

int minute = businessHoursPicker.getMinute();

356

357

// Validate business hours (9 AM - 5 PM)

358

if (hour < 9 || hour >= 17) {

359

showErrorDialog("Please select a time between 9:00 AM and 5:00 PM");

360

return;

361

}

362

363

// Save valid time

364

saveAppointmentTime(hour, minute);

365

});

366

```

367

368

## Complete Date/Time Selection Example

369

370

Example showing combined date and time selection with validation:

371

372

```java

373

public class AppointmentSchedulerActivity extends AppCompatActivity {

374

private MaterialButton dateButton;

375

private MaterialButton timeButton;

376

private MaterialButton scheduleButton;

377

378

private Long selectedDateMillis;

379

private Integer selectedHour;

380

private Integer selectedMinute;

381

382

@Override

383

protected void onCreate(Bundle savedInstanceState) {

384

super.onCreate(savedInstanceState);

385

setContentView(R.layout.activity_appointment_scheduler);

386

387

initViews();

388

setupDatePicker();

389

setupTimePicker();

390

updateScheduleButton();

391

}

392

393

private void initViews() {

394

dateButton = findViewById(R.id.date_button);

395

timeButton = findViewById(R.id.time_button);

396

scheduleButton = findViewById(R.id.schedule_button);

397

398

scheduleButton.setOnClickListener(v -> scheduleAppointment());

399

}

400

401

private void setupDatePicker() {

402

dateButton.setOnClickListener(v -> {

403

// Create constraints for future dates only

404

CalendarConstraints constraints = new CalendarConstraints.Builder()

405

.setStart(MaterialDatePicker.todayInUtcMilliseconds())

406

.setValidator(DateValidatorPointForward.now())

407

.build();

408

409

MaterialDatePicker<Long> datePicker = MaterialDatePicker.Builder.datePicker()

410

.setTitleText("Select appointment date")

411

.setSelection(selectedDateMillis != null ? selectedDateMillis : MaterialDatePicker.todayInUtcMilliseconds())

412

.setCalendarConstraints(constraints)

413

.build();

414

415

datePicker.addOnPositiveButtonClickListener(selection -> {

416

selectedDateMillis = selection;

417

418

// Format date for display

419

SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy", Locale.getDefault());

420

String dateString = sdf.format(new Date(selection));

421

dateButton.setText(dateString);

422

423

updateScheduleButton();

424

});

425

426

datePicker.show(getSupportFragmentManager(), "date_picker");

427

});

428

}

429

430

private void setupTimePicker() {

431

timeButton.setOnClickListener(v -> {

432

MaterialTimePicker timePicker = new MaterialTimePicker.Builder()

433

.setTimeFormat(TimeFormat.CLOCK_12H)

434

.setHour(selectedHour != null ? selectedHour : 9)

435

.setMinute(selectedMinute != null ? selectedMinute : 0)

436

.setTitleText("Select appointment time")

437

.build();

438

439

timePicker.addOnPositiveButtonClickListener(view -> {

440

int hour = timePicker.getHour();

441

int minute = timePicker.getMinute();

442

443

// Validate business hours

444

if (!isValidBusinessHour(hour)) {

445

showBusinessHoursError();

446

return;

447

}

448

449

selectedHour = hour;

450

selectedMinute = minute;

451

452

// Format time for display

453

Calendar calendar = Calendar.getInstance();

454

calendar.set(Calendar.HOUR_OF_DAY, hour);

455

calendar.set(Calendar.MINUTE, minute);

456

457

SimpleDateFormat timeFormat = new SimpleDateFormat("h:mm a", Locale.getDefault());

458

String timeString = timeFormat.format(calendar.getTime());

459

timeButton.setText(timeString);

460

461

updateScheduleButton();

462

});

463

464

timePicker.show(getSupportFragmentManager(), "time_picker");

465

});

466

}

467

468

private boolean isValidBusinessHour(int hour) {

469

return hour >= 9 && hour < 17; // 9 AM to 5 PM

470

}

471

472

private void showBusinessHoursError() {

473

new MaterialAlertDialogBuilder(this)

474

.setTitle("Invalid Time")

475

.setMessage("Please select a time between 9:00 AM and 5:00 PM")

476

.setPositiveButton("OK", null)

477

.show();

478

}

479

480

private void updateScheduleButton() {

481

boolean canSchedule = selectedDateMillis != null && selectedHour != null && selectedMinute != null;

482

scheduleButton.setEnabled(canSchedule);

483

}

484

485

private void scheduleAppointment() {

486

if (selectedDateMillis == null || selectedHour == null || selectedMinute == null) {

487

return;

488

}

489

490

// Create appointment datetime

491

Calendar appointmentTime = Calendar.getInstance();

492

appointmentTime.setTimeInMillis(selectedDateMillis);

493

appointmentTime.set(Calendar.HOUR_OF_DAY, selectedHour);

494

appointmentTime.set(Calendar.MINUTE, selectedMinute);

495

appointmentTime.set(Calendar.SECOND, 0);

496

appointmentTime.set(Calendar.MILLISECOND, 0);

497

498

// Check if appointment is at least 1 hour in the future

499

Calendar now = Calendar.getInstance();

500

now.add(Calendar.HOUR_OF_DAY, 1);

501

502

if (appointmentTime.before(now)) {

503

new MaterialAlertDialogBuilder(this)

504

.setTitle("Invalid Appointment Time")

505

.setMessage("Appointments must be scheduled at least 1 hour in advance.")

506

.setPositiveButton("OK", null)

507

.show();

508

return;

509

}

510

511

// Show confirmation dialog

512

SimpleDateFormat fullFormat = new SimpleDateFormat("EEEE, MMM dd, yyyy 'at' h:mm a", Locale.getDefault());

513

String appointmentString = fullFormat.format(appointmentTime.getTime());

514

515

new MaterialAlertDialogBuilder(this)

516

.setTitle("Confirm Appointment")

517

.setMessage("Schedule appointment for " + appointmentString + "?")

518

.setPositiveButton("Confirm", (dialog, which) -> {

519

// Save appointment

520

saveAppointment(appointmentTime);

521

showConfirmationSnackbar();

522

})

523

.setNegativeButton("Cancel", null)

524

.show();

525

}

526

527

private void saveAppointment(Calendar appointmentTime) {

528

// Save to database or send to server

529

// Implementation depends on your data storage solution

530

}

531

532

private void showConfirmationSnackbar() {

533

Snackbar.make(findViewById(android.R.id.content),

534

"Appointment scheduled successfully", Snackbar.LENGTH_LONG)

535

.setAction("View Details", v -> {

536

// Navigate to appointment details

537

})

538

.show();

539

}

540

}

541

542

// Date range picker for vacation booking

543

public class VacationBookingFragment extends Fragment {

544

private MaterialButton dateRangeButton;

545

private TextView selectedDatesText;

546

547

private void setupDateRangePicker() {

548

dateRangeButton.setOnClickListener(v -> {

549

// Create constraints - no weekends, future dates only

550

DateValidator weekdaysOnly = date -> {

551

Calendar cal = Calendar.getInstance();

552

cal.setTimeInMillis(date);

553

int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);

554

return dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY;

555

};

556

557

DateValidator futureOnly = DateValidatorPointForward.now();

558

DateValidator combinedValidator = CompositeDateValidator.allOf(

559

Arrays.asList(futureOnly, weekdaysOnly)

560

);

561

562

CalendarConstraints constraints = new CalendarConstraints.Builder()

563

.setValidator(combinedValidator)

564

.setStart(MaterialDatePicker.todayInUtcMilliseconds())

565

.build();

566

567

MaterialDatePicker<Pair<Long, Long>> dateRangePicker =

568

MaterialDatePicker.Builder.dateRangePicker()

569

.setTitleText("Select vacation dates")

570

.setCalendarConstraints(constraints)

571

.build();

572

573

dateRangePicker.addOnPositiveButtonClickListener(selection -> {

574

Long startDate = selection.first;

575

Long endDate = selection.second;

576

577

// Calculate number of days

578

long daysDiff = TimeUnit.MILLISECONDS.toDays(endDate - startDate) + 1;

579

580

SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd", Locale.getDefault());

581

String dateRangeText = dateFormat.format(new Date(startDate)) +

582

" - " + dateFormat.format(new Date(endDate));

583

584

dateRangeButton.setText(dateRangeText);

585

selectedDatesText.setText(daysDiff + " days selected");

586

});

587

588

dateRangePicker.show(getParentFragmentManager(), "date_range_picker");

589

});

590

}

591

}

592

```