2012-03-15 2 views
94

Последней INFO:поддержка FragmentPagerAdapter держит ссылку на старые фрагменты

я сузил свою проблему вплоть до того, чтобы быть проблемой с fragmentManager сохранения экземпляров старых фрагментов и мой ViewPager быть синхронизировано с моим FragmentManager. См. Этот вопрос ... http://code.google.com/p/android/issues/detail?id=19211#makechanges. Я до сих пор не знаю, как это решить. Любые предложения ...

Я попытался отладить это в течение длительного времени, и любая помощь будет принята с благодарностью. Я использую FragmentPagerAdapter, который принимает список фрагментов, как так:

List<Fragment> fragments = new Vector<Fragment>(); 
fragments.add(Fragment.instantiate(this, Fragment1.class.getName())); 
... 
new PagerAdapter(getSupportFragmentManager(), fragments); 

Реализация является стандартной. Я использую библиотеки вычислений ActionBarSherlock и v4 для фрагментов.

Моя проблема заключается в том, что после выхода из приложения и открытия нескольких других приложений и возврата, фрагменты теряют ссылку на FragmentActivity (то есть getActivity() == null). Я не могу понять, почему это происходит. Я попытался вручную установить setRetainInstance(true);, но это не поможет. Я понял, что это происходит, когда мой FragmentActivity будет уничтожен, однако это все равно, если я открою приложение, прежде чем я получу сообщение журнала. Есть идеи?

@Override 
protected void onDestroy(){ 
    Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY"); 
    super.onDestroy(); 
} 

Адаптер:

public class PagerAdapter extends FragmentPagerAdapter { 
    private List<Fragment> fragments; 

    public PagerAdapter(FragmentManager fm, List<Fragment> fragments) { 
     super(fm); 

     this.fragments = fragments; 

    } 

    @Override 
    public Fragment getItem(int position) { 

     return this.fragments.get(position); 

    } 

    @Override 
    public int getCount() { 

     return this.fragments.size(); 

    } 

} 

Один из моих фрагментов раздели, но я commetned everyhting из Thats раздели и до сих пор не работает ...

public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener { 
... 

@Override 
public void onCreate(Bundle savedInstanceState){ 
    super.onCreate(savedInstanceState); 
    handler = new Handler();  
    setHasOptionsMenu(true); 
} 

@Override 
public void onAttach(Activity activity) { 
    super.onAttach(activity); 
    Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH"); 
    context = activity; 
    if(context== null){ 
     Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL"); 
    }else{ 
     Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT"); 
    } 

} 

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

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    View v = inflater.inflate(R.layout.my_fragment,container, false); 

    return v; 
} 


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

private void callService(){ 
    // do not call another service is already running 
    if(startLoad || !canSet) return; 
    // set flag 
    startLoad = true; 
    canSet = false; 
    // show the bottom spinner 
    addFooter(); 
    Intent intent = new Intent(context, MyService.class); 
    intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver); 
    context.startService(intent); 
} 

private ResultReceiver resultReceiver = new ResultReceiver(null) { 
    @Override 
    protected void onReceiveResult(int resultCode, final Bundle resultData) { 
     boolean isSet = false; 
     if(resultData!=null) 
     if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){ 
      if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){ 
       removeFooter(); 
       startLoad = false; 
       isSet = true; 
      } 
     } 

     switch(resultCode){ 
     case MyService.STATUS_FINISHED: 
      stopSpinning(); 
      break; 
     case SyncService.STATUS_RUNNING: 
      break; 
     case SyncService.STATUS_ERROR: 
      break; 
     } 
    } 
}; 

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
    menu.clear(); 
    inflater.inflate(R.menu.activity, menu); 
} 

@Override 
public void onPause(){ 
    super.onPause(); 
} 

public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) { 
    boolean loadMore = /* maybe add a padding */ 
     firstVisible + visibleCount >= totalCount; 

    boolean away = firstVisible+ visibleCount <= totalCount - visibleCount; 

    if(away){ 
     // startLoad can now be set again 
     canSet = true; 
    } 

    if(loadMore) 

} 

