Страницы

Поиск по вопросам

Показаны сообщения с ярлыком fragment-transaction. Показать все сообщения
Показаны сообщения с ярлыком fragment-transaction. Показать все сообщения

понедельник, 24 февраля 2020 г.

java.lang.NullPointerException: Attempt to write to field 'int android.support.v4.app.Fragment.mNextAnim' on a null object reference [дубликат]

#java #android #android_fragment #fragment_transaction #fragment_manager


        
             
                
                    
                        
                            На этот вопрос уже даны ответы здесь:
                            
                        
                    
                
                        
                            Что такое Null Pointer Exception и как его исправить?
                                
                                    (4 ответа)
                                
                        
                                Закрыт 3 года назад.
            
                    
На Nexus 7 c Android 5.0(не 4.4) происходит крах с Exception из заголовка. После
непродолжительных поисков был найден следующий ответ. Меня он не устраивает, так как
в силу обстоятельств не могу отказаться от android.support.v4.app.  

Возможность операции над фрагментом, равным null, я тоже рассматривал, но такого
быть не должно. Отрывок кода под подозрением:  

String tag = FRAGMENT_STATE;
FragmentManager fragmentManager = getSupportFragmentManager();
...
stateFragment = new Fragment();
...
if (stateFragment != null) {
...
android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();//.support.v4
transaction.replace(R.id.content_frame, stateFragment, tag);
...          
transaction.commitAllowingStateLoss();  


Еще есть следующий отрывок:

  //Типо подключаю фрагменты
  if(onerFragment == null) Logi.d("OneFragment is Null");
  //if (oneFragment == null || twoeFragment == null) {
  Logi.d("блок подключения фрагментов");
  fragmentTransaction = fragmentManager.beginTransaction();
  Logi.d("oneGragment constructer before");
  oneFragment = new OneGragment();
  fragmentTransaction.add(R.id.one_fragment, oneFragment);
  Logi.d("TwoFragment constructer before");
  twoFragment = new TwoFragment();
  fragmentTransaction.add(R.id.two_fragment, twoFragment);

  fragmentTransaction.commitAllowingStateLoss();  


Логи:


  FATAL EXCEPTION: main
   Process: название пакета, PID: 7755
   java.lang.NullPointerException: Attempt to write to field 'int android.support.v4.app.Fragment.mNextAnim'
on a null object reference
      at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:709)
      at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1501)
      at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:458)
      at android.os.Handler.handleCallback(Handler.java:739)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:135)
      at android.app.ActivityThread.main(ActivityThread.java:5221)
      at java.lang.reflect.Method.invoke(Native Method)
      at java.lang.reflect.Method.invoke(Method.java:372)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)  


P.S Устройства у меня нет, логи взяты из GoogleTestLab, тестирование на эмуляторе
тоже не смогу. есть ли у кого-нибудь идеи что делать с этим "эксепшеном"?
    


Ответы

Ответ 1



Вот здесь сказано: Это происходит из-за того, что Вы вызываете методы remove(), add() и т.п. со значением null. Поразумевается, что replace() - это remove() и за ним add().

вторник, 10 декабря 2019 г.

Перезапуск фрагмента из RecyclerView

#java #android #fragment_back_stack #fragment_manager #fragment_transaction


Разрабатываю приложение несколько месяцев, оно постепенно обрастает новыми добавлениями.
Вкратце - это интернет магазин, оформлен в виде одной активити с NavigationDrawer,
все страницы реализованы на фрагментах, в некоторых случаях в дело вступает backstack,
например в случае каталога, у которого есть 4 уровня вложенности - основные категории,
подкатегории, список продуктов, страница продукта. первые два оформлены в виде ListView,
третий в виде RecyclerView с GridLayoutManager. 

Метод onBackPressed в активити определен следующим образом 

@Override
public void onBackPressed() {

    ProductPageFragment ppf = (ProductPageFragment)  getFragmentManager().findFragmentByTag(Flags.Stack.PRODUCT_PAGE_FRAGMENT);
    Log.d("ppf", ppf + "");
    SubcatalogFragment sf = (SubcatalogFragment) getFragmentManager().findFragmentByTag(Flags.Stack.SUBCATALOG_FRAGMENT);
    Log.d("sf", sf + "");
    ProductGridFragment pf  = (ProductGridFragment) getFragmentManager().findFragmentByTag(Flags.Stack.PRODUCT_GRID_FRAGMENT);
    Log.d("pf", pf + "");
    CartMainFragment cmf = (CartMainFragment) getFragmentManager().findFragmentByTag(Flags.Stack.CART_FRAGMENT);
    Log.d("cmf", cmf + "");
    PurchaseFragment purf = (PurchaseFragment) getFragmentManager().findFragmentByTag(Flags.Stack.PURCHASE_FRAGMENT);
    Log.d("purf", purf + "");

    boolean sfVisible = sf != null && sf.isVisible();
    boolean ppfVisible = ppf != null && ppf.isVisible();
    boolean pfVisible = pf != null && pf.isVisible();
    boolean cmfVisible = cmf != null && cmf.isVisible();
    boolean purfVisible = purf != null && purf.isVisible();


    //If the fragment exists and has some back-stack entry
    if ((sfVisible || pfVisible || ppfVisible || cmfVisible || purfVisible) && getFragmentManager().getBackStackEntryCount()
> 0) {
        bpCount = 0;
        getFragmentManager().popBackStack();
    }

    //Else, nothing in the direct fragment back stack
    else {
        // Let super handle the back press
        bpCount++;
        switch (bpCount) {
            case 1:
                Toast.makeText(
                        getApplicationContext(),
                        "Для выхода нажмите \"Назад\" ещё раз",
                        Toast.LENGTH_SHORT
                ).show();
                break;
            case 2:
                super.onBackPressed();
                break;
        }
    }
}


