onBackPressed для фрагментации (A, B, C)

У меня есть основная деятельность и три фрагмента (один, два, три). И все эти фрагменты расширяют BaseFragment:

public abstract class BaseFragment extends Fragment {
private Toolbar mToolbar;
private ActionBar mActionBar;

@Override
@CallSuper
public void onAttach(Activity context) {
    super.onAttach(context);

    mToolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
    mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
    //mActionBar.setTitle(setMyTitle());
    //Log.i("BaseFragment", "onAttach = "+getBackStackCount());
    resetToolbarNavigation(getBackStackCount()!=0);
}

//protected abstract String setMyTitle();

@Override
@CallSuper
public void onDetach() {
    super.onDetach();
    Log.i("BaseFragment", "onDetach = " + (getBackStackCount() - 1));
    resetToolbarNavigation((getBackStackCount() - 1 )!= 0);
}

private int getBackStackCount() {
    int b = getActivity().getSupportFragmentManager().getBackStackEntryCount();
    Log.i("BaseFragment", "getBackStackCount = "+b);

    return b;
}

private void resetToolbarNavigation(boolean backNavigationEnabled) {
    mActionBar.setDisplayHomeAsUpEnabled(backNavigationEnabled);
    Log.i("BaseFragment", "resetToolbarNavigation");
    if (backNavigationEnabled) {
        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i("resetToolbarNavigation", "setNavigationOnClickListener");
                getActivity().onBackPressed();
            }
        });
    }
    else {
        ((StartPageActivity) getActivity()).initToolbar();
        ((StartPageActivity) getActivity()).syncState();
    }
}

}

Но когда я нажимаю стрелку назад, я получаю исключение

09-22 19:28:13.233    5643-5643/com.test.mylist E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NullPointerException
        at com.test.exemple.BaseFragment$1.onClick(BaseFragment.java:56)
        at android.view.View.performClick(View.java:2485)
        at android.view.View$PerformClick.run(View.java:9080)
        at android.os.Handler.handleCallback(Handler.java:587)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:130)
        at android.app.ActivityThread.main(ActivityThread.java:3683)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:507)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
        at dalvik.system.NativeStart.main(Native Method)

onBackPressed внутри основного действия

    @Override
public void onBackPressed() {
        getSupportFragmentManager().popBackStack();
        Log.d("StartPageActivity", "onBackPressed " + getSupportFragmentManager().getBackStackEntryCount());

}

Вы можете помочь мне? Где ошибка? Что я делаю не так?


person one_man    schedule 22.09.2015    source источник
comment
какая строка 56 в BaseFragment?   -  person Jon    schedule 22.09.2015
comment
getActivity().onBackPressed();   -  person one_man    schedule 22.09.2015
comment
getActivity() должен возвращать значение null. Вы уверены, что действие прикреплено?   -  person Jon    schedule 22.09.2015
comment
Когда я открываю программу, отображаемый фрагмент 1, затем переходит к фрагменту 2, а затем переходит к фрагменту 3. Когда я нажимаю назад, 3->2 все работает, но когда 2->1 не работает, эта ошибка ... И я открываю только фрагмент 1 и 2(2->1 ) тоже хорошо работают. Но это не работает, когда у меня есть 3 и более фрагментов.........Отличный английский!))))   -  person one_man    schedule 22.09.2015


Ответы (2)


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

  • Отдельные обязанности: Activity должен заботиться о своих вещах, а Fragment должен нести ответственность только за функциональные возможности Fragment. Для этого вы можете сделать так, чтобы ваш фрагмент публиковал интерфейсы с методами, которые вам нужны для реализации Activity, а затем просто использовать его во фрагменте. Например, в вашем коде у вас есть сильные отношения в вашем фрагменте из вашей деятельности. Метод resetToolbarNavigation взаимодействует с представлениями, которые принадлежат Activity, обратите внимание, что метод не делает ничего, что является ответственностью Fragment, поэтому весь метод должен быть реализован в действии, тогда вы можете опубликовать интерфейс в своем BaseFragment, например:

    //Get the instance of the activity implementation 
    private BaseFragmentActions baseFragmentActions;
    //Publish your interface with the methods your Activity will implement 
    public interface BaseFragmentActions {
        public void resetToolbarNavigation(boolean backNavigationEnabled);
    }
    
    //In your onAttach get the implementation from your activity
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
    
        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            baseFragmentActions = (BaseFragmentActions)activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement interface BaseFragmentActions");
        }
    }
    

Затем в своей деятельности просто реализуйте метод и используйте его в своем фрагменте, например baseFragmentActions.resetToolbarNavigation(true); Обратите внимание, что таким образом BaseFragment не заботится об активности, просто попросите, чтобы активность, содержащая его, реализовывала необходимые ему методы. Для этого вы также можете использовать EventBus.

  • Транзакции между фрагментами всегда должны выполняться в действии, которое их содержит. Фрагмент не должен знать/заботиться о том, какой фрагмент должен быть загружен после.

  • Используйте концепцию «Активность знает, что это фрагменты, но фрагмент не знает, что это активность»

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

Надеюсь, это поможет вам в качестве концепции будущего дизайна и извините за мой английский.

person labreu    schedule 22.09.2015
comment
Итак, мне нужно создать метод в среднем фрагменте и реализовать этот интерфейс в моей деятельности? Внутренняя активность реализует метод переопределения..... - person one_man; 23.09.2015
comment
Да, например, ваша активность должна реализовывать этот интерфейс: открытый класс MyActivity расширяет активность, реализует BaseFragmentActions, а затем переопределяет методы для этого интерфейса в вашей деятельности, затем в вашем фрагменте у вас есть доступ baseFragmentActions к этим реализациям, чтобы вы могли его использовать, но есть не ссылка на деятельность, которая является хорошей частью этого - person labreu; 23.09.2015
comment
Вы хорошо знакомы со структурой андроида. Не могли бы вы увидеть это stackoverflow.com/questions/32543292/gray-line- вид изнутри карты - person one_man; 23.09.2015
comment
если вы можете посмотреть там stackoverflow. com/questions/35514833/ у вас проблемы с ответом - person one_man; 19.02.2016

Вот что я думаю, что происходит на основе ваших комментариев.

  1. вы открываете фрагмент 1, и resetToolbarNavigation(boolean) вызывается из onAttach
  2. вы переходите к фрагменту 2, и resetToolbarNavigation(boolean) вызывается из onAttach
  3. вы переходите к фрагменту 3, и resetToolbarNavigation(boolean) вызывается из onAttach
  4. you click the navigation button and fragment 3 handles it properly popping the backstack and navigating you back to fragment 2
    • resetToolbarNavigation(boolean) is not called from onAttach on fragment 2 because the fragment/activity wasn't destroyed and the fragment was never detached.
  5. вы нажимаете кнопку навигации, и она снова обрабатывается фрагментом 3, который не запущен, поэтому getActivity() возвращает null

введите здесь описание изображения

Вы должны попробовать установить прослушиватель навигации при отображении фрагмента. Если вы правильно выполняете транзакции фрагментов, вы сможете сделать это в onResume().

person Jon    schedule 22.09.2015