DatePicker se bloquea en mi dispositivo cuando se hace clic (con una aplicación personal)

Edit: Comprobé que el problema sólo aparece cuando el teléfono está configurado con el idioma "francés".

Actualmente estoy creando mi propia aplicación y tengo un problema al usar el widget DatePicker en mi teléfono (teléfono con modo de depuración: Samsung S5).

El error recopilado es: java.util.IllegalFormatConversionException

Añado más detalles sobre el problema reunido en logcat:

02-20 10:37:18.255 13029-13029/avappmobile.mytasks E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: avappmobile.mytasks, PID: 13029 java.util.IllegalFormatConversionException: %d can't format java.lang.String arguments at java.util.Formatter.badArgumentType(Formatter.java:1489) at java.util.Formatter.transformFromInteger(Formatter.java:1689) at java.util.Formatter.transform(Formatter.java:1461) at java.util.Formatter.doFormat(Formatter.java:1081) at java.util.Formatter.format(Formatter.java:1042) at java.util.Formatter.format(Formatter.java:1011) at java.lang.String.format(String.java:1803) at android.content.res.Resources.getString(Resources.java:1453) at android.content.Context.getString(Context.java:397) at android.widget.SimpleMonthView$MonthViewTouchHelper.getItemDescription(SimpleMonthView.java:684) at android.widget.SimpleMonthView$MonthViewTouchHelper.onPopulateNodeForVirtualView(SimpleMonthView.java:628) at com.android.internal.widget.ExploreByTouchHelper.createNodeForChild(ExploreByTouchHelper.java:377) at com.android.internal.widget.ExploreByTouchHelper.createNode(ExploreByTouchHelper.java:316) at com.android.internal.widget.ExploreByTouchHelper.access$100(ExploreByTouchHelper.java:50) at com.android.internal.widget.ExploreByTouchHelper$ExploreByTouchNodeProvider.createAccessibilityNodeInfo(ExploreByTouchHelper.java:711) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfVirtualNode(AccessibilityInteractionController.java:1179) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1091) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchDescendantsOfRealNode(AccessibilityInteractionController.java:1087) at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchAccessibilityNodeInfos(AccessibilityInteractionController.java:888) at android.view.AccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdUiThread(AccessibilityInteractionController.java:155) at android.view.AccessibilityInteractionController.access$400(AccessibilityInteractionController.java:53) at android.view.AccessibilityInteractionController$PrivateHandler.handleMessage(AccessibilityInteractionController.java:1236) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:145) at android.app.ActivityThread.main(ActivityThread.java:5834) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183) 