public void onScrollStateChanged(AbsListView arg0, int state) { 
    switch(state){ 
    case OnScrollListener.SCROLL_STATE_FLING: 
     adapter.setLoad(false); 
     lastState = OnScrollListener.SCROLL_STATE_FLING; 
     break; 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     adapter.setLoad(true); 
     if(lastState == SCROLL_STATE_FLING){ 
      // load the images on screen 
     } 

     lastState = OnScrollListener.SCROLL_STATE_IDLE; 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
     adapter.setLoad(true); 
     if(lastState == SCROLL_STATE_FLING){ 
      // load the images on screen 
     } 

     lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL; 
     break; 
    } 
} 

@Override 
public void onDetach(){ 
    super.onDetach(); 
    if(this.adapter!=null) 
     this.adapter.clearContext(); 

    Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED"); 
} 

public void update(final int id, String name) { 
    if(name!=null){ 
     getActivity().getSupportActionBar().setTitle(name); 
    } 

} 

}

Обновление метод вызывается, когда пользователь взаимодействует с другим фрагментом, и getActivity возвращает null. Вот метод другой фрагмент звонит ...

((MyFragment) pagerAdapter.getItem(1)).update(id, name); 

Я считаю, что, когда приложение будет уничтожен, то создается снова вместо того, чтобы просто запуска приложения до фрагмента по умолчанию приложение запускается, а затем переходит к ViewPager последняя известная страница. Это кажется странным, не следует ли приложению просто загружать фрагмент по умолчанию?

+12

Фрагмент Андроида отстой! – Hamidreza

+6

Бог, я люблю ваши сообщения в журнале: D –

ответ

109

У вас возникла проблема, потому что вы создаете и сохраняете ссылки на свои фрагменты за пределами PagerAdapter.getItem и пытаетесь использовать эти ссылки независимо от ViewPager. Как говорит Сераф, у вас есть гарантии того, что фрагмент был создан или добавлен в ViewPager в определенное время - это следует рассматривать как деталь реализации. ViewPager выполняет ленивую загрузку своих страниц; по умолчанию он загружает только текущую страницу, а другую - влево и вправо.

Если вы положили свое приложение на задний план, фрагменты, которые были добавлены в диспетчер фрагментов, автоматически сохраняются. Даже если ваше приложение убито, эта информация восстанавливается при повторном запуске приложения.

Теперь подумайте, что вы просмотрели несколько страниц, фрагменты A, B и C. Вы знаете, что они были добавлены в диспетчер фрагментов. Поскольку вы используете FragmentPagerAdapter, а не FragmentStatePagerAdapter, эти фрагменты по-прежнему будут добавлены (но потенциально отключены), когда вы перейдете на другие страницы.

Учтите, что вы затем используете свое приложение, а затем его убивают. Когда вы вернетесь, Android запомнит, что вы использовали Фрагменты A, B и C в диспетчере фрагментов и, таким образом, они воссоздавали их для вас, а затем добавляли их. Однако те, что добавлены в диспетчер фрагментов, теперь не те, что у вас есть в списке фрагментов в вашей деятельности.

FragmentPagerAdapter не будет пытаться позвонить getPosition, если для этой позиции страницы уже есть фрагмент. Фактически, поскольку фрагмент, воссозданный Android, никогда не будет удален, у вас нет надежды на его замену вызовом getPosition. Получить ручку на ней также довольно сложно получить ссылку на нее, потому что она была добавлена ​​с тегом, который вам неизвестен. Это по дизайну; вам не рекомендуется возиться с фрагментами, которыми управляет просмотр пейджер. Вы должны выполнять все свои действия внутри фрагмента, обмениваться информацией с активностью и, при необходимости, запрашивать переход на конкретную страницу.

Теперь вернемся к вашей проблеме с отсутствующей деятельностью. Вызов pagerAdapter.getItem(1)).update(id, name) после того, как все это произошло, возвращает вам фрагмент в вашем списке, , который еще не добавлен в диспетчер фрагментов, и поэтому он не будет иметь ссылку на активность. Я бы предположил, что ваш метод обновления должен изменить какую-либо общую структуру данных (возможно, управляемую операцией), а затем, когда вы переходите на конкретную страницу, она может нарисовать себя на основе этих обновленных данных.

