백 스택에서 재개에 대한 조각


94

Android 2.2에서 Fragments를 사용하기 위해 호환성 패키지를 사용하고 있습니다. 프래그먼트를 사용하고 그들 사이에 전환을 백 스택에 추가 할 때, 활동의 onResume과 동일한 동작을 달성하고 싶습니다. 백 스택, 프래그먼트 내에서 일종의 콜백을 활성화하고 싶습니다 (예 : 공유 UI 리소스에서 특정 변경을 수행하기 위해).

프래그먼트 프레임 워크 내에 내장 콜백이 없다는 것을 알았습니다. 이것을 달성하기위한 좋은 관행이 있습니까?


13
좋은 질문입니다. 이 시나리오의 사용 사례로 표시되는 조각에 따라 작업 표시 줄 제목을 변경하려고하는데 API에서 콜백이 누락 된 것처럼 보입니다.
Manfred Moser 2012 년

3
활동은 한 번에 표시되는 조각을 알고 있으므로 제목을 설정할 수 있습니다. 또한 onCreateView팝 후 조각에 대해 호출되는 작업을 수행 할 수 있다고 가정합니다 .
PJL

1
@PJL +1 참조에 따르면 그것이 수행되어야하는 방식입니다. 그러나 나는 리스너 방식을 선호
모모

답변:


115

더 나은 솔루션이 없기 때문에이 작업을 수행했습니다. 하나의 활동 (MyActivity)과 서로를 대체하는 단편이 거의 없다고 가정합니다 (한 번에 하나만 표시됨).

MyActivity에서 다음 리스너를 추가하십시오.

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

(보시다시피 호환성 패키지를 사용하고 있습니다).

getListener 구현 :

private OnBackStackChangedListener getListener()
    {
        OnBackStackChangedListener result = new OnBackStackChangedListener()
        {
            public void onBackStackChanged() 
            {                   
                FragmentManager manager = getSupportFragmentManager();

                if (manager != null)
                {
                    MyFragment currFrag = (MyFragment) manager.findFragmentById(R.id.fragmentItem);

                    currFrag.onFragmentResume();
                }                   
            }
        };

        return result;
    }

MyFragment.onFragmentResume()"뒤로"를 누른 후 호출됩니다. 하지만 몇 가지주의 사항 :

  1. 모든 트랜잭션을 백 스택에 추가했다고 가정합니다 (사용 FragmentTransaction.addToBackStack()).
  2. 스택이 변경 될 때마다 활성화되므로 (애니메이션과 같은 백 스택에 다른 항목을 저장할 수 있음) 동일한 조각 인스턴스에 대해 여러 호출을받을 수 있습니다.

3
그것이 당신에게 효과가 있다면 당신 자신의 대답을 올바른 것으로 받아 들여야합니다.
powerj1984

7
쿼리하는 조각 ID 측면에서 어떻게 작동합니까? 각 조각마다 어떻게 다르고 백 스택에서 올바른 조각을 찾을 수 있습니까?
Manfred Moser 2012 년

2
@Warpzit 그 메소드는 호출되지 않았고, 안드로이드 문서는 결코 호출되지 않을 것이라고 조용히 인정합니다 (활동이 재개 될 때만 호출됩니다. 활동이 이미 활성화 된 경우 절대 호출되지 않습니다)
Adam

2
우리가 이것을해야한다는 것은 어리석은 일! 하지만 다른 사람이이 "기능"을 찾을 수있게되어 기쁩니다
stevebot 2014-04-04

3
onResume ()은 호출되지 않습니다
Marty Miller

33

제안 된 솔루션을 약간 변경했습니다. 나를 위해 더 잘 작동합니다.

private OnBackStackChangedListener getListener() {
    OnBackStackChangedListener result = new OnBackStackChangedListener() {
        public void onBackStackChanged() {
            FragmentManager manager = getSupportFragmentManager();
            if (manager != null) {
                int backStackEntryCount = manager.getBackStackEntryCount();
                if (backStackEntryCount == 0) {
                    finish();
                }
                Fragment fragment = manager.getFragments()
                                           .get(backStackEntryCount - 1);
                fragment.onResume();
            }
        }
    };
    return result;
}

1
차이점과 이것을 사용하는 이유를 설명하십시오.
수학 냉각기

2
내 솔루션과 이전 솔루션의 차이점은 조각 더미에서 추가, 교체 또는 뒤로 이동과 같은 모든 조각 변경시 맨 위 조각의 "onResume"메서드가 호출된다는 것입니다. 이 메서드에는 하드 코딩 된 조각 ID가 없습니다. 기본적으로 메모리에 이미로드되었는지 여부에 관계없이 모든 변경 사항에 대해보고있는 조각에서 onResume ()을 호출합니다.
Brotoo25

9
getFragments()SupportFragmentManager에서 호출 된 메서드에 대한 기록이 없습니다 ... -1 :-/
Protostome

1
OnResume ()이 호출됩니다.하지만 빈 화면이 나타납니다.이 문제를 해결하는 다른 방법이 있습니까?
kavie 2014-10-06

