片段似乎非常适合将UI逻辑分离到一些模块中。但是ViewPager的生命周期对我来说仍然是模糊的。所以我们非常需要大师的思想!

Edit

参见下面的哑巴解决方案;-)

范围

主活动有一个带有片段的ViewPager。这些片段可以为其他(子主)活动实现稍微不同的逻辑,因此片段的数据是通过活动内部的回调接口填充的。第一次发射时一切正常,但是……

问题

When the activity gets recreated (e.g. on orientation change) so do the ViewPager's fragments. The code (you'll find below) says that every time the activity is created I try to create a new ViewPager fragments adapter the same as fragments (maybe this is the problem) but FragmentManager already has all these fragments stored somewhere (where?) and starts the recreation mechanism for those. So the recreation mechanism calls the "old" fragment's onAttach, onCreateView, etc. with my callback interface call for initiating data via the Activity's implemented method. But this method points to the newly created fragment which is created via the Activity's onCreate method.

问题

也许我使用了错误的模式,但即使是Android 3 Pro书也没有太多关于它的内容。所以,请给我一记重拳告诉我怎么做才是正确的。很多谢谢!

Code

主要活动

public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {

private MessagesFragment mMessagesFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    setContentView(R.layout.viewpager_container);
    new DefaultToolbar(this);

    // create fragments to use
    mMessagesFragment = new MessagesFragment();
    mStreamsFragment = new StreamsFragment();

    // set titles and fragments for view pager
    Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
    screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
    screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);

    // instantiate view pager via adapter
    mPager = (ViewPager) findViewById(R.id.viewpager_pager);
    mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);

    // set title indicator
    TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
    indicator.setViewPager(mPager, 1);

}

/* set of fragments callback interface implementations */

@Override
public void onMessageInitialisation() {

    Logger.d("Dash onMessageInitialisation");
    if (mMessagesFragment != null)
        mMessagesFragment.loadLastMessages();
}

@Override
public void onMessageSelected(Message selectedMessage) {

    Intent intent = new Intent(this, StreamActivity.class);
    intent.putExtra(Message.class.getName(), selectedMessage);
    startActivity(intent);
}

BasePagerActivity又名助手

public class BasePagerActivity extends FragmentActivity {

BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}

适配器

public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {

private Map<String, Fragment> mScreens;

public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {

    super(fm);
    this.mScreens = screenMap;
}

@Override
public Fragment getItem(int position) {

    return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}

@Override
public int getCount() {

    return mScreens.size();
}

@Override
public String getTitle(int position) {

    return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}

// hack. we don't want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {

    // TODO Auto-generated method stub
}

}

片段

public class MessagesFragment extends ListFragment {

private boolean mIsLastMessages;

private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;

private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;

// define callback interface
public interface OnMessageListActionListener {
    public void onMessageInitialisation();
    public void onMessageSelected(Message selectedMessage);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // setting callback
    mListener = (OnMessageListActionListener) activity;
    mIsLastMessages = activity instanceof DashboardActivity;

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    inflater.inflate(R.layout.fragment_listview, container);
    mProgressView = inflater.inflate(R.layout.listrow_progress, null);
    mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // instantiate loading task
    mLoadMessagesTask = new LoadMessagesTask();

    // instantiate list of messages
    mMessagesList = new ArrayList<Message>();
    mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
    setListAdapter(mAdapter);
}

@Override
public void onResume() {
    mListener.onMessageInitialisation();
    super.onResume();
}

public void onListItemClick(ListView l, View v, int position, long id) {
    Message selectedMessage = (Message) getListAdapter().getItem(position);
    mListener.onMessageSelected(selectedMessage);
    super.onListItemClick(l, v, position, id);
}

/* public methods to load messages from host acitivity, etc... */
}

解决方案

愚蠢的解决方案是将碎片保存在onSaveInstanceState(主机活动)与putFragment和获得他们在onCreate通过getFragment。但我仍然有一种奇怪的感觉,事情不应该这样发展……参见下面的代码:

    @Override