+1

Мне нравится ваше решение, так как оно очень изящно и, скорее всего, реорганизует мой код, так как вы сказали, что я хочу сохранить 100% логики и данных внутри фрагмента. Для вашего решения потребуется довольно много хранения всех данных в FragmentActivity, а затем использовать каждый фрагмент для обработки Logic. Как я уже сказал, я предпочитаю это, но взаимодействие между фрагментами очень тяжелое, и это может раздражать управление. В любом случае Спасибо за подробное объяснение. Вы объяснили это намного лучше, чем я мог. – Maurycy

+22

Короткие: никогда не держите ссылку на фрагмент вне адаптера – passsy

+2

Итак, как экземпляр Activity обращается к экземпляру FragmentPagerAdapter, если он не создает экземпляр FragmentPagerAdapter? Разве будущие экземпляры просто не восстанавливают FragmentPagerAdapter и все его фрагменты? Должен ли FragmentPagerAdapter реализовать все интерфейсы фрагментов для управления связью между фрагментами? –

7

Не пытайтесь взаимодействовать между фрагментами в ViewPager. Вы не можете гарантировать, что другой фрагмент присоединен или даже существует. Вместо того, чтобы изменять заголовок панели действий из фрагмента, вы можете сделать это из своей активности. Используйте стандартный шаблон интерфейса для этого:

public interface UpdateCallback 
{ 
    void update(String name); 
} 

public class MyActivity extends FragmentActivity implements UpdateCallback 
{ 
    @Override 
    public void update(String name) 
    { 
     getSupportActionBar().setTitle(name); 
    } 

} 

public class MyFragment extends Fragment 
{ 
    private UpdateCallback callback; 

    @Override 
    public void onAttach(SupportActivity activity) 
    { 
     super.onAttach(activity); 
     callback = (UpdateCallback) activity; 
    } 

    @Override 
    public void onDetach() 
    { 
     super.onDetach(); 
     callback = null; 
    } 

    public void updateActionbar(String name) 
    { 
     if(callback != null) 
      callback.update(name); 
    } 
} 
+0

Конечно, теперь будет включать код ... Я уже регистрирую эти методы. OnDetach вызывается, когда onStop вызывается в функции фрагмента. onAttach вызывается непосредственно перед onCreate of FragmentActivity. – Maurycy

+0

Хм спасибо, но проблема все еще сохраняется. Вместо этого я создал вместо этого callback. Могу ли я не поместить логику во Фрагмент? У меня есть фрагменты, сбрасывающие службы и другие элементы, требующие ссылки на эту активность. Мне пришлось бы переместить всю мою логику приложений в основную деятельность, чтобы избежать проблемы, которую я испытываю. Это кажется немного ненужным. – Maurycy

+0

Хорошо, поэтому я только что полностью проверил это и прокомментировал весь мой код ... кроме обратного вызова, чтобы изменить заголовок. WHEN-приложение сначала запускается, и я перехожу на этот экран, название названия изменено np. После загрузки нескольких приложений, а затем возвращение названия больше не меняется. Похоже, что обратный вызов получен по старому экземпляру активности. Я не могу подумать о другом объяснении – Maurycy

27

Я решил эту проблему путем доступа к моим фрагментам непосредственно через FragmentManager вместо через FragmentPagerAdapter, как так. Во-первых, я должен выяснить тег фрагмента авто, порожденного FragmentPagerAdapter ...

private String getFragmentTag(int pos){ 
    return "android:switcher:"+R.id.viewpager+":"+pos; 
} 

Тогда я просто получить ссылку на этот фрагмент и делать то, что мне нужно вот так ...

Fragment f = this.getSupportFragmentManager().findFragmentByTag(getFragmentTag(1)); 
((MyFragmentInterface) f).update(id, name); 
viewPager.setCurrentItem(1, true); 

Внутри моих фрагментов я установил setRetainInstance(false);, чтобы вручную добавлять значения в пакет savedInstanceState.

