三星设备上的自定义 DatePicker 崩溃 运行 Android API 级别 21 和 22

Custom DatePicker Crashes on Samsung Devices Running Android API Level 21 & 22

我在 Crashlytics 中收到了这份崩溃报告:

SimpleMonthView.java line 684
android.widget.SimpleMonthView$MonthViewTouchHelper.getItemDescription
-----
Fatal Exception: java.util.IllegalFormatConversionException
%d can't format java.lang.String arguments

我见过 但我无法在我的习惯中做到 class。

It's a Samsung bug in their Lollipop UI implementation. It affects Galaxy S4, S5, Note 3 and probably more devices.

我也看到了 ,但是你可以看到我已经在 newInstance 静态方法中设置了年份范围。

这是从 Here:

复制的我的自定义 DatePicker Class
public class MyDatePickerDialog extends DialogFragment implements OnClickListener {

private static final String TAG = "DatePickerDialog";
public static LinearLayout dayMonth;
public static int maxMonth;
static int minYear;
static int maxYear;
static int id;
private static TextView dayTV;
private static TextView monthTV;
private static TextView yearTV;
private static TextView dayNameTV;
private static int mColor = 0;
private static GradientDrawable circle;
private static Typeface mTypeFace = null;
private static OnDateSetListener mCallBack;
private TextView doneTV;
private TextView cancelTV;
FragmentManager fragmentManager;

public MyDatePickerDialog() {

}

public static MyDatePickerDialog newInstance(OnDateSetListener onDateSetListener, int requestID) {

    JDF jdf = new JDF();
    return newInstance(onDateSetListener, requestID, jdf.getIranianYear(), jdf.getIranianMonth(), jdf.getIranianDay());
}

public static MyDatePickerDialog newInstance(OnDateSetListener onDateSetListener, boolean darkTheme) {
    return newInstance(onDateSetListener, 0);
}

public static MyDatePickerDialog newInstance(OnDateSetListener onDateSetListener) {
    return newInstance(onDateSetListener, 0);
}

public static MyDatePickerDialog newInstance(OnDateSetListener onDateSetListener, int requestID, int year, int month, int day) {

    MyDatePickerDialog datePickerDialog = new MyDatePickerDialog();

    mCallBack = onDateSetListener;
    Date.setDate(year, month, day, false);

    int thisYear = new JDF().getIranianYear();
    datePickerDialog.setYearRange(thisYear, thisYear + 1);

    id = requestID;
    mColor = 0;
    mTypeFace = null;
    maxMonth = 0;

    return datePickerDialog;
}

public static void updateDisplay(int year, int month, int day) {
    try {
        MyDatePickerDialog.dayTV.setText(String.valueOf(day));
        MyDatePickerDialog.monthTV.setText(JDF.monthNames[month - 1]);
        MyDatePickerDialog.yearTV.setText(String.valueOf(year));
        MyDatePickerDialog.dayNameTV.setText(new JDF().getIranianDayName(year, month, day));
    } catch (Exception e) {
        Log.i(TAG, "updateDisplay -----> " + e);
    }
}

@Override
public void onStart() {
    setRetainInstance(true);
    super.onStart();
}

@Override
public int getTheme() {
    return super.getTheme();
}

private android.app.DatePickerDialog.OnDateSetListener dateSetListener() {
    return new android.app.DatePickerDialog.OnDateSetListener() {
        @Override
        public void onDateSet(DatePicker datePicker, int i, int i1, int i2) {

        }
    };
}

@Override
public View onCreateView(LayoutInflater layoutInflater,
                         ViewGroup container, Bundle savedInstanceState) {

    getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);

    final View view = layoutInflater.inflate(R.layout.date_layout,
            container, false);

    if (mColor == 0)
        try {
            mColor = getResources().getColor(R.color.blue);
        } catch (Resources.NotFoundException e) {
            //Sentry MyLog.w(TAG, new Throwable().getStackTrace()[0].getLineNumber(), e);
            e.printStackTrace();
        }

