5

我知道以前有人问过这个问题,但到目前为止给出的答案对我没有任何帮助。

我有一个 viewpager,其中填充了来自 FragmentStatePagerAdapter 的片段(android.support.v4.app.Fragment)。其中一些片段包含在方向更改时需要保留的逻辑,例如跟踪当前选择的视图。

但是,尽管我将相关数据保存在 onSaveInstanceState 中,但 savedInstanceState 始终为空。我可以通过将数据存储在一个静态变量中来解决这个问题(因为我每个片段只有一个实例对我有用)但我发现这是一个非常丑陋的解决方案,并且必须有一个正确的方法来做到这一点。

这是在旋转时不保留其状态的片段之一:

    public class PriceSelectFragment extends Fragment {

    private TableRow mSelected;
    private int mSelectedPos = 0;

    // newInstance constructor for creating fragment with arguments
    public static PriceSelectFragment newInstance() {
        PriceSelectFragment fragmentFirst = new PriceSelectFragment();
        return fragmentFirst;
    }

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_price_select, container, false);
        TableLayout mTable = (TableLayout)view.findViewById(R.id.price_table);

        List<PriceGroup> mPriceGroups = ((MainActivity) getActivity()).getPriceGroups();

        int i = 0;
        for (final PriceGroup group : mPriceGroups) {
            //Create row from layout and access child TextViews
            TableRow r = (TableRow)inflater.inflate( R.layout.price_group, mTable, false);
            TextView size = (TextView)r.getChildAt(0);
            TextView dimension = (TextView)r.getChildAt(1);
            TextView weight = (TextView)r.getChildAt(2);
            TextView price = (TextView)r.getChildAt(3);

            //Populate row with PriceGroup Data
            size.setText(group.sizeIndicator);
            dimension.setText(String.format("%2.0fx%2.0fx%2.0f", group.length, group.width, group.height));
            weight.setText(Float.toString(group.weight));
            price.setText(Integer.toString(group.price));

            //Alternate background color every other row
            if (i % 2 == 0) {
                r.setBackgroundDrawable(getResources().getDrawable(R.drawable.price_selector_1));
            }
            else {
                r.setBackgroundDrawable(getResources().getDrawable(R.drawable.price_selector_2));
            }
            mTable.addView(r); // Add to table

            r.setTag(i);
            r.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectRow((TableRow) v);
                }
            });
            i++;
        }

        mSelected = (TableRow)view.findViewWithTag(mSelectedPos);
        selectRow(mSelected);

        return view;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("selected", mSelectedPos);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (savedInstanceState != null) {
            mSelectedPos = savedInstanceState.getInt("selected");
        }
    }

    private void selectRow(TableRow row) {
        if ((int) mSelected.getTag() % 2 == 0) {
            mSelected.setBackgroundDrawable(getResources().getDrawable(R.drawable.price_selector_1));
        }
        else {
            mSelected.setBackgroundDrawable(getResources().getDrawable(R.drawable.price_selector_2));
        }
        mSelected = row;
        mSelectedPos = (int) mSelected.getTag();
        mSelected.setBackgroundColor(getResources().getColor(R.color.light_blue));
    }


}

如何解决这个问题而不必将我的状态保存在静态变量中?

编辑

我应该指出,所有片段都是以编程方式创建的,因此它们没有 id,我读到这可能是问题,但我也不知道如何解决。

我的应用程序也是这样的结构:

  • 带有 NavigationDrawer 的 MainActivity
    • 片段1
      • 浏览器
        • 子片段1 - 子片段5
    • 片段2
    • 片段3

我遇到问题的片段是子片段。

4

3 回答 3

4

在您Activity的托管中,Fragment您需要将对片段的引用存储在Bundle.

像这样的东西应该适合你

public void onCreate(Bundle savedInstanceState) {

    if (savedInstanceState != null) {
        //Restore your fragment instance
        fragment1 = getSupportFragmentManager().getFragment(
                    savedInstanceState, "fragment");
    }
}


protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    getSupportFragmentManager().putFragment(outState, "fragment", fragment1);
}

fragment1是您在问题中提到的需要重新创建的 Fragment1 的实例。

我以前没有用像你这样的结构做过这个,但这就是我要开始的方式:

onSaveInstanceState你的Fragment1我相信你需要对你的每个片段做同样的事情ViewPager。然后在onCreateViewFragment1从片段管理器中获取片段并重新创建你的ViewPager.

我在这里找到了这个答案,它几乎相同,但有更多细节:https ://stackoverflow.com/a/17135346/1417483

于 2015-04-29T13:50:30.810 回答
1

FragmentPagerAdapter 不会在不再可见的片段中调用 onSaveInstanceState。也许这就是导致您的问题的原因。尝试改用 FragmentStatePagerAdapter。

于 2017-05-23T12:23:52.053 回答
0

我终于得到了解决方案并解释了为什么会发生这种情况。我有一个非常相似的问题。我意识到当我向右滚动到我的第三个子片段然后回到第一个子片段时,第一个子片段的状态被保存了。但不是方向变化。

我认为只有在调用适配器的 destroyItem(..) 时才会保存状态。如果方向发生变化,则不会自动调用它。

因此,现在 MainFragment(包含 ViewPager)的 onSaveInstanceState 我为每个活动片段调用 destroyItem。我检查 activity.isChangingConfigurations() 因为如果我关闭屏幕也会调用 onSaveInstanceState ,但在这种情况下,所有片段都保持活动状态,无需更改任何内容。

我使用 onDestroy(boolean retain) 扩展了适配器,然后调用它:

//in the main-fragment which holds the ViewPager:
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if(getActivity()!=null && getActivity().isChangingConfigurations())
    {
        if (pager != null) {
        try {
            Log.w(TAG, TAG + " pager.onDestroy(true)");
            pager.onDestroy(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

MyFragmentStatePagerAdapter 中的实现:

public void onDestroy(boolean retain)
{
    this.m_allowDynamicLoading = false;
    if(retain)
    {
        try{
            if(getAdapter()!=null)
            {
                int limit = this.getOffscreenPageLimit();
                int currentIndex = this.getCurrentItem();
                if(currentIndex <0 || getAdapter().getCount() <= 0) 
                    return;
                //active fragments = fragments that are (less or equal) then
                //offscreenPageLimit awaw from the currently displayed one.
                for(int i = Math.min(currentIndex+limit, getAdapter().getCount()-1); 
                        i>= Math.max(0, currentIndex-limit);//erstes aktives fragment ist current - offscreen limit, aber nicht unter 0..
                        i--)    
                {
                    getAdapter().destroyItem(MessagingViewPager.this, i, getAdapter().instantiateItem(MessagingViewPager.this, i)); //this saved the state of that fragment, that will be restored after orientation change
                    Log.e(TAG,TAG + " orientation-change: destroying item " + i);                   
                }
            }
        }catch(Exception e){}   
    }
    else{ //retain = false is called onDestroy of the Fragment holding this Pager.
        try{
            this.setAdapter(null); 
            //this will destroy all fragments and forget the position
        }catch(Exception e){}   
    }       
}

还有一些话要说:

  1. 适配器采用 ChildFragmentManager 不是正常的
  2. SubFragment 不得使用 setRetainInstance(true) (否则异常) MainFragment 可以(在我的情况下确实)使用 setRetainInstance(true)
  3. 在 MainFragment 的 onCreate 中创建适配器,因此在方向更改时不会重新创建它。将适配器设置为寻呼机应在 onCreateView 中完成。
  4. MainFragment 的 OnDestroy(或 onDestroyView)使用 setAdapter(null) 来终止所有 Fragment 并释放资源。(在我的情况下,这是由 MyViewPager.onDestroy(false) 完成的)

等等:现在您在方向更改后在 SubFragments 中获得了您的 savedInstanceState 包。如果您只关闭屏幕,它不会破坏物品。

于 2016-04-21T06:51:36.183 回答