@Override 
public void onSaveInstanceState(Bundle outState) { 
    if(this.my !=null) 
     outState.putInt("myId", this.my.getId()); 

    super.onSaveInstanceState(outState); 
} 

, а затем в OnCreate я захватил этот ключ и восстановил состояние фрагмента по мере необходимости. Легкое решение, которое было трудно (по крайней мере, для меня) выяснить.

+0

У меня проблема, похожая на вас, но я не совсем понимаю ваше объяснение, можете ли вы предоставить более подробную информацию? У меня также есть и адаптер, который хранит фрагменты в списке, но когда я возобновляю свое приложение из последних приложений, приложения выходят из строя, потому что есть отдельный фрагмент.Проблема здесь http://stackoverflow.com/questions/11631408/android-fragment-getactivity-sometime-returns-null –

+3

Каково ваше «мое» в ссылке на фрагмент? – Josh

+0

Мы все знаем, что это решение не подходит, но это самый простой способ использования «FragmentByTag» в ViewPager. – Youngjae

0

Поскольку FragmentManager позаботится о восстановлении ваших фрагментов для вас, как только будет вызываться метод onResume(), у меня есть фрагмент, вызывающий активность, и добавьте себя в список. В моем случае я сохраняю все это в своей реализации PagerAdapter. Каждый фрагмент знает свою позицию, потому что он добавляется к аргументам фрагмента при создании. Теперь, когда мне нужно манипулировать фрагментом по определенному индексу, все, что мне нужно сделать, это использовать список из моего адаптера.

Ниже приведен пример адаптера для пользовательского ViewPager, который вырастет фрагмент при его перемещении в фокус и уменьшит его, когда он выйдет из фокуса. Помимо классов Adapter и Fragment, у меня здесь все, что вам нужно, так это чтобы родительская активность могла ссылаться на переменную адаптера, и вы настроены.

адаптер

public class GrowPagerAdapter extends FragmentPagerAdapter implements OnPageChangeListener, OnScrollChangedListener { 

public final String TAG = this.getClass().getSimpleName(); 

private final int COUNT = 4; 

public static final float BASE_SIZE = 0.8f; 
public static final float BASE_ALPHA = 0.8f; 

private int mCurrentPage = 0; 
private boolean mScrollingLeft; 

private List<SummaryTabletFragment> mFragments; 

public int getCurrentPage() { 
    return mCurrentPage; 
} 

public void addFragment(SummaryTabletFragment fragment) { 
    mFragments.add(fragment.getPosition(), fragment); 
} 

public GrowPagerAdapter(FragmentManager fm) { 
    super(fm); 

    mFragments = new ArrayList<SummaryTabletFragment>(); 
} 

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

@Override 
public Fragment getItem(int position) { 
    return SummaryTabletFragment.newInstance(position); 
} 

@Override 
public void onPageScrollStateChanged(int state) {} 

@Override 
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 

    adjustSize(position, positionOffset); 
} 

@Override 
public void onPageSelected(int position) { 
    mCurrentPage = position; 
} 

/** 
* Used to adjust the size of each view in the viewpager as the user 
* scrolls. This provides the effect of children scaling down as they 
* are moved out and back to full size as they come into focus. 
* 
* @param position 
* @param percent 
*/ 
private void adjustSize(int position, float percent) { 

    position += (mScrollingLeft ? 1 : 0); 
    int secondary = position + (mScrollingLeft ? -1 : 1); 
    int tertiary = position + (mScrollingLeft ? 1 : -1); 

    float scaleUp = mScrollingLeft ? percent : 1.0f - percent; 
    float scaleDown = mScrollingLeft ? 1.0f - percent : percent; 

    float percentOut = scaleUp > BASE_ALPHA ? BASE_ALPHA : scaleUp; 
    float percentIn = scaleDown > BASE_ALPHA ? BASE_ALPHA : scaleDown; 

    if (scaleUp < BASE_SIZE) 
     scaleUp = BASE_SIZE; 

    if (scaleDown < BASE_SIZE) 
     scaleDown = BASE_SIZE; 

    // Adjust the fragments that are, or will be, on screen 
    SummaryTabletFragment current = (position < mFragments.size()) ? mFragments.get(position) : null; 
    SummaryTabletFragment next = (secondary < mFragments.size() && secondary > -1) ? mFragments.get(secondary) : null; 
    SummaryTabletFragment afterNext = (tertiary < mFragments.size() && tertiary > -1) ? mFragments.get(tertiary) : null; 

    if (current != null && next != null) { 

     // Apply the adjustments to each fragment 
     current.transitionFragment(percentIn, scaleUp); 
     next.transitionFragment(percentOut, scaleDown); 

     if (afterNext != null) { 
      afterNext.transitionFragment(BASE_ALPHA, BASE_SIZE); 
     } 
    } 
} 