backStackEntryCount가 0이면 이것이 ArrayOutOfBounds Exception으로 이어진다 고 생각합니다. 내가 잘못 했습니까? 게시물을 수정하려고했지만 거부되었습니다.
마이크 T

6

popStackBack() 다음과 같은 콜백을 사용할 수 있습니다 : onHiddenChanged(boolean hidden)당신의 조각 내


2
이것은 앱 호환 조각에서 작동하지 않는 것 같습니다.
Lo-Tan

그게 제가 사용한 것입니다. 감사!
Albert Vila Calvo

가장 간단한 해결책! 조각이 현재 표시되고 짜여져 있는지 확인하기 만하면 앱바 제목을 설정할 수 있습니다.
EricH206

4

Android 개발자의 다음 섹션에서는 활동에 대한 이벤트 콜백 생성 통신 메커니즘에 대해 설명합니다 . 한 줄을 인용하려면 :

이를 수행하는 좋은 방법은 프래그먼트 내부에 콜백 인터페이스를 정의하고 호스트 활동이이를 구현하도록 요구하는 것입니다. 활동이 인터페이스를 통해 콜백을 수신하면 필요에 따라 레이아웃의 다른 조각과 정보를 공유 할 수 있습니다.

편집 : 조각에는 onStart(...)조각이 사용자에게 표시 될 때 호출되는가 있습니다. 마찬가지로 onResume(...)가시적이고 활발하게 실행될 때. 이것들은 그들의 활동 대응 물과 연결되어 있습니다. 요컨대 : 사용onResume()


1
사용자에게 표시 될 때마다 활성화되는 Fragment 클래스의 메서드를 찾고 있습니다. FragmentTransaction.add () / replace () 또는 FragmentManager.popBackStack () 때문입니다.
oriharel 2011-06-28

