wake-up-neo.net

DatePickerDialog Holo-Styling auf Android 7 Nougat fehlgeschlagen

Aus unserer Kundennachfrage möchten wir den HOLO-Stil für alle Android-Betriebssystemversionen im DatePickerDialog beibehalten, sth wie: DatePicker unter Android 7-

Auf Android 7 scheint es jedoch nicht richtig zu funktionieren:

DatePicker unter Android 7

Aus meiner Implementierung:

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

Es funktioniert gut für frühere Android 7. Hat jemand das gleiche Problem?

Bearbeitet: Lösung wurde in API 25 behoben https://code.google.com/u/106133255289400340786/

11
Trung Le

Ich benutze eine DatePickerDialog, um Benutzer an ihren Geburtstagen zu bitten. Leider habe ich eine Reihe von Beschwerden von Benutzern bezüglich des Material-Themedialogs erhalten, als ich es ausprobierte. Daher ist es für mich keine Option, darauf zu wechseln: Ich muss mich an den Holo-Themedialog halten.

Es stellt sich heraus, dass Android 7.0 mit einem Fehler geliefert wurde: Der Versuch, das Holo-Design auf dieser Plattform zu verwenden, greift stattdessen auf ein broken Material-Theme für die DatePickerDialog zurück. Sehen Sie diese zwei Fehlerberichte:

Ich habe eine modifizierte Form von diese Problemumgehung durch Jeff Lockhart verwendet, auf die in diesen Fehlerberichten verwiesen wird:

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;
    }
}

Was das macht, ist:

  • Rufen Sie das private DatePicker mDatePicker-Feld ab, das zu diesem Dialog gehört
  • Rufen Sie das private DatePickerDelegate mDelegate-Feld ab, das zu diesem Dialog gehört
  • Stellen Sie sicher, dass der Delegat nicht bereits eine Instanz von DatePickerSpinnerDelegate ist (der Typ des gewünschten Delegaten).
  • Entfernen Sie alle Ansichten aus der Variablen DatePicker, da es sich um die Widgets für Materialkalender handelt
  • Erstellen Sie eine neue Instanz von DatePickerSpinnerDelegate und weisen Sie sie dem Feld mDelegate von mDatePicker dieses Dialogfelds zu
  • Initialisieren Sie mDatePicker erneut mit Kalenderinformationen und einigen Parametern, damit die Spinner aufgeblasen werden

Um diese Problemumgehung zu verwenden, erstelle ich eine ContextThemeWrapper um meine Context, mit der ich ein Thema festlegen kann, in diesem Fall 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)
);

Hinweise :

  • Dies verwendet Reflektion, um auf private Felder zuzugreifen. Im Allgemeinen ist dies kein robuster Ansatz und Sie können sich nicht darauf verlassen. Ich minimiere das Risiko hier durch 1) Beschränkung auf eine einzige SDK-Version, v24; und 2) Umsetzen des gesamten Reflexionscodes in einen try {...} catch (Exception e) {/* NOP */}-Block. Wenn also eine der Reflexionen fehlschlägt, geschieht nichts und der (leider gebrochene) Standard-Material-Fallback wird verwendet.
  • Die obigen Fehlerberichte behaupten, dass dieses Problem in Android 7.1 (SDK 25) behoben wurde. Ich habe das nicht getestet.
  • Der ursprünglicher Workaround-Code war für TimePickerDialog, bei dem ein ähnliches Problem auftrat. Ich habe es geändert, um stattdessen mit DatePickerDialog zu arbeiten, und die Lösung wurde so vereinfacht, dass sie weniger generisch und spezifischer für meinen genauen Anwendungsfall ist. Sie können jedoch die vollständigere Originalversion verwenden und für Date anstelle von Time anpassen.
26
savanto

Datumsauswahl hat einen eigenen Stil https://developer.Android.com/reference/Android/R.style.html#Widget_DatePicker

Ich denke, Sie brauchen R.style.Widget.Holo.DatePicker anstelle von AlertDialog.THEME_HOLO_LIGHT. Möglicherweise müssen Sie möglicherweise einen eigenen leeren Stil mit @Android:style/Widget.Holo.DatePicker als übergeordnetem Element erstellen und diesen verwenden. 

0
Ben

Zusätzlich zum Lösung von savanto : Ab API 28 ist Holo Light Theme veraltet. Wenn Sie auf API 21/Lollipop-Geräte und neuere Geräte abzielen, sollten Sie Materialdesignstile verwenden. Aktivieren Sie diese Lösung . Ich habe diese Stile definiert und dann im ContextThemeWrapper mit R.style.dlg_datePicker Verknüpft:

<style name="dlg_datePicker" parent="Theme.AppCompat.Light.Dialog">
    <item name="Android:datePickerStyle">@style/datePicker_style</item>
    <item name="Android:textSize">18sp</item>
</style>

<style name="datePicker_style" parent="Android:Widget.Material.Light.DatePicker">
    <item name="Android:datePickerMode">spinner</item>
</style>
0
guglhupf

Versuchen Sie zu folgen, dass es funktioniert. Ich habe getestet Und es hat keine leeren Lücken.

Auch mDatePickerListener ist mein DatePickerListner, den Sie erstellen können.

//Setting theme to Android.R.style.Theme_Holo_Light_Dialog  
Calendar cal = Calendar.getInstance(TimeZone.getDefault());
DatePickerDialog datePicker = new DatePickerDialog(getContext(), Android.R.style.Theme_Holo_Light_Dialog,
                    mDatePickerListener,
                    cal.get(Calendar.YEAR),
                    cal.get(Calendar.MONTH),
                    cal.get(Calendar.DAY_OF_MONTH));

datePicker.setCancelable(false);
datePicker.setTitle(getString(R.string.select_your_dob));
//This line is important to remove blank gaps
datePicker.getWindow().setBackgroundDrawable(new ColorDrawable(Android.graphics.Color.TRANSPARENT));
datePicker.show();
0
Akshay Taru