@Override 
public void onScrollChanged(int l, int t, int oldl, int oldt) { 

    // Keep track of which direction we are scrolling 
    mScrollingLeft = (oldl - l) < 0; 
} 
} 

Фрагмент

public class SummaryTabletFragment extends BaseTabletFragment { 

public final String TAG = this.getClass().getSimpleName(); 

private final float SCALE_SIZE = 0.8f; 

private RelativeLayout mBackground, mCover; 
private TextView mTitle; 
private VerticalTextView mLeft, mRight; 

private String mTitleText; 
private Integer mColor; 

private boolean mInit = false; 
private Float mScale, mPercent; 

private GrowPagerAdapter mAdapter; 
private int mCurrentPosition = 0; 

public String getTitleText() { 
    return mTitleText; 
} 

public void setTitleText(String titleText) { 
    this.mTitleText = titleText; 
} 

public static SummaryTabletFragment newInstance(int position) { 

    SummaryTabletFragment fragment = new SummaryTabletFragment(); 
    fragment.setRetainInstance(true); 

    Bundle args = new Bundle(); 
    args.putInt("position", position); 
    fragment.setArguments(args); 

    return fragment; 
} 

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

    mRoot = inflater.inflate(R.layout.tablet_dummy_view, null); 

    setupViews(); 
    configureView(); 

    return mRoot; 
} 

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

    if (savedInstanceState != null) { 
     mColor = savedInstanceState.getInt("color", Color.BLACK); 
    } 

    configureView(); 
} 

@Override 
public void onSaveInstanceState(Bundle outState) { 

    outState.putInt("color", mColor); 

    super.onSaveInstanceState(outState); 
} 

@Override 
public int getPosition() { 
    return getArguments().getInt("position", -1); 
} 

@Override 
public void setPosition(int position) { 
    getArguments().putInt("position", position); 
} 

public void onResume() { 
    super.onResume(); 

    mAdapter = mActivity.getPagerAdapter(); 
    mAdapter.addFragment(this); 
    mCurrentPosition = mAdapter.getCurrentPage(); 

    if ((getPosition() == (mCurrentPosition + 1) || getPosition() == (mCurrentPosition - 1)) && !mInit) { 
     mInit = true; 
     transitionFragment(GrowPagerAdapter.BASE_ALPHA, GrowPagerAdapter.BASE_SIZE); 
     return; 
    } 

    if (getPosition() == mCurrentPosition && !mInit) { 
     mInit = true; 
     transitionFragment(0.00f, 1.0f); 
    } 
} 

private void setupViews() { 

    mCover = (RelativeLayout) mRoot.findViewById(R.id.cover); 
    mLeft = (VerticalTextView) mRoot.findViewById(R.id.title_left); 
    mRight = (VerticalTextView) mRoot.findViewById(R.id.title_right); 
    mBackground = (RelativeLayout) mRoot.findViewById(R.id.root); 
    mTitle = (TextView) mRoot.findViewById(R.id.title); 
} 

