Używam DatePickerDialog
do monitowania użytkowników o ich urodziny. Niestety, otrzymałem od użytkowników wiele skarg na temat dialogów związanych z materiałami podczas ich testowania, więc przełączenie się na nie jest dla mnie opcją: muszę trzymać się dialogów o tematyce Holo.
Okazuje się, że Android 7.0 dostarczany z błędem: próbuje użyć motywu Holo na tej platformie, a nie spadnie z powrotem do korzystania z uszkodzony Materiał motyw dla DatePickerDialog
.Zobacz te raporty dwóch błędów:
użyłem zmodyfikowaną formę this workaround by Jeff Lockhart wymieniony w tych raportów o błędach:
private static final class FixedHoloDatePickerDialog extends DatePickerDialog {
private FixedHoloDatePickerDialog(Context context, OnDateSetListener callBack,
int year, int monthOfYear, int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
// Force spinners on Android 7.0 only (SDK 24).
// Note: I'm using a naked SDK value of 24 here, because I'm
// targeting SDK 23, and Build.VERSION_CODES.N is not available yet.
// But if you target SDK >= 24, you should have it.
if (Build.VERSION.SDK_INT == 24) {
try {
final Field field = this.findField(
DatePickerDialog.class,
DatePicker.class,
"mDatePicker"
);
final DatePicker datePicker = (DatePicker) field.get(this);
final Class<?> delegateClass = Class.forName(
"android.widget.DatePicker$DatePickerDelegate"
);
final Field delegateField = this.findField(
DatePicker.class,
delegateClass,
"mDelegate"
);
final Object delegate = delegateField.get(datePicker);
final Class<?> spinnerDelegateClass = Class.forName(
"android.widget.DatePickerSpinnerDelegate"
);
if (delegate.getClass() != spinnerDelegateClass) {
delegateField.set(datePicker, null);
datePicker.removeAllViews();
final Constructor spinnerDelegateConstructor =
spinnerDelegateClass.getDeclaredConstructor(
DatePicker.class,
Context.class,
AttributeSet.class,
int.class,
int.class
);
spinnerDelegateConstructor.setAccessible(true);
final Object spinnerDelegate = spinnerDelegateConstructor.newInstance(
datePicker,
context,
null,
android.R.attr.datePickerStyle,
0
);
delegateField.set(datePicker, spinnerDelegate);
datePicker.init(year, monthOfYear, dayOfMonth, this);
datePicker.setCalendarViewShown(false);
datePicker.setSpinnersShown(true);
}
} catch (Exception e) { /* Do nothing */ }
}
}
/**
* Find Field with expectedName in objectClass. If not found, find first occurrence of
* target fieldClass in objectClass.
*/
private Field findField(Class objectClass, Class fieldClass, String expectedName) {
try {
final Field field = objectClass.getDeclaredField(expectedName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) { /* Ignore */ }
// Search for it if it wasn't found under the expectedName.
for (final Field field : objectClass.getDeclaredFields()) {
if (field.getType() == fieldClass) {
field.setAccessible(true);
return field;
}
}
return null;
}
}
Co to jest:
- Uzyskaj prywatny
DatePicker mDatePicker
pole należące do tej dialogowym
- Pobierz prywatną
DatePickerDelegate mDelegate
boiska należącej do tej dialogowym
- Sprawdź, czy delegat nie jest już instancją
DatePickerSpinnerDelegate
(typ delegata chcemy)
- Usuń wszystko widoki z
DatePicker
, ponieważ są materiałem widgety kalendarza
- Załóż nową instancję
DatePickerSpinnerDelegate
i przypisać go do dziedziny mDatePicker
tego dialogowym
- Re inicjalizacji
mDelegate
ialize mDatePicker
z informacją kalendarzowym, a niektóre params, aby zmusić go do nadmuchać błystki
Aby skorzystać z tego rozwiązania, tworzę ContextThemeWrapper
wokół mojego Context
, która pozwala mi ustawić motyw w tym przypadku Holo:
final Context themedContext = new ContextThemeWrapper(
this.getContext(),
android.R.style.Theme_Holo_Light_Dialog
);
final DatePickerDialog dialog = new FixedHoloDatePickerDialog(
themedContext,
datePickerListener,
calender.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
Uwagi:
- Wykorzystuje to odbicie, aby uzyskać dostęp do pól prywatnych. Zasadniczo nie jest to podejście solidne i nie można na nim liczyć. Ograniczam tu ryzyko, 1) ograniczając to do jednej wersji SDK, v24; i 2) zawijanie całego kodu odbicia w bloku
try {...} catch (Exception e) {/* NOP */}
, więc jeśli jakiekolwiek odbicie nie powiedzie się, nic się nie stanie i użyty zostanie (niestety zepsuty) domyślny powrót materiału.
- Powyższe raporty o błędach twierdzą, że problem ten został naprawiony w systemie Android 7.1 (SDK 25). Nie testowałem tego.
- Urządzenie original workaround code było dla modelu
TimePickerDialog
, które cierpiało na podobny problem. Zmodyfikowałem go do pracy z DatePickerDialog
zamiast tego, a także uprościłem rozwiązanie, aby było mniej ogólne i bardziej szczegółowe dla mojego dokładnego przypadku użycia. Możesz jednak użyć bardziej kompletnej oryginalnej wersji i po prostu zmienić ją na Date
zamiast Time
.
Czy zaktualizowałeś swoją bibliotekę wsparcia? – Shuddh
@Shuddh już wypróbowałem, ale wciąż tak samo :) Próbowałem "24 .2.1" https://developer.android.com/topic/libraries/support-library/revisions.html –
Występuję również w tym problemie. Ktoś ma rozwiązanie? – FinalFive