如何解决自定义时间选择器中的膨胀异常?
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();
我在我的 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();