    circle = new GradientDrawable();
    try {
        circle.setCornerRadius(getResources().getDimension(
                R.dimen.circle_radius));
        circle.setColor(mColor);
        circle.setAlpha(50);
    } catch (Resources.NotFoundException e) {
        e.printStackTrace();
    }

    fragmentManager = getChildFragmentManager();

    dayTV = view.findViewById(R.id.day);
    monthTV = view.findViewById(R.id.month);
    yearTV = view.findViewById(R.id.year);
    dayNameTV = view.findViewById(R.id.day_name);
    doneTV = view.findViewById(R.id.done);
    cancelTV = view.findViewById(R.id.cancel);
    dayMonth = view.findViewById(R.id.dayMonthBack);

    if (mTypeFace != null) {
        dayTV.setTypeface(mTypeFace);
        monthTV.setTypeface(mTypeFace);
        yearTV.setTypeface(mTypeFace);
        dayNameTV.setTypeface(mTypeFace);
        doneTV.setTypeface(mTypeFace);
        cancelTV.setTypeface(mTypeFace);
    }

    doneTV.setTextColor(mColor);
    cancelTV.setTextColor(mColor);

    view.findViewById(R.id.blue_card)
            .setBackgroundColor(mColor);

    dayMonth.setOnClickListener(this);
    yearTV.setOnClickListener(this);
    doneTV.setOnClickListener(this);
    cancelTV.setOnClickListener(this);

    updateDisplay(Date.getYear(), Date.getMonth(), Date.getDay());

    view.findViewById(R.id.dayMonthBack).performClick();

    return view;
}

@Override
public void onClick(View v) {
    if (v.getId() == R.id.year) {
        dayTV.setAlpha(0.5f);
        monthTV.setAlpha(0.5f);
        yearTV.setAlpha(1f);
        YearMainFragment yearMainFragment = new YearMainFragment();
        switchFragment(yearMainFragment.yearMainFragement(minYear, maxYear));
    } else if (v.getId() == R.id.dayMonthBack) {
        dayTV.setAlpha(1f);
        monthTV.setAlpha(1f);
        yearTV.setAlpha(0.5f);
        switchFragment(new MonthMainFragment());
    } else if (v.getId() == R.id.done) {
        if (mCallBack != null) {
            Calendar calendar = Calendar.getInstance();
            JDF j = new JDF();
            j.setIranianDate(Date.getYear(), Date.getMonth(), Date.getDay());
            try {
                calendar = j.getGregorianCalendar(Date.getYear(),
                        Date.getMonth(), Date.getDay());
            } catch (Exception e) {
                Log.i(TAG, "-----> Exception onClosedItemClick: " + e);
            }
            mCallBack.onDateSet(id, calendar, Date.getYear(), Date.getMonth(), Date.getDay(), dayNameTV.getText().toString());
        }
        dismissAllowingStateLoss();
    } else if (v.getId() == R.id.cancel) {
        dismissAllowingStateLoss();
    }
}

void switchFragment(final Fragment fragment) {
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.frame_container, fragment);
    transaction.addToBackStack(null);
    try {
        transaction.commit();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/*  ---------- Getters & Setters ----------  */

public static GradientDrawable getCircle() {
    return circle;
}

public static int getColor() {
    return mColor;
}

public static Typeface getTypeFace() {
    return mTypeFace;
}

public void setTypeFace(Typeface typeface) {
    mTypeFace = typeface;
}

public static int getRequestID() {
    return id;
}

public void setRequestID(int requestID) {
    id = requestID;
}

public void setMainColor(int color) {
    mColor = color;
}

public void setYearRange(int _minYear, int _maxYear) {
    minYear = _minYear;
    maxYear = _maxYear;
}

public void setInitialDate(int year, int month, int day) {
    Date.setDate(year, month, day, false);
}

public void setInitialDate(Calendar calendar) {
    JDF jdf = new JDF();
    jdf.setGregorianDate(calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH) + 1,
            calendar.get(Calendar.DAY_OF_MONTH));
    Date.setDate(jdf.getIranianYear(), jdf.getIranianMonth(),
            jdf.getIranianDay(), false);
}