protected void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);
    getSupportFragmentManager()
            .putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}

protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    ...
    // create fragments to use
    if (savedInstanceState != null) {
        mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
                savedInstanceState, MessagesFragment.class.getName());
                StreamsFragment.class.getName());
    }
    if (mMessagesFragment == null)
        mMessagesFragment = new MessagesFragment();
    ...
}

当前回答

add:

   @SuppressLint("ValidFragment")

在你上课之前。

如果它不起作用,可以这样做:

@SuppressLint({ "ValidFragment", "HandlerLeak" })

其他回答

add:

   @SuppressLint("ValidFragment")

在你上课之前。

如果它不起作用,可以这样做:

@SuppressLint({ "ValidFragment", "HandlerLeak" })

BasePagerAdapter是什么?你应该使用一个标准的寻页适配器——FragmentPagerAdapter或FragmentStatePagerAdapter,这取决于你是否想要ViewPager不再需要的片段被保留(前者)或保存它们的状态(后者)并在再次需要时重新创建。

使用ViewPager的示例代码可以在这里找到

的确,跨活动实例管理视图寻呼机中的片段有点复杂,因为框架中的FragmentManager负责保存状态并恢复寻呼机所生成的任何活动片段。这实际上意味着,适配器在初始化时需要确保它与任何恢复的片段重新连接。您可以查看FragmentPagerAdapter或FragmentStatePagerAdapter的代码,以了解这是如何实现的。

有一点不同的意见,不要自己存储碎片,而是把它留给FragmentManager,当你需要对碎片做一些事情时,在FragmentManager中查找它们:

//make sure you have the right FragmentManager 
//getSupportFragmentManager or getChildFragmentManager depending on what you are using to manage this stack of fragments
List<Fragment> fragments = fragmentManager.getFragments();
if(fragments != null) {
   int count = fragments.size();
   for (int x = 0; x < count; x++) {
       Fragment fragment = fragments.get(x);
       //check if this is the fragment we want, 
       //it may be some other inspection, tag etc.
       if (fragment instanceof MyFragment) {
           //do whatever we need to do with it
       }
   }
}

如果你有很多fragment, instanceof check的成本可能不是你想要的,但是记住FragmentManager已经记录了Fragments,这是一件好事。

对于你的问题,我找到了另一个相对简单的解决方法。

正如你从FragmentPagerAdapter源代码中看到的,由FragmentPagerAdapter管理的片段存储在FragmentManager的标签下,使用:

String tag="android:switcher:" + viewId + ":" + index;

viewId是container. getid(),容器是你的ViewPager实例。索引是片段的位置。因此,你可以保存对象id到outState:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("viewpagerid" , mViewPager.getId() );
}

@Override
    protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null)
        viewpagerid=savedInstanceState.getInt("viewpagerid", -1 );  

    MyFragmentPagerAdapter titleAdapter = new MyFragmentPagerAdapter (getSupportFragmentManager() , this);        
    mViewPager = (ViewPager) findViewById(R.id.pager);
    if (viewpagerid != -1 ){
        mViewPager.setId(viewpagerid);
    }else{
        viewpagerid=mViewPager.getId();
    }
    mViewPager.setAdapter(titleAdapter);

如果你想与这个片段通信,你可以从FragmentManager中获取,比如:

getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewpagerid + ":0")

如果有人有问题与他们的FragmentStatePagerAdapter不正确地恢复其碎片的状态…新的片段是由FragmentStatePagerAdapter创建的,而不是它从状态恢复它们…

确保在调用viewpage . setadapter (fragmentStatePagerAdapter)之前调用viewpage . setoffscreenpagelimit ()

调用viewpage . setoffscreenpagelimit()…ViewPager将立即查找它的适配器并尝试获取它的片段。这可能发生在ViewPager有机会从savedInstanceState恢复片段之前(因此创建的新片段不能从savedInstanceState重新初始化,因为它们是新的)。