@oriharel 업데이트 된 답변을 참조하십시오. 액티비티가로드되면 onAttach, onCreate, onActivityCreated, onStart, onResume과 같은 다양한 호출을 받게됩니다. `setRetainInstance (true) '를 호출 한 경우 다시 클릭 할 때 조각이 다시 표시 될 때 onCreate가 표시되지 않습니다.
PJL

15
onStart ()는 동일한 활동의 ​​프래그먼트 사이에서 "뒤로"를 클릭 할 때 호출되지 않습니다. 설명서에서 대부분의 콜백을 시도했지만 이러한 시나리오에서는 호출되지 않았습니다. 해결 방법은 내 대답을 참조하십시오.
oriharel 2011-06-28

compat lib v4와 함께 흠 내 조각에 대한 onResume이 표시됩니다. @oriharel의 대답은 포 그라운드로 가져 오는 것보다 popBackStack을 통해 표시되는 것을 구별하기 때문에 좋아합니다.
PJL

3

내 활동 onCreate ()

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

이 메서드를 사용하여 특정 Fragment를 포착하고 onResume ()을 호출합니다.

private FragmentManager.OnBackStackChangedListener getListener()
    {
        FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
                if (currentFragment instanceof YOURFRAGMENT) {
                    currentFragment.onResume();
                }
            }
        };

        return result;
    }

2

약간 개선되어 관리자 솔루션에 포함되었습니다.

명심할 사항. FragmentManager는 싱글 톤이 아니며 Activity 내의 Fragment 만 관리하므로 모든 활동에서 새로운 것이됩니다. 또한 지금까지이 솔루션은 조각의 가시성을 제어하는 ​​데 도움이되는 setUserVisibleHint () 메서드를 호출하는 ViewPager를 고려하지 않았습니다.

이 문제를 처리 할 때 다음 클래스를 자유롭게 사용하십시오 (Dagger2 주입 사용). 활동 요청 :

//inject FragmentBackstackStateManager instance to myFragmentBackstackStateManager
FragmentManager fragmentManager = getSupportFragmentManager(); 
myFragmentBackstackStateManager.apply(fragmentManager);

FragmentBackstackStateManager.java :

@Singleton
public class FragmentBackstackStateManager {

    private FragmentManager fragmentManager;

    @Inject
    public FragmentBackstackStateManager() {
    }

    private BackstackCallback backstackCallbackImpl = new BackstackCallback() {
        @Override
        public void onFragmentPushed(Fragment parentFragment) {
            parentFragment.onPause();
        }

        @Override
        public void onFragmentPopped(Fragment parentFragment) {
            parentFragment.onResume();
        }
    };

    public FragmentBackstackChangeListenerImpl getListener() {
        return new FragmentBackstackChangeListenerImpl(fragmentManager, backstackCallbackImpl);
    }

    public void apply(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
        fragmentManager.addOnBackStackChangedListener(getListener());
    }
}

FragmentBackstackChangeListenerImpl.java :

public class FragmentBackstackChangeListenerImpl implements FragmentManager.OnBackStackChangedListener {

    private int lastBackStackEntryCount = 0;
    private final FragmentManager fragmentManager;
    private final BackstackCallback backstackChangeListener;

    public FragmentBackstackChangeListenerImpl(FragmentManager fragmentManager, BackstackCallback backstackChangeListener) {
        this.fragmentManager = fragmentManager;
        this.backstackChangeListener = backstackChangeListener;
        lastBackStackEntryCount = fragmentManager.getBackStackEntryCount();
    }

    private boolean wasPushed(int backStackEntryCount) {
        return lastBackStackEntryCount < backStackEntryCount;
    }

    private boolean wasPopped(int backStackEntryCount) {
        return lastBackStackEntryCount > backStackEntryCount;
    }

    private boolean haveFragments() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList != null && !fragmentList.isEmpty();
    }


    /**
     * If we push a fragment to backstack then parent would be the one before => size - 2
     * If we pop a fragment from backstack logically it should be the last fragment in the list, but in Android popping a fragment just makes list entry null keeping list size intact, thus it's also size - 2
     *
     * @return fragment that is parent to the one that is pushed to or popped from back stack
     */
    private Fragment getParentFragment() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList.get(Math.max(0, fragmentList.size() - 2));
    }

    @Override
    public void onBackStackChanged() {
        int currentBackStackEntryCount = fragmentManager.getBackStackEntryCount();
        if (haveFragments()) {
            Fragment parentFragment = getParentFragment();

            //will be null if was just popped and was last in the stack
            if (parentFragment != null) {
                if (wasPushed(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPushed(parentFragment);
                } else if (wasPopped(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPopped(parentFragment);
                }
            }
        }

        lastBackStackEntryCount = currentBackStackEntryCount;
    }
}

BackstackCallback.java :

public interface BackstackCallback {
    void onFragmentPushed(Fragment parentFragment);

    void onFragmentPopped(Fragment parentFragment);
}

1

조각이 백 스택에 배치되면 Android는 단순히 뷰를 파괴합니다. 조각 인스턴스 자체는 죽지 않습니다. 시작하는 간단한 방법은 onViewCreated 이벤트를 수신하는 것입니다. 여기에 "onResume ()"로직을 넣습니다.

boolean fragmentAlreadyLoaded = false;
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState == null && !fragmentAlreadyLoaded) {
            fragmentAlreadyLoaded = true;

            // Code placed here will be executed once
        }

        //Code placed here will be executed even when the fragment comes from backstack
    }

0

이것은 조각이 활동에 첨부되어 있으면 onResume ()을 호출 할 수있는 정답입니다. 또는 onAttach 및 onDetach를 사용할 수 있습니다.


1
예제를 게시 할 수 있습니까?
kavie 2014-10-06

0

조각에 대한 onResume ()이 잘 작동합니다 ...

public class listBook extends Fragment {

    private String listbook_last_subtitle;
...

    @Override
       public void onCreate(Bundle savedInstanceState) {

        String thisFragSubtitle = (String) getActivity().getActionBar().getSubtitle();
        listbook_last_subtitle = thisFragSubtitle;
       }
...

    @Override
        public void onResume(){
            super.onResume();
            getActivity().getActionBar().setSubtitle(listbook_last_subtitle);
        }
...

0
public abstract class RootFragment extends Fragment implements OnBackPressListener {

 @Override
 public boolean onBackPressed() {
  return new BackPressImpl(this).onBackPressed();
 }

 public abstract void OnRefreshUI();

}


public class BackPressImpl implements OnBackPressListener {

 private Fragment parentFragment;

 public BackPressImpl(Fragment parentFragment) {
  this.parentFragment = parentFragment;
 }

 @Override
 public boolean onBackPressed() {
  ((RootFragment) parentFragment).OnRefreshUI();
 }
}

효과를보기 위해 RootFragment의 Frament를 마지막으로 확장합니다.


0

내 해결 방법은 새 제목으로 설정하기 전에 조각에서 작업 표시 줄의 현재 제목을 가져 오는 것입니다. 이렇게하면 조각이 터지면 해당 제목으로 다시 변경할 수 있습니다.

@Override
public void onResume() {
    super.onResume();
    // Get/Backup current title
    mTitle = ((ActionBarActivity) getActivity()).getSupportActionBar()
            .getTitle();
    // Set new title
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(R.string.this_fragment_title);
}

@Override
public void onDestroy() {
    // Set title back
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(mTitle);

    super.onDestroy();
}

0

FragmentTags모든 조각 클래스를 정의하기 위해 열거 형 을 사용했습니다.

TAG_FOR_FRAGMENT_A(A.class),
TAG_FOR_FRAGMENT_B(B.class),
TAG_FOR_FRAGMENT_C(C.class)

통과하다 FragmentTags.TAG_FOR_FRAGMENT_A.name()조각 태그로 .

그리고 지금

@Override
public void onBackPressed(){
   FragmentManager fragmentManager = getFragmentManager();
   Fragment current
   = fragmentManager.findFragmentById(R.id.fragment_container);
    FragmentTags fragmentTag = FragmentTags.valueOf(current.getTag());

  switch(fragmentTag){
    case TAG_FOR_FRAGMENT_A:
        finish();
        break;
   case TAG_FOR_FRAGMENT_B:
        fragmentManager.popBackStack();
        break;
   case default: 
   break;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.