public void setFutureDisabled(Boolean disabled) {
    if (disabled) {
        JDF jdf = new JDF();
        maxMonth = jdf.getIranianMonth() - 11;
        maxYear = jdf.getIranianYear();

        if (minYear > maxYear)
            minYear = maxYear - 1;

        if (Date.getMonth() > jdf.getIranianMonth())
            Date.setMonth(jdf.getIranianMonth());
        if (Date.getDay() > jdf.getIranianDay())
            Date.setDay(jdf.getIranianDay());
        if (Date.getYear() > jdf.getIranianYear())
            Date.setYear(jdf.getIranianYear());
    } else
        maxMonth = 0;
}

/*  ---------- End of Getters & Setters ----------  */

public interface OnDateSetListener {
    void onDateSet(int id, Calendar calendar, int year,
                   int month, int day, String dayName);
}
}

我该如何解决这个问题?

更新

堆栈跟踪:

# OS Version: 5.1.1
# Device: Galaxy Core Prime
# RAM Free: 38.8%
# Disk Free: 12.8%

#0. Crashed: main
at java.util.Formatter.badArgumentType + 1489(Formatter.java:1489)
at java.util.Formatter.transformFromInteger + 1689(Formatter.java:1689)
at java.util.Formatter.transform + 1461(Formatter.java:1461)
at java.util.Formatter.doFormat + 1081(Formatter.java:1081)
at java.util.Formatter.format + 1042(Formatter.java:1042)
at java.util.Formatter.format + 1011(Formatter.java:1011)
at java.lang.String.format + 1803(String.java:1803)
at android.content.res.Resources.getString + 668(Resources.java:668)
at android.content.Context.getString + 390(Context.java:390)
at android.widget.SimpleMonthView$MonthViewTouchHelper.getItemDescription + 684(SimpleMonthView.java:684)
at android.widget.SimpleMonthView$MonthViewTouchHelper.onPopulateNodeForVirtualView + 628(SimpleMonthView.java:628)
at com.android.internal.widget.ExploreByTouchHelper.createNodeForChild + 397(ExploreByTouchHelper.java:397)
at com.android.internal.widget.ExploreByTouchHelper.createNode + 324(ExploreByTouchHelper.java:324)
at com.android.internal.widget.ExploreByTouchHelper.access0 + 49(ExploreByTouchHelper.java:49)
at com.android.internal.widget.ExploreByTouchHelper$ExploreByTouchNodeProvider.createAccessibilityNodeInfo + 741(ExploreByTouchHelper.java:741)
at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchAccessibilityNodeInfos + 805(AccessibilityInteractionController.java:805)
at android.view.AccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdUiThread + 155(AccessibilityInteractionController.java:155)
at android.view.AccessibilityInteractionController.access0 + 53(AccessibilityInteractionController.java:53)
at android.view.AccessibilityInteractionController$PrivateHandler.handleMessage + 1146(AccessibilityInteractionController.java:1146)
at android.os.Handler.dispatchMessage + 102(Handler.java:102)
at android.os.Looper.loop + 135(Looper.java:135)
at android.app.ActivityThread.main + 5910(ActivityThread.java:5910)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke + 372(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run + 1405(ZygoteInit.java:1405)
at com.android.internal.os.ZygoteInit.main + 1200(ZygoteInit.java:1200)

--

Fatal Exception: java.util.IllegalFormatConversionException: %d can't format java.lang.String arguments
at java.util.Formatter.badArgumentType + 1489(Formatter.java:1489)
at java.util.Formatter.transformFromInteger + 1689(Formatter.java:1689)
at java.util.Formatter.transform + 1461(Formatter.java:1461)
at java.util.Formatter.doFormat + 1081(Formatter.java:1081)
at java.util.Formatter.format + 1042(Formatter.java:1042)
at java.util.Formatter.format + 1011(Formatter.java:1011)
at java.lang.String.format + 1803(String.java:1803)
at android.content.res.Resources.getString + 668(Resources.java:668)
at android.content.Context.getString + 390(Context.java:390)
at android.widget.SimpleMonthView$MonthViewTouchHelper.getItemDescription + 684(SimpleMonthView.java:684)
at android.widget.SimpleMonthView$MonthViewTouchHelper.onPopulateNodeForVirtualView + 628(SimpleMonthView.java:628)
at com.android.internal.widget.ExploreByTouchHelper.createNodeForChild + 397(ExploreByTouchHelper.java:397)
at com.android.internal.widget.ExploreByTouchHelper.createNode + 324(ExploreByTouchHelper.java:324)
at com.android.internal.widget.ExploreByTouchHelper.access0 + 49(ExploreByTouchHelper.java:49)
at com.android.internal.widget.ExploreByTouchHelper$ExploreByTouchNodeProvider.createAccessibilityNodeInfo + 741(ExploreByTouchHelper.java:741)
at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchAccessibilityNodeInfos + 805(AccessibilityInteractionController.java:805)
at android.view.AccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdUiThread + 155(AccessibilityInteractionController.java:155)
at android.view.AccessibilityInteractionController.access0 + 53(AccessibilityInteractionController.java:53)
at android.view.AccessibilityInteractionController$PrivateHandler.handleMessage + 1146(AccessibilityInteractionController.java:1146)
at android.os.Handler.dispatchMessage + 102(Handler.java:102)
at android.os.Looper.loop + 135(Looper.java:135)
at android.app.ActivityThread.main + 5910(ActivityThread.java:5910)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke + 372(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run + 1405(ZygoteInit.java:1405)
at com.android.internal.os.ZygoteInit.main + 1200(ZygoteInit.java:1200)

#0. Crashed: main
at java.util.Formatter.badArgumentType + 1489(Formatter.java:1489)
at java.util.Formatter.transformFromInteger + 1689(Formatter.java:1689)
at java.util.Formatter.transform + 1461(Formatter.java:1461)
at java.util.Formatter.doFormat + 1081(Formatter.java:1081)
at java.util.Formatter.format + 1042(Formatter.java:1042)
at java.util.Formatter.format + 1011(Formatter.java:1011)
at java.lang.String.format + 1803(String.java:1803)
at android.content.res.Resources.getString + 668(Resources.java:668)
at android.content.Context.getString + 390(Context.java:390)
at android.widget.SimpleMonthView$MonthViewTouchHelper.getItemDescription + 684(SimpleMonthView.java:684)
at android.widget.SimpleMonthView$MonthViewTouchHelper.onPopulateNodeForVirtualView + 628(SimpleMonthView.java:628)
at com.android.internal.widget.ExploreByTouchHelper.createNodeForChild + 397(ExploreByTouchHelper.java:397)
at com.android.internal.widget.ExploreByTouchHelper.createNode + 324(ExploreByTouchHelper.java:324)
at com.android.internal.widget.ExploreByTouchHelper.access0 + 49(ExploreByTouchHelper.java:49)
at com.android.internal.widget.ExploreByTouchHelper$ExploreByTouchNodeProvider.createAccessibilityNodeInfo + 741(ExploreByTouchHelper.java:741)
at android.view.AccessibilityInteractionController$AccessibilityNodePrefetcher.prefetchAccessibilityNodeInfos + 805(AccessibilityInteractionController.java:805)
at android.view.AccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdUiThread + 155(AccessibilityInteractionController.java:155)
at android.view.AccessibilityInteractionController.access0 + 53(AccessibilityInteractionController.java:53)
at android.view.AccessibilityInteractionController$PrivateHandler.handleMessage + 1146(AccessibilityInteractionController.java:1146)
at android.os.Handler.dispatchMessage + 102(Handler.java:102)
at android.os.Looper.loop + 135(Looper.java:135)
at android.app.ActivityThread.main + 5910(ActivityThread.java:5910)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke + 372(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run + 1405(ZygoteInit.java:1405)
at com.android.internal.os.ZygoteInit.main + 1200(ZygoteInit.java:1200)

Samsung 的 Lollipop 实现中存在错误。如果启用了 Talkback,这会发生在某些 Locale 上。您可以通过在具有 Holo 主题的三星设备上强制 DatePicker 来修复。

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (isBrokenSamsungDevice())
    setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);
}

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