private void configureView() { 

    Fonts.applyPrimaryBoldFont(mLeft, 15); 
    Fonts.applyPrimaryBoldFont(mRight, 15); 

    float[] size = UiUtils.getScreenMeasurements(mActivity); 
    int width = (int) (size[0] * SCALE_SIZE); 
    int height = (int) (size[1] * SCALE_SIZE); 

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height); 
    mBackground.setLayoutParams(params); 

    if (mScale != null) 
     transitionFragment(mPercent, mScale); 

    setRandomBackground(); 

    setTitleText("Fragment " + getPosition()); 

    mTitle.setText(getTitleText().toUpperCase()); 
    mLeft.setText(getTitleText().toUpperCase()); 
    mRight.setText(getTitleText().toUpperCase()); 

    mLeft.setOnClickListener(new OnClickListener() { 

     @Override 
     public void onClick(View v) { 

      mActivity.showNextPage(); 
     } 
    }); 

    mRight.setOnClickListener(new OnClickListener() { 

     @Override 
     public void onClick(View v) { 

      mActivity.showPrevPage(); 
     } 
    }); 
} 

private void setRandomBackground() { 

    if (mColor == null) { 
     Random r = new Random(); 
     mColor = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255)); 
    } 

    mBackground.setBackgroundColor(mColor); 
} 

public void transitionFragment(float percent, float scale) { 

    this.mScale = scale; 
    this.mPercent = percent; 

    if (getView() != null && mCover != null) { 

     getView().setScaleX(scale); 
     getView().setScaleY(scale); 

     mCover.setAlpha(percent); 
     mCover.setVisibility((percent <= 0.05f) ? View.GONE : View.VISIBLE); 
    } 
} 

@Override 
public String getFragmentTitle() { 
    return null; 
} 
} 
94

я нашел простое решение, которое работало для меня.

Сделайте ваш адаптер фрагмента расширяет FragmentStatePagerAdapter вместо FragmentPagerAdapter и метод переопределения OnSave вернуть нуль

@Override 
public Parcelable saveState() 
{ 
    return null; 
} 

Это предотвратить андроид от воссоздания фрагмента


Через день я нашел еще и лучшее решение.

Позвоните setRetainInstance(true) для всех ваших фрагментов и сохраните ссылки на них где-нибудь. Я сделал это в статической переменной в своей деятельности, потому что объявлен как singleTask, и фрагменты могут оставаться неизменными все время.

Таким образом, андроид не воссоздает фрагменты, а использует одинаковые экземпляры.

+4

Спасибо, спасибо, спасибо. Так много, у меня действительно нет слов, чтобы поблагодарить вас. Мик, я преследовал этот вопрос за последние 10 дней и попытался так много методов. Но эти четыре магические линии сохранили мою жизнь :) –

+4

, статичная ссылка на фрагменты и/или действия - очень рискованная вещь, так как это может привести к утечкам памяти очень легко. Конечно, если вы будете осторожны, вы можете легко справиться с этим, установив их нулевыми, когда они больше не нужны. –

+0

Это сработало для меня. Фрагменты и их соответствующие представления сохраняют свои ссылки после сбоя и перезагрузки приложения. Благодаря! – swebal

0

Мое решение: Я устанавливаю почти каждый Вид как static. Теперь мое приложение прекрасно взаимодействует. Возможность называть статические методы повсюду, может быть, не очень хороший стиль, но зачем играть с кодом, который не работает? Я прочитал много вопросов и их ответы здесь на SO, и ни одно решение не принесло успеха (для меня).

Я знаю, что это может привести к утечке памяти и пустой кучи, и мой код не будет соответствовать другим проектам, но я не чувствую страха об этом - я тестировал приложение на разных устройствах и условиях, никаких проблем при все, платформа Android, похоже, справляется с этим. Пользовательский интерфейс обновляется каждую секунду, и даже на устройстве S2 ICS (4.0.3) приложение может обрабатывать тысячи геометров.

4

После нескольких часов поиска аналогичной проблемы, я думаю, есть другое решение. Это, по крайней мере, это сработало для меня, и мне нужно изменить пару строк.

Это проблема, которую я имел, у меня есть активность с пейджером вида, в котором используется FragmentStatePagerAdapter с двумя фрагментами. Все работает нормально, пока я не заставляю активность разрушаться (параметры разработчика) или я поворачиваю экран. Я сохраняю ссылку на два фрагмента после их создания внутри метода getItem.

В этот момент действие будет создано снова, и все будет хорошо работать в этот момент, но я потерял ссылку на мои фрагменты, так как getItem не вызван снова.

