在ViewPager中重新加载片段时出现问题

时间:2018-09-21 09:16:46

标签: android android-fragments android-viewpager fragmentstatepageradapter

我在ViewPager中的片段有问题。在我的应用程序中,我有4个使用FragmentStatePagerAdapter实现的选项卡。 这是我的SectionsPageAdapter.java

public class SectionsPageAdapter extends FragmentStatePagerAdapter {

private String fragment0 = "";
private Fragment fragAt0;
private FragmentManager manager;
private String[] tabNames;
Context context;


public SectionsPageAdapter(FragmentManager fm, Context ctx, String lastFrag) {
    super(fm);
    context = ctx;
    tabNames = ctx.getResources().getStringArray(R.array.tabs_name);
    this.manager = fm;
    this.listener = new fragmentChangeListener();
    fragment0 = lastFrag;
}


@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return ActivityTypeFragment.newInstance(0);
        case 1:
            return EventsFragment.newInstance();
        case 2:
            return CalendarFragment.newInstance();
        case 3:
            if(fragAt0 == null){

                switch(fragment0){
                    case "Chart":
                        fragAt0 = ChartFragment.newInstance(listener);
                        break;
                    case "Hour":
                        fragAt0 = HourChartFragment.newInstance(listener);
                        break;
                    case "Daily":
                        fragAt0 = DailyChartFragment.newInstance(listener);
                        break;
                    default:
                        fragAt0 = ChartFragment.newInstance(listener);
                }
            }
            return fragAt0;
        default:
            return ActivityTypeFragment.newInstance(0);
    }
}

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

@Nullable
@Override
public CharSequence getPageTitle(int position) {
    return tabNames[position];
}

@Override
public int getItemPosition(@NonNull Object object) {
    if (object instanceof ChartFragment && fragAt0 instanceof DailyChartFragment
        || object instanceof HourChartFragment && fragAt0 instanceof DailyChartFragment)
    {
        return POSITION_NONE;
    }
    else if (object instanceof DailyChartFragment && fragAt0 instanceof ChartFragment
            || object instanceof HourChartFragment && fragAt0 instanceof ChartFragment){
        return POSITION_NONE;
    }
    else if (object instanceof HourChartFragment && fragAt0 instanceof ChartFragment)
    {
        return POSITION_NONE;
    }
    else if (object instanceof ChartFragment && fragAt0 instanceof HourChartFragment
            || object instanceof DailyChartFragment && fragAt0 instanceof HourChartFragment)
    {
        return POSITION_NONE;
    }

    return POSITION_NONE;
}

public interface nextFragmentListener {
    void fragment0Changed(String newFragmentIdentification, FragmentManager fragmentManager);
}
private nextFragmentListener listener;

private final class fragmentChangeListener implements nextFragmentListener {


    @Override
    public void fragment0Changed(String fragment, FragmentManager fm) {
        fragment0 = fragment;

        manager.beginTransaction().remove(fragAt0).commitNow();
        switch (fragment) {
            case "Chart":
                fragAt0 = ChartFragment.newInstance(listener);

                break;
            case "Daily":
                fragAt0 = DailyChartFragment.newInstance(listener);
                break;
            case "Hour":
                fragAt0 = HourChartFragment.newInstance(listener);
                break;
        }
        SectionsPageAdapter.this.notifyDataSetChanged();
    }
}

public String getFragmentName()
{
    return fragment0;
}

}

因此,在最后一个选项卡中,我有一个片段-ChartFragment,其中有Spinner,带有用于选择另一个图表的选项。当用户选择另一个图表时,通过调用我的SectionsPageAdapter中的fragment0Changed方法,可以在此选项卡中重新加载另一个图表 类。我在https://stackoverflow.com/a/27304530/1573414中找到了这个解决方案。

这是我要在“图表”标签中显示的每个片段中都有的onItemSelected函数

@Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (view != null) {
            String selectedMenu = ((TextView) view).getText().toString();
            if (selectedMenu == getString(R.string.best_appealing_chart)) {
                LoadBestAppealingChart();
            } else if (selectedMenu == getString(R.string.hour_chart)) {
                chartListener.fragment0Changed("Hour", getActivity().getSupportFragmentManager());
            }
            else
            {
                chartListener.fragment0Changed("Daily", getActivity().getSupportFragmentManager());
            }
        }
        else {
            LoadBestAppealingChart();
        }
    }

因此,一切正常,直到我旋转设备为止,之后选择其他图表将不起作用。应用抛出异常:

java.lang.IllegalStateException: Fragment host has been destroyed 
at android.support.v4.app.FragmentManagerImpl.ensureExecReady(FragmentManager.java:2183)
        at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2211)
        at android.support.v4.app.BackStackRecord.commitNow(BackStackRecord.java:643).

就我而言,我已经读过文章https://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html,but,也没有使用异步方法替换片段,也没有在Activity生命周期方法中调用它。我在微调器的onSelectionChanged方法中进行片段事务。我试过的方法:使用commitNowAllowingStateLoss()并不能使事情变得更好,它只是没有引发异常,片段仍然无法重新加载;将getSupportFragmentManager()参数传递给fragment0Changed方法也没有帮助,但是即使我使用简单的commitNow()方法,这样做也不会引发任何异常。但是有帮助的是,旋转后我选择应用程序的第一个或第二个选项卡,然后转到第四个选项卡(“图表”选项卡),然后我可以切换(重新加载)片段而没有任何问题。有趣的是,切换到第三个选项卡(“日历”选项卡)也无济于事。切换到第三选项卡并返回到第四选项卡后,仍然不会重新加载片段。

2 个答案:

答案 0 :(得分:0)

我认为,与Fragment传递侦听器进行通信是Android上的常见错误。 Fragment上的侦听器必须由the Activity or a child Fragment实现。因此,您必须使Activity或Fragment实现NextFragmentListener ...其次,永远不要在ViewPager上引用Fragment,这是在泄漏旧的fragment0

答案 1 :(得分:0)

公共类SubAdapter扩展FragmentStatePagerAdapter {

/ *只是在super()调用中传递行为,因此每次调用resume方法时,您都可以重新加载所需的任何东西 但是您应该使用扩展* /

的子类

私有字符串tile_list [];

public SubAdapter (FragmentManager fm, String[] tile_list)
{

    super(fm,FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    this.tile_list = tile_list;
}

@Override
public Fragment getItem(int position)
{
    Fragment fragment;
    switch (position)
    {
        case 0:
            fragment=new new_fragment();
            break;

          default:
              fragment=new new_fragment();

    }
    return fragment;
}

@Override
public int getCount()
{
    return tile_list.length;
}

@Nullable
@Override
public CharSequence getPageTitle(int position)
{
    return tile_list[position];
}

}