2016-10-05 13 views
8

Z naszego zapotrzebowania klientów chcemy zachować styl HOLO na DatePickerDialog dla wszystkich wersji Android OS, czegoś jak: DatePicker on Android 7-DatePickerDialog Holo stylizacji zawiodły na Androida 7 nugat

Ale wydaje się nie działać prawidłowo na Androidzie 7:

DatePicker on Android 7

Z mojego realizacji:

new DatePickerDialog(getContext(), AlertDialog.THEME_HOLO_LIGHT, 
         mCalendarPickerListener, 
         calendar.get(Calendar.YEAR), 
         calendar.get(Calendar.MONTH), 
         calendar.get(Calendar.DAY_OF_MONTH)); 

Działa dobrze dla wcześniejszego Androida 7. Każdy ma ten sam problem?

Zmieniano: rozwiązanie okazuje się być ustalona w API 25 https://code.google.com/u/106133255289400340786/

+0

Czy zaktualizowałeś swoją bibliotekę wsparcia? – Shuddh

+0

@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 –

+0

Występuję również w tym problemie. Ktoś ma rozwiązanie? – FinalFive

Odpowiedz

16

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.
+0

pomimo, że jest to problem związany z pracą, ale dzięki, brzmi to nieźle, –

+0

brzmi jak poprawny FIX powinien być za pomocą interfejsu API 25 https://code.google.com/u/106133255289400340786/ –

+0

set datner nie działa, zawsze wraca data uruchomienia – WolfJee

0

Data zbieracz ma swój odrębny styl https://developer.android.com/reference/android/R.style.html#Widget_DatePicker

myślę, że trzeba R.style.Widget.Holo.DatePicker zamiast AlertDialog.THEME_HOLO_LIGHT. Możliwe, że będziesz musiał utworzyć własny pusty styl, który ma wartość @android:style/Widget.Holo.DatePicker, ponieważ jest nadrzędny i używa go.

+0

To nie działa poprawnie, zamiast tego pokazuje pełny kalendarz z pustymi przerwami. –

Powiązane problemy