【问题标题】:onCreate() called twice on configuration change in Fragment with ViewPageronCreate() 在 Fragment 中使用 ViewPager 进行配置更改时调用了两次
【发布时间】:2017-06-24 14:23:40
【问题描述】:

一般背景:
我有一个带有 MainFragment 的 MainActivity。 MainFragment 有一个 ViewPager,我使用 FragmentStatePagerAdapter 根据 SqliteDatabase 的内容显示 DetailFragments。此数据库的内容在 LocationActivity 中是可见和可管理的。当我单击 LocationActivity 中的某个项目时,我使用 intent.putextra 附加该项目的位置,然后在 MainFragment 的 ViewPager 中显示正确的位置。当我在 LocationActivity 中添加一个项目时,它会将项目的位置保存在 SharedPreferences 中,并且我在 MainFragment 中获得正确的位置以显示我刚刚添加的项目的片段。

问题:
它可以工作,但是当发生配置更改时,我会丢失 ViewPager 选择的实际页面。所以我用 onSaveInstanceState() 保存位置,如果它不为空,我在 onCreateView() 和 onViewCreated() 中检索它,我通过 if 语句根据条件获取 ViewPager.setCurrentItem() 所需的位置。但是随后再次调用 onCreate() ,然后 onSaveInstanceState() 为空,并且我丢失了在配置更改之前选择的页面。你知道为什么它被调用两次吗?我能做些什么来防止这种情况发生?感谢您的帮助

日志

06-24 09:15:05.974 activityMain.MainFragment: onLoadFinished() onPageSelected() 2
06-24 09:15:08.276 activityMain.MainFragment: onSaveInstanceState() 2
06-24 09:15:08.320 activityMain.MainFragment: onCreate()
06-24 09:15:08.342 activityMain.MainFragment: onCreateView()
06-24 09:15:08.342 activityMain.MainFragment: onCreateView() savedInstanceState Position: 2
06-24 09:15:08.349 activityMain.MainFragment: onViewCreated() get position from mCurrentPositionState 2
06-24 09:15:08.349 activityMain.MainFragment: onActivityCreated()
06-24 09:15:08.394 activityMain.MainFragment: onCreate()
06-24 09:15:08.396 activityMain.MainFragment: onCreateView()
06-24 09:15:08.401 activityMain.MainFragment: onViewCreated()) get position from SharedPreferences 4
06-24 09:15:08.401 activityMain.MainFragment: onActivityCreated()
06-24 09:15:08.407 activityMain.MainFragment: onResume()
06-24 09:15:08.458 activityMain.MainFragment: restartCursorLoader
06-24 09:15:08.508 activityMain.MainFragment: onLoadFinished()
06-24 09:15:08.537 activityMain.MainFragment: onLoadFinished() setCurrentItem: 4

一个最小的、完整的、可验证的例子

MainFragment.java

public class MainFragment extends Fragment {

    public MainFragment() {
        // Required empty public constructor
    }

    private static final String TAG = MainFragment.class.getName();
    private static final String PAGE_SELECTED = "state_instance";

    private MainPagerAdapter mAdapter;
    private ViewPager mViewPager;

    private int mLocationPosition;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

        if (savedInstanceState != null) {
            mLocationPosition = savedInstanceState.getInt(PAGE_SELECTED);
            Log.i(TAG, "onCreateView() savedInstanceState not null, position: " + mLocationPosition);
        } else {
            mLocationPosition = 0;
            Log.i(TAG, "onCreateView() savedInstanceState null, position: " + mLocationPosition);
        }

        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
        mAdapter = new MainPagerAdapter(getActivity().getSupportFragmentManager());
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mViewPager.setAdapter(mAdapter);
        mViewPager.setCurrentItem(mLocationPosition, false);
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                mLocationPosition = position;
                Log.i(TAG, "onActivityCreated() mLocationPosition value: " + mLocationPosition);
        }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(PAGE_SELECTED, mLocationPosition);
        Log.i(TAG, "onSaveInstanceState() mLocationPosition value: " + mLocationPosition);
        super.onSaveInstanceState(outState);
    }
}

