片段的onClickListener在onDestroyView之后调用

roc*_*cko 11 android android-lifecycle android-fragments

我有一个问题在哪里ListFragment.onListItemClick被称为onDestroyView.我在现场收到大量的错误报告(每天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)
Run Code Online (Sandbox Code Playgroud)

从调用造成getListView().getItemAtPositionMyFragment.onListItemClick(1290 MyFragment).如何getView在单击处理程序回调期间返回null?我还确定片段在此阶段已分离,isAdded()为false,并且getActivity为null.

一种解决方法是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();
    }
Run Code Online (Sandbox Code Playgroud)

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

这是一个与未答复的q类似的问题:片段的getView()在OnClickListener回调中返回null

我正在使用setOnRetainInstance(true)我的碎片,顺便说一句.

spi*_*ce7 1

您确实没有提供太多信息,但根据您提供的信息,听起来片段待处理事务可能是您的问题。

在 Android 中,每当您更改或实例化片段时,除非另有指示,否则都是通过待处理事务完成的。这本质上是一种竞争条件。

getSupportFragmentManager().beginTransaction()
    .replace(R.id.container, new ExampleFragment()
    .commit();
Run Code Online (Sandbox Code Playgroud)

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

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

你可以尝试一些事情:

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

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

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

}
Run Code Online (Sandbox Code Playgroud)