Подтвердить что ты не робот

При нажатии кнопки DatePicker на моем устройстве (с личным приложением)

Изменить: я проверил, что проблема возникает только в том случае, если на телефоне установлен "французский" язык.

В настоящее время я создаю свое собственное приложение, и у меня проблема при использовании виджета DatePicker на моем телефоне (телефон режима отладки: Samsung S5).

Сбор ошибки: java.util.IllegalFormatConversionException

Я добавлю дополнительные сведения о проблеме, собранной в 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)

Вот мой код диалога:

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

И вот связанный макет xml:

<?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>

Дело в том, что у меня были две проблемы (приводящие к сбою приложения в любом случае):

  • Когда я вхожу в свое приложение и напрямую перехожу к виджету DatePicker, календарь отображается правильно, и как только я выбираю день (я говорю день, потому что нет проблем с изменением года, например), приложение вылетает.
  • Если я перейду к другой функции, нажмите, чтобы отобразить DatePicker, он сработает напрямую.

Я видел другие темы, такие как: Ошибка DatePicker в samsung с Android 5.0

В этом потоке я не получаю достаточной информации, так как данное решение позволяет отображать только кадрирующий вид календаря, и, кроме того, показанный счетчик не центрирован и представлен как мой (помещается непосредственно в верхнюю часть экрана).

Если вам нужна дополнительная информация, пожалуйста, спросите меня.

Спасибо. Алекс.

4b9b3361

Ответ 1

Это ошибка Samsung в реализации Lollipop UI. Это влияет на Galaxy S4, S5, Note 3 и, вероятно, на большее количество устройств. Для нас это происходит на языках de_DE и de_AT, но, похоже, это проблема, которая затрагивает несколько языков.

Я исправил это, заставив устройства Samsung использовать тему Holo для выбора даты. В нашем 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;
}

Благодаря Samsung, их пользователи не получат хороший выбор для выбора материалов.

Ответ 2

У меня есть возможность FIX PATCH, которая фактически позволит пользователю продолжать использовать библиотеку Lollipop.

Я мог только воспроизвести крах, включив TalkBack на S4 с Android 5.0.1 на французском языке, кажется, что Samsung передает всю дату в строку выбора позиции, используемую для удобства доступа, а не номер дня.

Основываясь на решении обрезки контекста @mreichelt, мы можем переопределить функцию getString (id, args) и исправить эту проблему.

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

Итак, если мы предоставим этот контекст конструктору DatePickerDialog, он устранит проблему.

UPDATE: Эта проблема также появляется в веб-просмотре, когда страницы содержат поля ввода даты, лучший способ, который я нашел, чтобы решить обе проблемы (диалог и веб-просмотр), - это переопределить функцию активности GetResources следующим образом:

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

Примечание. Решение включает в себя кешированные ресурсы - это означает, что при добавлении событий в приложении изменения ресурсов (например, изменение языка) вам может потребоваться обновить кэшированные ресурсы.

Ответ 3

Я нашел обходное решение, играя с конфигурацией и переменными Locale.

Я просто модифицирую язык, если я определяю его на французском ( "fr" ) языке и заменяю его английским ( "en" ) языком, а затем возвращаюсь к нормальной работе из диалогового окна 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));
    }

И как только я выхожу из

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

Ответ 4

Забудьте о встроенном наборе дат. ИМХО переключение языка не является решением, потому что я хочу иметь сборщик в текущем регионе. Существует только один способ избавиться от сбоя: используйте библиотеку, которая обеспечивает независимую реализацию.

Для фрагмента выбора даты: https://github.com/flavienlaurent/datetimepicker

Для виджета для выбора даты: https://github.com/SingleCycleKing/CustomTimePicker (это скорее отправная точка, чем готовое к использованию решение)

Ответ 5

Я заметил одну небольшую проблему с решением @mreichelt. Диалог был отображен без заголовка, показывающего текущую выбранную дату (включая день недели). Явная настройка заголовка и немедленная установка даты снова решили эту проблему для меня.

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;

Ответ 7

это также происходит, когда вы программно определяете min date дальше, чем max date

datePicker.getDatePicker().setMaxDate(15000000);

datePicker.getDatePicker().setMinDate(16000000);