片段中的 ConfigurationChange 后 onClickListener 无法正常工作

onClickListener not working (properly) after ConfigurationChange in fragments

简介

嗯,这是一个艰难的。至少,是这么认为的…… 整理一下:

I did not find any answers to my question after searching the internet (using Google).

我发现的一切都是关于人们为他们的 View 设置错误的 onClickListener。我想这在我的案例中是同样的问题,但 none 其他问题与我的匹配。

This好像是同一个问题。。。没有答案。

设置

我在 AppCompatActivity.

中设置了三个 Fragment 和一个 ViewPager
viewPager = (ViewPager) findViewById(R.id.main_view_pager);
viewPager.setAdapter(new SectionsPagerAdapter(getSupportFragmentManager()));

SectionsPagerAdapter 处理。

public class SectionsPagerAdapter extends FragmentPagerAdapter {

    SectionsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return MainTextHolder.newInstance(tabIndicatorOnClick);
            case 1:
                return PostWriter.newInstance(tabIndicatorOnClick);
            case 2:
                return TopPage.newInstance(tabIndicatorOnClick);
            default:
                return Error.newInstance();
        }
    }

    @Override
    public int getCount() {
        return 3;
    }

    // ... more methods
}

在每个 Fragment 中,我都有一些内容加上一个自定义 TabIndicator

(下面的xml文件是我的View的指标)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <View
        android:id="@+id/fragment_divider_one"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:tag="one" />

    <View
        android:id="@+id/fragment_divider_two"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:tag="two" />

    <View
        android:id="@+id/fragment_divider_three"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:tag="three" />

</LinearLayout>

然后我在 FragmentonCreateView(LayoutInflater inflater, ViewGroup Bundle savedInstanceState) 方法中为每个 View 的(分隔符)设置一个 OnClickListenerOnClickListener 已经在我的 Activity 中准备好了,我也在其中实例化了我的 ViewPager 以便我可以更改当前的 Item (Fragment/tab)。

private final View.OnClickListener tabIndicatorOnClick = new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (view.getTag().toString().equals("one"))
            viewPager.setCurrentItem(0, true); // second argument for smooth transition
        else if (view.getTag().toString().equals("two"))
            viewPager.setCurrentItem(1, true);
        else if (view.getTag().toString().equals("three"))
            viewPager.setCurrentItem(2, true);
    }
};

我通过将 OnClickListener 放入我的 newInstance(View.OnClickListener tabIndicatorOnClick) 方法中将其传递给 Fragment

This is for one of the fragments. It is identical for the others!

public static MainTextHolder newInstance(View.OnClickListener tabIndicatorOnClick) {
    MainTextHolder fragment = new MainTextHolder();
    Bundle args = new Bundle();
    fragment.putIndicatorTabIndicatorOnClick(tabIndicatorOnClick);
    fragment.setArguments(args);
    fragment.setRetainInstance(true);
    return fragment;
}

我的putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick)方法是Interface中的Void。它只是将 OnClickListener 应用于 Class(Fragment).

@Override
public void putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick) {
    this.tabIndicatorOnClick = tabIndicatorOnClick;
}

有用吗

是的。它工作得很好......直到 ConfigurationChange 发生。就我而言,我通过 改变方向 .

对其进行了测试

问题

之后ConfigurationChange一切正常。 OnClickListener 应用于 all View 中的 all Fragment但是 tabIndicatorOnClick OnClickListener 在第二个和第三个Fragment 中是一个空对象引用

所以在 PostWriterTopPageView 甚至没有真正的 OnClickListener'出于什么原因。但它变得更好了:在 MainTextHolder FragmenttabIndicatorOnClick 不是 null object reference,但它不再改变 ViewPagerItem .它运行代码,但不滚动 Tab.

当打开设备的"Don't keep activities"开发者选项并离开应用程序时,所有OnClickListener的是 null object references。甚至那些 MainTextHolder.

总结

ConfigurationChange 之后,OnClickListener 被传递到我的第一个 Fragment,但它无法正常工作并且在 剩余的 Fragment' s 甚至没有通过 / a null object reference.

问题 可以通过销毁 Activity 然后重新加载来 重现。

最后...

...我不知道出了什么问题。我希望我正确地组织了我的问题。

可能感兴趣

minSdkVersion: 14

targetSdkVersion: 25

我的 ViewPager 上已经有一个 SpringIndicator。这没有 OnClickListener,所以我添加了我自己的 "overlayed" 指标,它具有所需的 OnClick 功能。我也喜欢它的外观,但如果你能为我的 TabIndicator 提供更好的解决方案,我也很乐意将其作为答案。

我遇到了多次将方法放入 xml :onClick 属性的解决方案,但为此我需要为每个 [=18] 创建相同的 OnClickListener =] 这不是很好的方法,也不是 解决 我的问题,而是解决它的方法。 - 我需要将 ViewPager 传递到我的 Fragment,然后从 xml 调用该方法,正如我所说,这只是另一种方式,而不是我喜欢的方式做吧。另外不知道有没有效果。我很可能会测试它。

Fragment 中的其他 OnClickListener 仍然可以正常工作。所以正如我所说,问题在于 OnClickListener 本身不起作用 right/being a null object reference.

首先,对问题的详细描述写得很好。 Whosebug 上的很多提问者都可以借鉴这个问题的清晰表达问题的能力。

这个错误似乎是由于没有考虑 Fragment 生命周期的变化。在 ViewPager 内部,Fragment 会经历各种状态,包括暂时离开屏幕时隐藏和显示,如果离开屏幕并且系统释放内存则暂停和恢复,如果 Android 系统决定关闭则最终创建和销毁保存 Activity 的实例状态(例如来自配置更改)。在后一种情况下,Android 将尝试从 ViewPager 中保存的实例状态恢复片段的状态。由于您的 Fragments 无法保存在参数中传递的 View.OnClickedListener,因此它们在恢复时最终会得到一个 null 指针,这会导致您的错误。

为了修复错误,我建议您不要将 View.OnClickedListener 作为参数传递给片段。相反,通过 Activity 中的 public 方法将 OnClickListener 公开给您的片段,并让片段自己在 onResume() 中获取它。通过这种方式,您可以保证片段在恢复状态时将引用 View.OnClickedListener 。所以你的 onResume() 可能看起来像这样:

@Override
public void onResume() {
    OnClickListener onClickListener = ((MyActivity) getActivity).getOnClickListener();
    putIndicatorTabIndicatorOnClick(onClickListener);

}

既然你在onResume()中设置了它,你应该在onPause()中删除它:

@Override
public void onPause() {
    //set the onClickListener to null to free up the resource
}

像这样使用onResume()onPause()来释放监听器的资源是开发人员指南中推荐的方法。

当然,您必须通过检查 onCreate(Bundle savedInstanceState) 中保存的状态并适当地恢复它来确保您的 Activity 也处理自己的生命周期。

请注意,可以通过多种方式触发保存实例状态和恢复:

  1. 配置状态更改(例如旋转 phone 并导致 Activity 横向显示而不是纵向显示
  2. 设备内存不足(例如,如果最近的应用程序中有大量活动)
  3. 打开开发人员选项/不保留活动并使用主页将您的应用程序放入最近的应用程序,然后导航回您的应用程序。

您将能够使用其中一种方法来确定您是否正确处理了 Activity 和 Fragment 的生命周期。