Поддержка FragmentPagerAdapter содержит ссылку на старые фрагменты
ПОСЛЕДНЯЯ ИНФОРМАЦИЯ:
я сузил свою проблему до того, что стал проблемой с фрагментом, сохраняющим экземпляры старых фрагментов, и моим просмотрщиком, не синхронизированным с моим FragmentManager. См. Эту проблему... http://code.google.com/p/android/issues/detail?id=19211. Я до сих пор не знаю, как решить эту проблему. Какие-либо предложения...
Я пытался отладить это в течение долгого времени, и любая помощь будет принята с благодарностью. Я использую 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();
}
}
Один из моих фрагментов раздет, но я оценил все, что раздето, и до сих пор не работает...
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 возвращает ноль. Вот метод, который вызывает другой фрагмент...
((MyFragment) pagerAdapter.getItem(1)).update(id, name);
Я считаю, что когда приложение уничтожается, затем создается снова, а не просто запускает приложение до фрагмента по умолчанию, приложение запускается, а затем viewpager переходит на последнюю известную страницу. Это кажется странным, разве приложение не должно просто загружаться во фрагмент по умолчанию?
12 ответов
Вы столкнулись с проблемой, потому что вы создаете и храните ссылки на свои фрагменты вне PagerAdapter.getItem
и пытаются использовать эти ссылки независимо от ViewPager. Как говорит Серафим, у вас есть гарантии, что фрагмент был создан / добавлен в ViewPager в определенное время - это следует рассматривать как деталь реализации. ViewPager выполняет ленивую загрузку своих страниц; по умолчанию загружается только текущая страница, а слева и справа.
Если вы поместите свое приложение в фоновый режим, фрагменты, которые были добавлены в менеджер фрагментов, будут сохранены автоматически. Даже если ваше приложение убито, эта информация восстанавливается при повторном запуске приложения.
Теперь рассмотрим, что вы просмотрели несколько страниц, фрагменты A, B и C. Вы знаете, что они были добавлены в менеджер фрагментов. Потому что вы используете FragmentPagerAdapter
и не FragmentStatePagerAdapter
эти фрагменты будут по-прежнему добавляться (но потенциально могут быть отключены) при переходе на другие страницы.
Предположим, что вы затем создаете фоновое приложение, а затем его убивают. Когда вы вернетесь, Android вспомнит, что вы использовали Фрагменты A, B и C в менеджере фрагментов, поэтому он воссоздает их для вас, а затем добавляет их. Однако те, которые добавлены в диспетчер фрагментов, теперь НЕ являются теми, которые есть в вашем списке фрагментов в вашей деятельности.
FragmentPagerAdapter не будет пытаться вызвать getPosition
если для этой конкретной позиции страницы уже добавлен фрагмент. Фактически, поскольку фрагмент, воссозданный Android, никогда не будет удален, у вас нет надежды заменить его вызовом getPosition
, Получить указатель на него также довольно сложно, чтобы получить ссылку на него, потому что он был добавлен с неизвестным вам тегом. Это по замыслу; Вам не рекомендуется возиться с фрагментами, которыми управляет пейджер. Вы должны выполнять все свои действия внутри фрагмента, общаться с действием и запрашивать переключение на определенную страницу, если это необходимо.
Теперь вернемся к вашей проблеме с отсутствующей активностью. призвание pagerAdapter.getItem(1)).update(id, name)
после того, как все это произойдет, вы вернете фрагмент в своем списке, который еще не добавлен в диспетчер фрагментов, и поэтому у него не будет ссылки Activity. Я бы сказал, что ваш метод обновления должен изменить некоторую общую структуру данных (возможно, управляемую действием), а затем, когда вы переходите на определенную страницу, он может нарисовать себя на основе этих обновленных данных.
Я нашел простое решение, которое сработало для меня.
Сделайте так, чтобы ваш фрагментный адаптер расширял FragmentStatePagerAdapter вместо FragmentPagerAdapter и переопределил метод onSave для возврата null
@Override
public Parcelable saveState()
{
return null;
}
Это мешает Android воссоздать фрагмент
Через день я нашел другое и лучшее решение.
Вызов setRetainInstance(true)
для всех ваших фрагментов и сохраните ссылки на них где-нибудь. Я сделал это в статической переменной в своей деятельности, потому что она объявлена как singleTask и фрагменты могут оставаться неизменными все время.
Таким образом, Android не воссоздает фрагменты, а использует одни и те же экземпляры.
Я решил эту проблему, обращаясь к своим фрагментам напрямую через 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);
так что я могу вручную добавить значения в комплект saveInstanceState.
@Override
public void onSaveInstanceState(Bundle outState) {
if(this.my !=null)
outState.putInt("myId", this.my.getId());
super.onSaveInstanceState(outState);
}
и затем в OnCreate я беру этот ключ и восстанавливаю состояние фрагмента по мере необходимости. Простое решение, которое было трудно (по крайней мере, мне) найти.
Глобальное рабочее проверенное решение.
getSupportFragmentManager()
несколько раз сохраняет нулевую ссылку, а пейджер View не создает новую, поскольку находит ссылку на тот же фрагмент. Итак, чтобы преодолеть это использование getChildFragmentManager()
решает проблему простым способом.
Не делай этого:
new PagerAdapter(getSupportFragmentManager(), fragments);
Сделай это:
new PagerAdapter(getChildFragmentManager() , fragments);
Не пытайтесь взаимодействовать между фрагментами в 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);
}
}
Вы можете удалить фрагменты, когда уничтожаете viewpager, в моем случае я удалил их на onDestroyView()
моего фрагмента:
@Override
public void onDestroyView() {
if (getChildFragmentManager().getFragments() != null) {
for (Fragment fragment : getChildFragmentManager().getFragments()) {
getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
}
}
super.onDestroyView();
}
После нескольких часов поиска аналогичной проблемы, я думаю, что есть другое решение. По крайней мере, это сработало для меня, и мне нужно всего лишь изменить пару строк.
Это проблема, с которой я столкнулся. У меня есть операция с пейджером представления, который использует 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(), который будет вызываться для каждого из ваших фрагментов.
Надеюсь, это кому-нибудь поможет.
Поскольку люди не склонны читать комментарии, вот ответ, который в основном дублирует то, что я написал здесь:
основная причина проблемы заключается в том, что система Android не вызывает getItem
для получения фактически отображаемых фрагментов, но instantiateItem
. Этот метод сначала пытается найти и повторно использовать экземпляр фрагмента для данной вкладки вFragmentManager
. Только если этот поиск завершился неудачно (что происходит только в первый раз, когдаFragmentManager
создается заново) тогда getItem
называется. По очевидным причинам не следует воссоздавать фрагменты (которые могут быть тяжелыми), например, каждый раз, когда пользователь поворачивает свое устройство.
Чтобы решить эту проблему, вместо создания фрагментов сFragment.instantiate
в своей деятельности вы должны делать это с pagerAdapter.instantiateItem
и все эти звонки должны быть окружены startUpdate/finishUpdate
вызовы методов, которые запускают / фиксируют транзакцию фрагмента соответственно. getItem
должно быть местом, где действительно создаются фрагменты с использованием соответствующих конструкторов.
List<Fragment> fragments = new Vector<Fragment>();
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myLayout);
ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);
adapter.startUpdate(viewPager);
fragments.add(adapter.instantiateItem(viewPager, 0));
fragments.add(adapter.instantiateItem(viewPager, 1));
// and so on if you have more tabs...
adapter.finishUpdate(viewPager);
}
class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager manager) {super(manager);}
@Override public int getCount() {return 2;}
@Override public Fragment getItem(int position) {
if (position == 0) return new Fragment0();
if (position == 1) return new Fragment1();
return null; // or throw some exception
}
@Override public CharSequence getPageTitle(int position) {
if (position == 0) return getString(R.string.tab0);
if (position == 1) return getString(R.string.tab1);
return null; // or throw some exception
}
}
Я столкнулся с той же проблемой, но мой 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, как я:)
Я решил проблему, сохранив фрагменты в 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);
}
}
Мое решение: я устанавливаю почти каждый вид как static
, Теперь мое приложение взаимодействует идеально. Возможность вызывать статические методы из любого места, возможно, не очень хороший стиль, но зачем играть с кодом, который не работает? Я прочитал много вопросов и ответов на них здесь на SO, и ни одно решение не принесло успеха (для меня).
Я знаю, что это может привести к утечке памяти и лишней куче, и мой код не подойдет для других проектов, но я не боюсь этого - я тестировал приложение на разных устройствах и в любых условиях, никаких проблем, Android Платформа, кажется, в состоянии справиться с этим. Пользовательский интерфейс обновляется каждую секунду, и даже на устройстве S2 ICS (4.0.3) приложение может обрабатывать тысячи геомаркеров.
Поскольку 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;
}
}
Просто чтобы ты знал...
В добавление к куче проблем с этими классами, есть довольно интересная ошибка, которой стоит поделиться.
Я использую ViewPager для навигации по дереву элементов (выберите элемент, и пейджер просмотра анимирует прокрутку вправо, и появится следующая ветвь, вернитесь назад, и ViewPager прокрутит в обратном направлении, чтобы вернуться к предыдущему узлу),
Проблема возникает, когда я помещаю фрагменты в конец FragmentStatePagerAdapter. Он достаточно умен, чтобы замечать, что элементы меняются, и достаточно умен, чтобы создавать и заменять фрагмент при изменении элемента. Но недостаточно умный, чтобы отбросить состояние фрагмента, или недостаточно умный, чтобы обрезать внутренне сохраненные состояния фрагмента при изменении размера адаптера. Поэтому, когда вы извлекаете элемент и вставляете новый в конец, фрагмент для нового элемента получает сохраненное состояние фрагмента для старого элемента, что вызвало абсолютный хаос в моем коде. Мои фрагменты содержат данные, которые могут потребовать много работы для повторного получения из Интернета, поэтому не сохранение состояния действительно не было возможным вариантом.
У меня нет чистого обходного пути. Я использовал что-то вроде этого:
public void onSaveInstanceState(Bundle outState) {
IFragmentListener listener = (IFragmentListener)getActivity();
if (listener!= null)
{
if (!listener.isStillInTheAdapter(this.getAdapterItem()))
{
return; // return empty state.
}
}
super.onSaveInstanceState(outState);
// normal saving of state for flips and
// paging out of the activity follows
....
}
Несовершенное решение, потому что новый экземпляр фрагмента все еще получает Bundle SavedState, но по крайней мере он не несет устаревших данных.