bpCount, в данном случае, – счетчик нажатий на back, чтобы приложение не закрывалось
по первому нажатию.

На главной странице потребовалось разместить популярные продукты, использовал для
этого RecyclerView с GridLayoutManager и тем же viewholder, что и в списке продуктов.
По нажатию на итем должна открываться страница продукта. Запускаем приложение - видим
список продуктов, открываем страницу первого попавшегося продукта - отрисовывается
нормально. Нажимаем клавишу back, видим главную, открываем другой продукт (или этот
же, не имеет значения) и фрагмент не отрисовывается. если помещать его в контейнер
методом replace - то видим серый экран (стандартный фон) на месте контейнера, если
add, то RecyclerView отскролливается на выбранный продукт, при этом в обоих случаях
onCreateView фрагмета выполняется. 

Код, вызывающий фрагмент выглядит так:

private void showProductPage() {
    ProductPageFragment ppf = new ProductPageFragment();

    Bundle bundle = new Bundle();
    bundle.putParcelable(PRODUCT_TAG, product);

    ppf.setArguments(bundle);

    ((Activity) this.itemView.getContext()).getFragmentManager().beginTransaction()
            .replace(R.id.container, ppf, Flags.Stack.PRODUCT_PAGE_FRAGMENT)
            .addToBackStack(Flags.Stack.CATALOG_BACKSTACK)
            .commit();
}


this - это viewholder, itemView - view холдера, контекст является инстансом активити.

Мне бы хотелось понять, почему такое может происходить, ведь по первому клику фрагмент
отрисовывается нормально. И если кто нибудь подробно может мне объяснить разницу между
основными методами FragmentTransaction, то я тоже был бы очень признателен. Спасибо.

PS. Да, забыл, к чему я, собственно, расписывал как устроен каталог - во всем каталоге
нажатие на back отрабатывает нормально, а вот со страницей продукта происходит такая
же ерунда, как и при открытии его с главной. При первом запуске страница продукта отрисовывается,
а потом - нет.

PPS. Удалил лишние проверки из popBackStack, на всякий случай, ситуация не изменилась.
Вроде всё выполняется в одном потоке, но объявлял syncronized как метод, так и блок,
результат тот же. Мне не понятно, почему метод onCreateView выполняется, как и остальные
методы фрагмента, в  методе onResume вывожу isVisible и оно true, но при этом вьющка
не рисуется. Или может она рисуется где то ещё? Смотрим реализацию isVisible в базовом
классе фрагмента:

    final public boolean isVisible() {
        return isAdded() && !isHidden() && mView != null
                && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
    }


Значит нам надо выяснить, как реализованы и что значат все эти условия. Ищем isAdded

    final public boolean isAdded() {
        return mActivity != null && mAdded; // Задается FragmentManager при добавлении
фрагмента
    }


isHidden:

    final public boolean isHidden() {
        return mHidden; // значение задается функциями showFragment и hideFragment
вызванные из экземляра FragmentManager
    }


mView это view фрагмента, который создаётся в onCreateView, 

view.getWindowToken выглядит так

    public IBinder getWindowToken() {
        return mAttachInfo != null ? mAttachInfo.mWindowToken : null;


// возвращет null если view не присоединено к контейнеру
        }
ну и View.VISIBLE означает, что view фрагмента отрисовывается. 

Следовательно ОС считает, что фрагмент у меня нарисован и существует. 
    


Ответы

Ответ 1



