如何解决自定义时间选择器中的膨胀异常?

How to resolve an inflate exception in a custom timepicker?

我在我的 android 应用程序中创建了一个自定义时间选择器对话框,并且 android studio 一直在 "inflater.inflate may produce null exception".

以下是自定义时间选择器的 xml 文件:

attrs.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <attr name="timePickerStyle" format="reference" />
    <declare-styleable name="CustomTimePicker">
       <attr name="internalLayout" format="reference"  />
    </declare-styleable>
 </resources>

time_picker_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<app.customview.CustomTimePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/timePicker"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

time_picker.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_gravity="center_horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layoutDirection="ltr">
        <!-- hour -->
        <NumberPicker
            android:id="@+id/hour"
            android:layout_width="70dip"
            android:layout_height="wrap_content"
            android:focusable="true"
            android:focusableInTouchMode="true"
            />

        <!-- minute -->
        <NumberPicker
            android:id="@+id/minute"
            android:layout_width="70dip"
            android:layout_height="wrap_content"
            android:layout_marginStart="5dip"
            android:focusable="true"
            android:focusableInTouchMode="true"
            />

        <!-- minute -->
        <NumberPicker
            android:id="@+id/amPm"
            android:layout_width="70dip"
            android:layout_height="wrap_content"
            android:layout_marginStart="5dip"
            android:focusable="true"
            android:focusableInTouchMode="true"
            />

</LinearLayout>

CustomTimePickerDialog.Java

package app.customview;


import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TimePicker;

import app.customview.CustomTimePicker.OnTimeChangedListener;

/**
 * A dialog that prompts the user for the time of day using a {@link TimePicker}.
 * <p/>
 * <p>See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
 * guide.</p>
 */
public class CustomTimePickerDialog extends AlertDialog
        implements OnClickListener, OnTimeChangedListener {

    private static final String HOUR = "hour";
    private static final String MINUTE = "minute";
    private static final String IS_24_HOUR = "is24hour";
    private final CustomTimePicker mTimePicker;
    private final OnTimeSetListener mCallback;
    int mInitialHourOfDay;
    int mInitialMinute;
    boolean mIs24HourView;
    private View view;
    /**
     * @param context      Parent.
     * @param callBack     How parent is notified.
     * @param hourOfDay    The initial hour.
     * @param minute       The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public CustomTimePickerDialog(Context context,
                                  OnTimeSetListener callBack,
                                  int hourOfDay, int minute, boolean is24HourView) {
        this(context, 0, callBack, hourOfDay, minute, is24HourView);
    }

    /**
     * @param context      Parent.
     * @param theme        the theme to apply to this dialog
     * @param callBack     How parent is notified.
     * @param hourOfDay    The initial hour.
     * @param minute       The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public CustomTimePickerDialog(Context context,
                                  int theme,
                                  OnTimeSetListener callBack,
                                  int hourOfDay, int minute, boolean is24HourView) {
        super(context, theme);
        mCallback = callBack;
        mInitialHourOfDay = hourOfDay;
        mInitialMinute = minute;
        mIs24HourView = is24HourView;

        setIcon(0);
        // buggy
        setTitle("Set Time");

        //Context themeContext = getContext();
        setButton(BUTTON_POSITIVE, "Done", this);

        LayoutInflater inflater = getLayoutInflater();

        try {
            view = inflater.inflate(R.layout.time_picker_dialog, null);
        } catch(android.view.InflateException e) {
            view = inflater.inflate(R.layout.time_picker_dialog, null);
            // do nothing
        }
        setView(view);
        mTimePicker = (CustomTimePicker) view.findViewById(R.id.timePicker);

        // initialize state
        mTimePicker.setIs24HourView(mIs24HourView);
        mTimePicker.setCurrentHour(mInitialHourOfDay);
        mTimePicker.setCurrentMinute(mInitialMinute);
        mTimePicker.setOnTimeChangedListener(this);

    }

    public void onClick(DialogInterface dialog, int which) {
        tryNotifyTimeSet();
    }

    public void updateTime(int hourOfDay, int minutOfHour) {
        mTimePicker.setCurrentHour(hourOfDay);
        mTimePicker.setCurrentMinute(minutOfHour);
    }

    private void tryNotifyTimeSet() {
        if (mCallback != null) {
            mTimePicker.clearFocus();
            mCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
                    mTimePicker.getCurrentMinute());
        }
    }

    @Override
    protected void onStop() {
        tryNotifyTimeSet();
        super.onStop();
    }

    @Override
    public Bundle onSaveInstanceState() {
        Bundle state = super.onSaveInstanceState();
        state.putInt(HOUR, mTimePicker.getCurrentHour());
        state.putInt(MINUTE, mTimePicker.getCurrentMinute());
        state.putBoolean(IS_24_HOUR, mTimePicker.is24HourView());
        return state;
    }

    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        int hour = savedInstanceState.getInt(HOUR);
        int minute = savedInstanceState.getInt(MINUTE);
        mTimePicker.setIs24HourView(savedInstanceState.getBoolean(IS_24_HOUR));
        mTimePicker.setCurrentHour(hour);
        mTimePicker.setCurrentMinute(minute);
    }

    @Override
    public void onTimeChanged(CustomTimePicker view, int hourOfDay, int minute) {
        // TODO Auto-generated method stub

    }


    /**
     * The callback interface used to indicate the user is done filling in
     * the time (they clicked on the 'Set' button).
     */
    public interface OnTimeSetListener {

        /**
         * @param view      The view associated with this listener.
         * @param hourOfDay The hour that was set.
         * @param minute    The minute that was set.
         */
        void onTimeSet(CustomTimePicker view, int hourOfDay, int minute);
    }

}

