【问题标题】:Can't find spinner after rotating the screen旋转屏幕后找不到微调器
【发布时间】:2018-01-24 14:54:59
【问题描述】:

我正在使用“片段”来获取包含两个“片段”的布局,因此我的类扩展了“片段”。

我在左侧“片段”上加载了一个“ListView”,在右侧“片段”上我有另一个修改过的“ListView”。在这里,我有一个“微调器”,我必须更改它的颜色。

我有这个代码:

private void loadSpinner(int value) {
    //Not relevant code

    adapter = new ArrayAdapter<CharSequence>(getActivity().getApplicationContext(), android.R.layout.simple_spinner_item, data);

    adapter.setDropDownViewResource(R.layout.spinner);

    spinner.setAdapter(adapter);
}

@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    String value = parent.getItemAtPosition(position).toString();

   ((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.black));

  //Some code
}

上面的代码按预期工作直到我旋转我的设备的屏幕。在这里,我在((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.black)); 收到nullpointerexception

如果我评论上面的行,因为我在旋转屏幕之前保存状态,所以在我旋转屏幕后,我的数据会恢复并且一切都按预期工作除了打开的微调器浅色,因此不可见。

我知道我可以创建自己的“微调器”布局并解决此问题,但我想知道如何解决此问题。

【问题讨论】:

  • 你在哪里打电话给loadSpinner
  • @Szymon 嗨。在onCreateView,我正在这样做spinner = (Spinner) itemDataLayout.findViewById(R.id.availableValues); spinner.setOnItemSelectedListener(this);,并且微调器是从functionX 加载的。当我单击左侧listview 的一个项目时,函数functionX 被调用。如果我来自savedrestoredstate,则从onStart() 调用functionX
  • 您可能在调用loadSpinner 之前触发了onItemSelected,这会设置适配器。您可以通过在onItemSelected 和在线spinner.setAdapter() 中放置断点来验证顺序,并查看轮换后首先调用的内容
  • @Favolas 如果我在之前的评论中写的是这种情况,您将始终在onItemSelected 中有 NPE,因为 spinner 还没有子项显示。因此,您将有两种选择来解决此问题,但首先您需要检查是否是这种情况。
  • @kiruwka Nop。 loadSpinner 先来

标签: android android-layout listview android-fragments


【解决方案1】:

屏幕旋转后,您的Spinner 视图尚未附加任何子视图(您可以通过调用parent.getChildCount() 进行验证),因此您的parent.getChildAt(0) 返回null,从而导致NPE

说明

在 Activity 创建之后,Android 通过调用 onMeasure() 后跟 onLayout() 来显示布局层次结构中每个视图的内容。

Spinner 将创建其子视图,填充来自适配器的数据,并在调用 onLayout() 期间附加,就像 see here 一样。

因此,通常对于您的 Spinner,您将有序列:onMeasure -> onLayout -> onItemSelected
并且由于 onLayoutonItemSelected 之前被调用 - 在初始活动启动期间一切正常。

现在,让我们看看旋转屏幕时会发生什么:

在销毁 Activity 之前,它的 onSavedInstanceState 被调用,这会将调用传播到 SpinneronSavedInstanceState,这将保存当前选定的位置。

轮换后,您的活动被重新创建,然后其onRestoreInstanceState 被调用。

当Activity#onRestoreInstanceState被调用时,最终会调用到Spinner的onRestoreInstanceState,代码如下:

    SavedState ss = (SavedState) state;
    //...some code
    if (ss.selectedId >= 0) {
        mDataChanged = true;
        //...some more code
    }

如您所见,如果微调器的先前状态使用onSavedInstanceState 保存,它将始终设置 mDataChanged 标志。

然后,onMeasure() 被调用,代码如下:

    if (mDataChanged) {
        handleDataChanged();
    }

handleDataChanged()的实现最终会调用fireOnSelected(),触发Spinner的onItemSelected

因此onItemSelectedonMeasure 期间被调用,在onLayout 之前。
如上所述,Spinner 在其 onLayout 被调用之前没有子级。

希望问题现在已经清楚了。

解决方法

要确认该行为并解决 NPE,您只需在活动中为 onSavedInstanceState/onRestoreInstanceState 提供空实现(无需调用超级实现):

@Override 
protected void onSaveInstanceState(Bundle outState) { /* do nothing */ }

这会阻止设置mDataChangedonMeasure 不会触发fireOnSelected

补充说明

绝对不建议在覆盖 onSavedInstanceState/onRestoreInstanceState 时不调用 Activity super. 实现,至少您将无法自动保存内部视图(包括微调器)的状态。
我鼓励你这样做只是为了验证上面解释的行为并看到NPE 消失了。

最好的解决方案是在 .xml 布局中为您的微调器项定义具有所需颜色的TextView,并使用它为微调器构造ArrayAdapter

【讨论】:

  • 非常感谢您的解释。你说的对。将实现我的自定义微调器。
  • @Favolas 很高兴它有帮助。请考虑接受。新年快乐!
  • 嘿..我遇到了同样的问题..我实现了我的自定义微调器..仍然得到相同的nullpointerexception.您使用自定义微调器或其他东西解决了这个问题吗?
  • 我解决了using this solution
  • @Favolas,您的解决方案是否在 XML 中定义了颜色?我有一个类似的问题,但我不能依赖在 XML 中定义颜色,因为颜色仅在运行时才知道。我的问题在这里:stackoverflow.com/questions/33747884/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-13
  • 1970-01-01
  • 2016-04-07
  • 2021-10-02
相关资源
最近更新 更多