Не увидел в вашем коде используете ли вы рекомендованный паттерн OnFragmentInteraction - судя по обилию всяких условий в onBackPressed видимо нет, а зря. Смысл паттерна, в том, что корневой Activity должен реализовывать интерфейс отрабатывающий изменения в подчиненных фрагментах. Грубо говоря, если вы выбрали некий элемент списка, то корневой Activity должен быть в курсе этого, чтобы при возврате обратно отработать эти изменения. По-моему это плохая идея ловить onBackPressed - гораздо удобнее работать с onBackStackChanged примерно так: @Override public void onBackStackChanged() { Log.i(TAG, "Back stack changed!"); Log.i(TAG, "Stack size=" + getSupportFragmentManager().getBackStackEntryCount()); if(getSupportFragmentManager().getBackStackEntryCount()==0) {//we're on main //перерисовываем здесь } shouldDisplayHomeUp(); } Для перерисовки надо или дать знать фрагменту, что контент изменился - типа notifyDatasetChanged или просто перерисовать Cursor который лежит под фрагментом (если лежит). Можно попробовать отцепить и заново прицепить фрагмент: getSupportFragmentManager() .beginTransaction() .detach(contentFragment) .attach(contentFragment) .commit(); Говорят работает - честно скажу не пробовал.

Ответ 2



Попробуйте в адаптер передать ссылку на фрагмент, коий содержит ваш RecyclerView и вместо FragmentManager-а активити использовать FragmentManager фрагмента, который можно получить методом getChildFragmentManager() класса Fragment. Т.е. вместо ((Activity) this.itemView.getContext()).getFragmentManager() сделать instanceOfYoursFragmentThatHoldesRecycleViewWhichYouPassToAdaptersClass.getChildFragmentManager()

понедельник, 17 июня 2019 г.

Фрагменты наслаиваются друг на друга. [дубликат]

На данный вопрос уже ответили: Наложение фрагмента на фрагмент. За добавленным фрагментом виден предыдущий 2 ответа @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); layoutForFragment = (FrameLayout) findViewById(R.id.layout_for_fragment); fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.add(R.id.layout_for_fragment,cardStation);
fragmentTransaction.commit(); }
В методе OnCreate я вставляю фрагмент во FrameLayout. Проблема в том, что при повороте экрана или при вылете с ошибкой фрагменты накладываются друг на друга на одно и то же место. Как решить проблему ?


Ответ

При повороте экрана активити пересоздаётся. При пересоздании активити система пытается восстановить предыдущее состояние. В предыдущем состоянии в активити добавлен фрагмент и система его восстанавливает, добавляя на экран. После этого выполняется onCreate, где вы добавляете ещё один фрагмент. Итого их у вас (число поворотов экрана)*1
Вам надо просто проверять восстановила ли активити к-л фрагмент, поискав его во FragmentManager по ID контейнера, и, если нет, то добавлять его:
cardStation = getSupportFragmentManager().findFragmentById(R.id.layout_for_fragment); if (cardStation == null) { cardStation = new CardStation(); getSupportFragmentManager(). beginTransaction().add(R.id.layout_for_fragment, cardStation) .commit(); }

вторник, 7 мая 2019 г.

java.lang.NullPointerException: Attempt to write to field 'int android.support.v4.app.Fragment.mNextAnim' on a null object reference [дубликат]

На данный вопрос уже ответили: Что такое Null Pointer Exception и как его исправить? 4 ответа На Nexus 7 c Android 5.0(не 4.4) происходит крах с Exception из заголовка. После непродолжительных поисков был найден следующий ответ. Меня он не устраивает, так как в силу обстоятельств не могу отказаться от android.support.v4.app.
Возможность операции над фрагментом, равным null, я тоже рассматривал, но такого быть не должно. Отрывок кода под подозрением:
String tag = FRAGMENT_STATE; FragmentManager fragmentManager = getSupportFragmentManager(); ... stateFragment = new Fragment(); ... if (stateFragment != null) { ... android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();//.support.v4 transaction.replace(R.id.content_frame, stateFragment, tag); ... transaction.commitAllowingStateLoss();
Еще есть следующий отрывок:
//Типо подключаю фрагменты if(onerFragment == null) Logi.d("OneFragment is Null"); //if (oneFragment == null || twoeFragment == null) { Logi.d("блок подключения фрагментов"); fragmentTransaction = fragmentManager.beginTransaction(); Logi.d("oneGragment constructer before"); oneFragment = new OneGragment(); fragmentTransaction.add(R.id.one_fragment, oneFragment); Logi.d("TwoFragment constructer before"); twoFragment = new TwoFragment(); fragmentTransaction.add(R.id.two_fragment, twoFragment);
fragmentTransaction.commitAllowingStateLoss();
Логи:
FATAL EXCEPTION: main Process: название пакета, PID: 7755 java.lang.NullPointerException: Attempt to write to field 'int android.support.v4.app.Fragment.mNextAnim' on a null object reference at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:709) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1501) at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:458) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
P.S Устройства у меня нет, логи взяты из GoogleTestLab, тестирование на эмуляторе тоже не смогу. есть ли у кого-нибудь идеи что делать с этим "эксепшеном"?


Ответ

Вот здесь сказано: Это происходит из-за того, что Вы вызываете методы remove(), add() и т.п. со значением null
Поразумевается, что replace() - это remove() и за ним add()