CustomTimePicker.java

public CustomTimePicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // initialization based on locale
        setCurrentLocale(Locale.getDefault());

        // process style attributes
         TypedArray attributesArray = context.obtainStyledAttributes(
                attrs, R.styleable.CustomTimePicker, defStyle, 0);
        int layoutResourceId = attributesArray.getResourceId(
           R.styleable.CustomTimePicker_internalLayout, R.layout.time_picker);
         attributesArray.recycle();

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(layoutResourceId, this, true);

        // hour
        mHourSpinner = (NumberPicker) findViewById(R.id.hour);
        mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
                if (!is24HourView()) {
                    if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY)
                            || (oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) {
                        mIsAm = !mIsAm;
                        updateAmPmControl();
                    }
                }
                onTimeChanged();
            }
        });


        // minute
        mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
        mMinuteSpinner.setMinValue(0);
        mMinuteSpinner.setMaxValue(3);
        mMinuteSpinner.setDisplayedValues(new String[]{"0", "15", "30", "45"});
        mMinuteSpinner.setFormatter(new TwoDigitFormatter());
        mMinuteSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
                onTimeChanged();
            }
        });

        /* Get the localized am/pm strings and use them in the spinner */
        mAmPmStrings = new DateFormatSymbols().getAmPmStrings();

        // am/pm
        View amPmView = findViewById(R.id.amPm);
        if (amPmView instanceof Button) {
            mAmPmSpinner = null;
            mAmPmButton = (Button) amPmView;
            mAmPmButton.setOnClickListener(new OnClickListener() {
                public void onClick(View button) {
                    button.requestFocus();
                    mIsAm = !mIsAm;
                    updateAmPmControl();
                    onTimeChanged();
                }
            });
        } else {
            mAmPmButton = null;
            mAmPmSpinner = (NumberPicker) amPmView;
            mAmPmSpinner.setMinValue(0);
            mAmPmSpinner.setMaxValue(1);
            mAmPmSpinner.setDisplayedValues(mAmPmStrings);
            mAmPmSpinner.setOnValueChangedListener(new OnValueChangeListener() {
                public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                    picker.requestFocus();
                    mIsAm = !mIsAm;
                    updateAmPmControl();
                    onTimeChanged();
                }
            });
        }

        // update controls to initial state
        updateHourControl();
        updateAmPmControl();

        setOnTimeChangedListener(NO_OP_CHANGE_LISTENER);

        // set to current time
        setCurrentHour(mTempCalendar.get(Calendar.HOUR_OF_DAY));
        setCurrentMinute(mTempCalendar.get(Calendar.MINUTE));

        if (!isEnabled()) {
            setEnabled(false);
        }

    }

===============================

正是 CustomTimePickerDialog.Java 中的这一行 android studio 一直在说 "may producc java.lang.null exception..." 如果我无论如何都尝试访问视图对象,应用程序也会崩溃,因为它返回 null .奇怪的是,偶尔这段代码运行良好,我可以看到时间选择器和应用程序工作正常,但 90% 的时间,视图膨胀代码 returns null 和应用程序崩溃。

有人可以指出我可以做些什么来解决这个问题(修复警告并最终确保视图 inflation 有效)。

谢谢

版主注意事项:请不要将此标记为重复,这不是一般的 "null exception" 错误问题。我已经搜索了好几天才问这个问题,因为我找不到任何可能解决我问题的答案。

您可能会收到该错误,因为您正在 LayoutInflator 使用系统服务,这些服务在 onCreate() 之前对 Activity 不可用。尝试通过以下方式获取布局充气器:

LayoutInflater inflater = getLayoutInflater();