主活动

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.fragment_container, new MainFragment());
        fragmentTransaction.commit();
    }
}

MainDetailFragment

public class MainDetailFragment extends Fragment {

    private static final String TAG = MainDetailFragment.class.getName();
    private TextView mTextViewLocation;

    protected static final String ARGUMENT_PAGE = "location_page";
    protected static final String ARGUMENT_NAME = "location_name";

    private String mLocation;

    public MainDetailFragment() {
    }

    protected static MainDetailFragment newInstance(int page, String locationName) {
        MainDetailFragment mainDetailFragment = new MainDetailFragment();
        Bundle arguments = new Bundle();
        arguments.putInt(MainDetailFragment.ARGUMENT_PAGE, page);
        arguments.putString(MainDetailFragment.ARGUMENT_NAME, locationName);
        mainDetailFragment.setArguments(arguments);
        return mainDetailFragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments().containsKey(ARGUMENT_NAME)) {
            mLocation = getArguments().getString(ARGUMENT_NAME);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_main_detail, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTextViewLocation = (TextView) view.findViewById(R.id.tv_city_name);
        mTextViewLocation.setText(mLocation);
    }
}

MainPagerAdapter

public class MainPagerAdapter extends FragmentStatePagerAdapter {

    private static final String TAG = MainPagerAdapter.class.getName();

    private static int NUM_ITEMS = 3;

    public MainPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    // Returns total number of pages
    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    // Returns the fragment to display for that page
    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0: 
                return MainDetailFragment.newInstance(0, "Paris");
            case 1: 
                return MainDetailFragment.newInstance(1, "London");
            case 2: 
                return MainDetailFragment.newInstance(2, "New York");
            default:
                return null;
        }
    }
}

【问题讨论】:

  • 你能用更少的代码创建一个更简单的例子来说明你的问题吗?有关如何执行此操作的提示,请参阅 minimal reproducible example
  • 感谢您的回复,我刚刚编辑了我的问题,并尝试用更少的代码和更多的信息使其更具可读性。
  • 请注意,每一行代码除了用于实际缩进的空格外,还必须以 4 个额外空格开头。我已经尽我所能修复了这个问题,还添加了一些缺失的右括号。请仔细检查以确保您在此处发布的代码在有人复制和粘贴时能够真正编译。
  • 为了帮助创建一个真正最小但仍然完整的代码示例,我经常发现从头开始一个新项目并逐渐添加与我的问题相关的部分很有帮助。如果你这样做,我认为你仍然可以减少你在这里给出的代码。例如,工具栏的几行是无关紧要的。
  • @Code-Apprentice 哦,我明白了,我将创建一个新项目,因为我认为这段代码不会编译。感谢您的时间和建议

标签: android android-fragments android-viewpager android-lifecycle android-configchanges


【解决方案1】:

您需要确保不会在配置更改后再次添加 MainFragment。您应该将 Activity 更新为如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    if (savedInstanceState != null) {
        return;
    }

    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.fragment_container, new MainFragment());
    fragmentTransaction.commit();
}

原因是Activity本身已经有MainFragment被添加的记录,配置更改后会自动恢复。如果您再次执行相同的事务,首先您会看到恢复的MainFragment 启动,然后它将被新Fragment 事务中的新事务和一个新的、不同的MainFragment 将经历自己的初始化过程。这导致似乎对onCreate 多次调用MainFragment

【讨论】:

  • 或者,您可以反转if 语句的条件:if (savedInstanceState == null) { /* Perform fragment transaction */ }
  • @Brian Yencho,非常感谢这个解决方案和清晰的解释它解决了我的问题。
  • @Code-Apprentice,我可能更喜欢这种方法。如果有人需要,我仍会尽快编辑我的问题
【解决方案2】:

在 MainFragment 的 onCreate() 中调用它

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i(TAG, "onCreate()");
    setRetainInstance(true);
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多