Aquí está mi código DialogFragment:

 public class DatePFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener { DatePicker dp; OnDatePickedListener mCallback; Integer mLayoutId = null; // Interface definition public interface OnDatePickedListener { public void onDatePicked(int textId, int year, int month, int day); } // make sure the Activity implement the OnDatePickedListener public void onAttach(Activity activity) { super.onAttach(activity); try { mCallback = (OnDatePickedListener)activity; } catch (final ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnDatePickedListener"); } } @Override public Dialog onCreateDialog(Bundle savedInstanceState){ mCallback = (OnDatePickedListener)getActivity(); Calendar cal = Calendar.getInstance(); Bundle bundle = this.getArguments(); mLayoutId = bundle.getInt("layoutId"); int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH); int day = cal.get(Calendar.DAY_OF_MONTH); View view = getActivity().getLayoutInflater().inflate(R.layout.datepfragment, null); dp = (DatePicker) view.findViewById(R.id.datePicker); // Create a new instance of DatePickerDialog and return it return new DatePickerDialog(getActivity(),this,year, month, day); } @Override public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { if (mCallback != null) { // Use the date chosen by the user. mCallback.onDatePicked(mLayoutId, year, monthOfYear+1, dayOfMonth); } } } 

Y aquí está el diseño xml asociado:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/txtDPTitle" android:id="@+id/txtDPTitle" android:layout_gravity="center_horizontal" android:gravity="center" android:layout_alignParentTop="true" android:layout_alignParentStart="true" android:layout_marginTop="35dp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btnDPValidate" android:id="@+id/btnDPValidate" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="35dp" /> <DatePicker android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/datePicker" android:layout_gravity="center_horizontal" android:layout_marginLeft="0dp" android:datePickerMode="spinner" android:calendarViewShown="false" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout> 

El hecho es que tenía dos tipos de problemas (que conducen a la caída de la aplicación de todos modos):

  1. Cuando entro mi aplicación y veo directamente al widget de DatePicker, el calendario se muestra correctamente y tan pronto como selecciono un día (digo un día, porque no hay ningún problema cambiando el año por ejemplo), la aplicación se bloquea.
  2. Si voy en otra función el clic para mostrar el DatePicker, se bloquea directamente.

Vi otros hilos como: DatePicker accidente en samsung con android 5.0

En este hilo no obtengo suficiente información, ya que la solución dada permite mostrar sólo la vista de hilandero del calendario y además el hilador mostrado no está centrado y presentado como el mío (puesto directamente en la parte superior de la pantalla).

Si necesita más detalles por favor pregúnteme.

Gracias. Alex.

Es un fallo de Samsung en su implementación de Lollipop UI. Afecta a Galaxy S4, S5, Nota 3 y probablemente más dispositivos. Para nosotros ocurre en los lenguajes de_DE y de_AT, pero parece ser un problema que afecta a varios idiomas.

Lo arreglé forzando a los dispositivos Samsung a usar el tema Holo para el selector de fechas. En nuestro DatePickerFragment:

 @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { /* calendar code here */ Context context = getActivity(); if (isBrokenSamsungDevice()) { context = new ContextThemeWrapper(getActivity(), android.R.style.Theme_Holo_Light_Dialog); } return new DatePickerDialog(context, this, year, month, day); } private static boolean isBrokenSamsungDevice() { return (Build.MANUFACTURER.equalsIgnoreCase("samsung") && isBetweenAndroidVersions( Build.VERSION_CODES.LOLLIPOP, Build.VERSION_CODES.LOLLIPOP_MR1)); } private static boolean isBetweenAndroidVersions(int min, int max) { return Build.VERSION.SDK_INT >= min && Build.VERSION.SDK_INT <= max; } 

Gracias a Samsung, sus usuarios no obtendrán el selector de fechas diseñado por el material.

Tengo un FIX PATCH posible que permitirá al usuario seguir usando la librería Lollipop.

Sólo podía reproducir el bloqueo al permitir TalkBack en un S4 con Android 5.0.1 francés, parece que Samsung está pasando toda la fecha a la cadena de selección de elementos utilizados para accesibilidad en lugar del número de día.

Basándonos en la solución @mreichelt de envolver el contexto, podemos anular la función getString (id, args) y realmente solucionar el problema.

 context = new ContextWrapper(getActivity()) { private Resources wrappedResources; @Override public Resources getResources() { Resources r = super.getResources(); if(wrappedResources == null) { wrappedResources = new Resources(r.getAssets(), r.getDisplayMetrics(), r.getConfiguration()) { @NonNull @Override public String getString(int id, Object... formatArgs) throws NotFoundException { try { return super.getString(id, formatArgs); } catch (IllegalFormatConversionException ifce) { Log.e("DatePickerDialogFix", "IllegalFormatConversionException Fixed!", ifce); String template = super.getString(id); template = template.replaceAll("%" + ifce.getConversion(), "%s"); return String.format(getConfiguration().locale, template, formatArgs); } } }; } return wrappedResources; } }; 

Por lo tanto, si proporcionamos este contexto al constructor DatePickerDialog, se solucionará el problema.

UPDATE: Este problema también aparece en webview cuando las páginas contienen campos de entrada de fecha, la mejor manera que encontré que resolver ambos problemas (diálogo y webview) es anular la función de getResources de actividad como este:

 @Override public Resources getResources() { if (wrappedResources == null) { wrappedResources = wrapResourcesFixDateDialogCrash(super.getResources()); } return wrappedResources; } public Resources wrapResourcesFixDateDialogCrash(Resources r) { return new Resources(r.getAssets(), r.getDisplayMetrics(), r.getConfiguration()) { @NonNull @Override public String getString(int id, Object... formatArgs) throws NotFoundException { try { return super.getString(id, formatArgs); } catch (IllegalFormatConversionException ifce) { Log.i("DatePickerDialogFix", "IllegalFormatConversionException Fixed!", ifce); String template = super.getString(id); template = template.replaceAll("%" + ifce.getConversion(), "%s"); return String.format(getConfiguration().locale, template, formatArgs); } } }; } 

Nota: La solución incluye recursos almacenados en caché, lo que significa que si tiene eventos en la aplicación que cambian los recursos (como el cambio de idioma) que necesite para actualizar los recursos almacenados en caché.

Encuentro una solución, jugando con las variables de configuración y de la configuración regional.

Solo modifico el idioma si detecto que es un idioma francés ("fr") y lo reemplazo con un idioma inglés ("en"), luego vuelvo a ponerlo en normal una vez fuera del DatePicker DialogFragment.

 public void showDatePickerDialog(int layoutId) { Integer year = cal.get(Calendar.YEAR); Integer month = cal.get(Calendar.MONTH); Integer day = cal.get(Calendar.DAY_OF_MONTH); // Due to an issue with DatePicker and fr language (locale), if locale language is french, this is changed to us to make the DatePicker working if(Locale.getDefault().getLanguage().toString().equals(FR_LANG_CONTEXT)){ Configuration config = new Configuration(); config.locale = new Locale(US_LANG_CONTEXT); getApplicationContext().getResources().updateConfiguration(config, null); } Bundle bundle = createDatePickerBundle(layoutId, year, month, day); DialogFragment newFragment = new DatePFragment(); newFragment.setArguments(bundle); newFragment.show(fm, Integer.toString(layoutId)); } 

Y una vez que salgo

 @Override public void onDatePicked(int LayoutId, int year, int month, int day){ // Once out from the DatePicker, if we were working on a fr language, we update back the configuration with fr language. if(Locale.getDefault().getLanguage().toString().equals(FR_LANG_CONTEXT)){ Configuration config = new Configuration(); config.locale = new Locale(FR_LANG_CONTEXT); getApplicationContext().getResources().updateConfiguration(config, null); } String date = day + "/" + month + "/" + year; txtTaskDate.setText(date); } 

Olvídate del selector de fechas incorporado. IMHO conmutación de la configuración regional no es una solución, porque quiero tener el selector en la configuración actual. Sólo hay una manera de deshacerse del accidente: utilice una biblioteca que proporcione una implementación independiente.

Para un fragmento de selector de fechas: https://github.com/flavienlaurent/datetimepicker

Para un widget selector de fechas: https://github.com/SingleCycleKing/CustomTimePicker (esto es más un punto de partida que una solución lista para usar)

Observé un problema menor con la solución de @mreichelt. El diálogo se mostró sin que el título mostrara la fecha seleccionada (incluido el día de la semana). Definir explícitamente el título, e inmediatamente ajustar la fecha de nuevo resuelto ese problema para mí.

 Context context = getActivity(); if (isBrokenSamsungDevice()) { context = new ContextThemeWrapper(getActivity(), android.R.style.Theme_Holo_Light_Dialog); } DatePickerDialog datePickerDialog = new DatePickerDialog(context, this, year, month, day); if (isBrokenSamsungDevice()) { datePickerDialog.setTitle(""); datePickerDialog.updateDate(year, month, day); } return datePickerDialog; 

Basado en mreichelts respuesta he creado un SupportDatePickerDialog.java que envuelve aroudn DatePickerDialog y corrige automáticamente el contexto de android 5.0 y 5.1.

Ver el esencial

  • Android: Error de instalación: INSTALL_FAILED_INSUFFICIENT_STORAGE
  • Depuración USB: Cómo revocar la opción "Permitir siempre desde este equipo"
  • ¿Cómo conectarse con un dispositivo Bluetooth pareado programático en Android?
  • Uso del selector de dispositivos Android Bluetooth
  • Lista de dispositivos BLE para Android después de escanear dispositivos
  • No hay controlador para Asus MeMO Pad para hacer la depuración de aplicaciones con eclipse
  • No se puede conectar a bluetooth Dispositivos de dispositivos de salud utilizando Android BluetoothProfile
  • Android SDK DDMS en Eclipse no reconoce mi teléfono Android
  • Confusión de id de dispositivo de android
  • Ejecutar aplicaciones de Android en el dispositivo de hardware de Android Studio por USB
  • No se puede encontrar el dispositivo android con el comando "dispositivos adb"
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.