11

我有一个问题,在哪里ListFragment.onListItemClick调用 after onDestroyView。我在现场收到了很多错误报告(大约 1000 个活跃用户每天 10-20 个),但我发现重现它的唯一方法是在单击整个屏幕时敲击后退按钮。成百上千的用户真的在这样做吗?这是跟踪:

java.lang.IllegalStateException: Content view not yet created
at au.com.example.activity.ListFragment.ensureList(ListFragment.java:860)
at au.com.example.activity.ListFragment.getListView(ListFragment.java:695)
at au.com.example.activity.MyFragment.onListItemClick(MyFragment.java:1290)
at au.com.example.activity.ListFragment$2.onItemClick(ListFragment.java:90)
at android.widget.AdapterView.performItemClick(AdapterView.java:301)
at android.widget.AbsListView.performItemClick(AbsListView.java:1519)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3278)
at android.widget.AbsListView$1.run(AbsListView.java:4327)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5293)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
at dalvik.system.NativeStart.main(Native Method)

由调用getListView().getItemAtPosition( MyFragment.onListItemClickMyFragment:1290) 引起。如何getView在单击处理程序回调期间返回 null?我还确定在此阶段片段已分离,isAdded() 为假,getActivity 为空。

一种解决方法是getListViewlistView从回调传入的 替换public void onListItemClick(ListView listView, View v, int position, long id),但其他函数仍需要更新 UI 的其他部分,所以这只会将问题转移到其他地方。相反,我取消了回调onDestroyView

public void onDestroyView() {           
        mHandler.removeCallbacks(mRequestFocus);
        if(mList!=null){
             mList.setOnItemClickListener(null);
        }
        mList = null;
        mListShown = false;
        mEmptyView = mProgressContainer = mListContainer = null;
        mStandardEmptyView = null;
        super.onDestroyView();
    }

但是我在其他(非列表)片段中仍然存在这个 onClick 问题。当片段被删除时(例如在 中),框架究竟是如何正常抑制这些回调的onBackPressed -> popBackStackImmediate()?在onDestroyView中,我取消了在 中创建的额外视图onCreateView。我是否需要手动清除我这样设置的每个侦听器?

这与未回答的问题类似:Fragment's getView() 在 OnClickListener 回调中返回 null

setOnRetainInstance(true)顺便说一句,我在我的片段中使用。

4

3 回答 3

1

您确实没有提供太多信息,但根据您提供的信息,听起来 Fragment pending Transactions可能是您的问题。

在 Android 中,每当您更改或实例化 Fragment 时,这一切都是通过 Pending Transactions 完成的,除非另有说明。这本质上是一种竞争条件。

getSupportFragmentManager().beginTransaction()
    .replace(R.id.container, new ExampleFragment()
    .commit();

UI 线程有一个工作队列,它需要在任何给定时间执行。即使您在运行上述代码后提交了 FragmentTransaction,它实际上已在队列末尾的 UI 线程上排队,在当前待处理的所有内容完成后发生。这意味着如果在事务未决时发生点击事件(这很容易发生,即您在屏幕上进行垃圾点击,或用多个手指点击),这些点击事件将在 FragmentTransaction之后放置在 UI 线程队列中。

最终结果是处理了 Fragment Transaction,您的 Fragment View 被销毁,然后您调用getView()它并返回 null。

你可以尝试几件事:

  1. getSupportFragmentManager().executePendingTransactions()这将立即执行所有待处理的事务,并删除待处理的方面

  2. 在执行片段视图被销毁后可能运行的代码之前,检查片段isVisible()或或其他片段“是”方法是否允许您获取有关片段在其生命周期中的当前状态的运行时信息(即isAdded()点击监听器)

  3. 因此,假设您有一个单击处理程序,当用户单击某些内容时,您会为另一个片段设置动画。您可以使用类似下面的代码,FragmentTransaction在最外层视图之前运行(在片段中,它将是从返回的内容getView()),如果视图将被销毁,这将永久禁用对视图的点击,或者如果您要重新使用视图,则在一段时间内暂时禁用点击。

希望这可以帮助。


public class ClickUtil {

  /**
   * Disables any clicks inside the given given view.
   *
   * @param view The view to iterate over and disable all clicks.
   */
  public static void disable(View view) {
    disable(view, null);
  }

  /**
   * Disables any clicks inside the given given view for a certain amount of time.
   *
   * @param view The view to iterate over and disable all clicks.
   * @param millis The number of millis to disable clicks for.
   */
  public static void disable(View view, Long millis) {
    final List<View> clickableViews = (millis == null) ? null : new ArrayList<View>();
    disableRecursive(view, clickableViews);

    if (millis != null) {
      MainThread.handler().postDelayed(new Runnable() {
        @Override public void run() {

          for (View v : clickableViews) {
            v.setClickable(true);
          }

        }
      }, millis);
    }
  }

  private static void disableRecursive(View view, List<View> clickableViews) {
    if (view.isClickable()) {
      view.setClickable(false);

      if (clickableViews != null)
        clickableViews.add(view);
    }

    if (view instanceof ViewGroup) {
      ViewGroup vg = (ViewGroup) view;
      for (int i = 0; i < vg.getChildCount(); i++) {
        disableRecursive(vg.getChildAt(i), clickableViews);
      }
    }
  }

}
于 2014-12-15T20:55:15.877 回答
-1

您可以在许多情况下使用 mHandler.removeCallbacksAndMessages(null) 为我工作。

于 2015-01-31T17:25:17.197 回答
-1

我敢打赌,这是由于存在于您的应用程序内部某处的额外无状态片段。我个人不鼓励保留实例并让 Android 尽其所能,而您使用标准机制来保留您的状态(saveInstanceState、数据库、高级类/模式、SharedPreferences 等)。

就个人而言,我在保留片段实例时遇到了很多问题(通常是通过配置更改或离开和重新进入应用程序重新创建或重新激活片段时),通常导致两个片段,其中一个连接到视图,无国籍,因此无用;而“真实的”保持之前的状态与视图没有任何联系,因此最终会出现异常和你不想拥有的各种幻想。

从不保留实例开始,看看会发生什么。

于 2015-01-12T14:40:26.017 回答