Это, как я установил эту проблему, внутри FragmentStatePagerAdapter:

@Override 
    public Object instantiateItem(ViewGroup container, int position) { 
     Object aux = super.instantiateItem(container, position); 

     //Update the references to the Fragments we have on the view pager 
     if(position==0){ 
      fragTabOne = (FragOffersList)aux; 
     } 
     else{ 
      fragTabTwo = (FragOffersList) aux; 
     } 

     return aux; 
    } 

Вы не получите вызов на GetItem снова, если адаптер уже есть ссылки на него внутри, и вы не должны изменить что. Вместо этого вы можете получить фрагмент, который он использует, глядя на этот другой метод instantiateItem(), который будет вызываться для каждого из ваших фрагментов.

Надежды, которые помогают кому угодно.

+0

Что делать, если у вас много фрагментов? Вы действительно хотите сохранить ссылку для каждого из них? Разве это не плохо для памяти? –

+0

Все зависит от того, чего именно вы пытаетесь достичь. Обычно с этими пейджерами вы не держите больше трех фрагментов за раз. Тот, что посередине, и один с каждой стороны. Для чего я хочу, мне действительно нужно иметь ссылку на фрагменты, но я знаю, что мне нужно иметь дело только с двумя. – Lancelot

19

Глобальное рабочее испытательное решение.

getSupportFragmentManager() содержит нулевую ссылку несколько раз, а просмотр пейджера не создает новых, поскольку находит ссылку на тот же фрагмент. Таким образом, чтобы использовать это, getChildFragmentManager() решает проблему простым способом.

Не делайте этого:

new PagerAdapter(getSupportFragmentManager(), fragments);

ли это:

new PagerAdapter(getChildFragmentManager() , fragments);

+2

это было бы возможно только в том случае, если вы размещаете (и создаете экземпляр) pagerAdapter внутри фрагмента, а не внутри своей деятельности. В этом случае вы правы, вы должны использовать childFragmentManager по умолчанию. Использование SupportFragmentManager было бы неправильно по умолчанию –

3

Вы можете удалить фрагменты, когда уничтожить ViewPager, в мой случай, я remo веды их на onDestroyView() моего фрагмента:

@Override 
public void onDestroyView() { 

    if (getChildFragmentManager().getFragments() != null) { 
     for (Fragment fragment : getChildFragmentManager().getFragments()) { 
      getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss(); 
     } 
    } 

    super.onDestroyView(); 
} 
0

Я сталкивался с таким же вопросом, но мой ViewPager был внутри TopFragment, который создал и установить адаптер с помощью setAdapter(new FragmentPagerAdapter(getChildFragmentManager())).

Я исправил эту проблему, перекрывая onAttachFragment(Fragment childFragment) в TopFragment, как это:

@Override 
public void onAttachFragment(Fragment childFragment) { 
    if (childFragment instanceof OnboardingDiamondsFragment) { 
     mChildFragment = (ChildFragment) childFragment; 
    } 

    super.onAttachFragment(childFragment); 
} 

Как уже известно (см ответы выше), когда childFragmentManager воссоздать себя, но и создавать фрагменты, которые были внутри ViewPager.
Важная часть состоит в том, что после этого он называет onAttachFragment, и теперь у нас есть ссылка на новый воссозданный фрагмент!

Надеется, что это поможет кто-нибудь получает этот старый Q, как я :)

0

Я решил проблему сохранения фрагментов в SparceArray:

public abstract class SaveFragmentsPagerAdapter extends FragmentPagerAdapter { 

    SparseArray<Fragment> fragments = new SparseArray<>(); 

    public SaveFragmentsPagerAdapter(FragmentManager fm) { 
     super(fm); 
    } 

    @Override 
    public Object instantiateItem(ViewGroup container, int position) { 
     Fragment fragment = (Fragment) super.instantiateItem(container, position); 
     fragments.append(position, fragment); 
     return fragment; 
    } 

    @Nullable 
    public Fragment getFragmentByPosition(int position){ 
     return fragments.get(position); 
    } 

} 
Смежные вопросы