片段中的 ConfigurationChange 后 onClickListener 不工作(正确)

cre*_*not 5 java android android-configchanges android-fragments

介绍

呵呵,这是一个艰难的。至少,是这么认为的……澄清一下:

搜索互联网后(使用谷歌),我没有找到我的问题的任何答案。

我发现的一切都是关于人们onClickListener为他们View的错误设置的。我想在我的情况下这是同样的问题,但没有其他问题与我的匹配。

似乎是同样的问题......它没有答案。

设置

我有三只Fragment的成立连同ViewPager我的AppCompatActivity

viewPager = (ViewPager) findViewById(R.id.main_view_pager);
viewPager.setAdapter(new SectionsPagerAdapter(getSupportFragmentManager()));
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

在每一个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>
Run Code Online (Sandbox Code Playgroud)

然后我在's方法中OnClickListener为每个View's (dividers)设置了一个。在已经在准备我的,我也实例化了我,这样我可以改变当前(/片)。FragmentonCreateView(LayoutInflater inflater, ViewGroup Bundle savedInstanceState)OnClickListenerActivityViewPagerItemFragment

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);
    }
};
Run Code Online (Sandbox Code Playgroud)

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

这是针对其中一个片段的。其他的都是一样的!

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;
}
Run Code Online (Sandbox Code Playgroud)

我的putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick)方法是 a Voidin an Interface。它只适用OnClickListenerClass( Fragment)。

@Override
public void putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick) {
    this.tabIndicatorOnClick = tabIndicatorOnClick;
}
Run Code Online (Sandbox Code Playgroud)

有用吗

是的,它确实。它工作得很好......直到ConfigurationChange发生。就我而言,我通过更改方向对其进行了测试。

问题

之后发生的事情ConfigurationChange是一切正常。的OnClickListener被施加到所有 View的在所有 Fragment的,tabIndicatorOnClick OnClickListener是一个空对象引用在第二和第三Fragment

所以在PostWriterTopPage中,无论出于何种原因,都没有真正的OnClickListeneron View。但它变得更好:在MainTextHolder FragmenttabIndicatorOnClick不是null object reference,但它不会改变ViewPagerItem了。它运行代码,但不滚动Tab.

当开启“不保留活动” IND开发人员选项设备和退出应用,所有OnClickListener的都是null object references。甚至那些在MainTextHolder.

概括

之后,ConfigurationChangeOnClickListener被传递到我的第一次Fragment,但没有正常工作,并在剩余Fragment它甚至不是通过/年null object reference

可以通过销毁Activity然后重新加载复制该问题。

到底...

......我不知道出了什么问题。我希望我的问题结构正确。

可能有兴趣

最小SDK版本14

目标SDK版本25

我已经有一个SpringIndicatorViewPager。这没有功能,OnClickListener所以我添加了我自己的具有所需功能的“叠加”指标OnClick。我也喜欢它的外观,但如果您能给我一个更好的解决方案TabIndicator,我也很乐意将其作为答案。

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

其他OnClickListener的在Fragment仍然正常工作。所以正如我所说的,问题在于OnClickListener它本身无法正常工作/成为null object reference.

Dav*_*son 2

首先,写得很好,详细描述了问题。StackOverflow 上的很多提问者都可以借鉴这个问题阐明问题的能力。

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

要修复该错误,我建议您不要将View.OnClickedListener作为参数传递给 Fragments。相反,OnClickListener通过 Activity 中的公共方法将 暴露给 Fragment,并让 Fragments 在其onResume(). View.OnClickedListener这样您就可以保证片段在处于恢复状态时都会有一个引用。所以你onResume()可能看起来像这样:

@Override
public void onResume() {
    OnClickListener onClickListener = ((MyActivity) getActivity).getOnClickListener();
    putIndicatorTabIndicatorOnClick(onClickListener);
Run Code Online (Sandbox Code Playgroud)

}

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

@Override
public void onPause() {
    //set the onClickListener to null to free up the resource
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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