Android 4.2에서 지원 라이브러리는 중첩 된 조각을 지원합니다 . 여기를 참조하세요 . 나는 그것을 가지고 놀았고 백 스택 및 getChildFragmentManager () 에 관한 흥미로운 동작 / 버그를 발견했습니다 . getChildFragmentManager () 및 addToBackStack (String name)을 사용할 때 뒤로 버튼을 누르면 시스템이 이전 조각으로 백 스택을 실행하지 않습니다. 반면에 getFragmentManager () 및 addToBackStack (String name)을 사용하는 경우 뒤로 버튼을 누르면 시스템이 이전 조각으로 돌아갑니다.
저에게는이 동작이 예상치 못한 일입니다. 내 장치의 뒤로 버튼을 누르면 마지막으로 추가 된 조각이 자식 조각 관리자의 백 스택에 추가 된 경우에도 백 스택에 마지막으로 추가 된 조각이 팝업 될 것으로 예상됩니다.
이 동작이 맞습니까? 이 동작이 버그입니까? 이 문제에 대한 해결 방법이 있습니까?
getChildFragmentManager ()를 사용한 샘플 코드 :
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
getFragmentManager ()를 사용한 샘플 코드 :
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
답변
이 솔루션은 @Sean 답변의 더 나은 버전 일 수 있습니다.
@Override
public void onBackPressed() {
// if there is a fragment and the back stack of this fragment is not empty,
// then emulate 'onBackPressed' behaviour, because in default, it is not working
FragmentManager fm = getSupportFragmentManager();
for (Fragment frag : fm.getFragments()) {
if (frag.isVisible()) {
FragmentManager childFm = frag.getChildFragmentManager();
if (childFm.getBackStackEntryCount() > 0) {
childFm.popBackStack();
return;
}
}
}
super.onBackPressed();
}
다시 한 번 위의 @Sean 답변을 기반으로이 솔루션을 준비했습니다.
@ AZ13이 말했듯이이 솔루션은 한 수준의 자식 조각 상황에서만 가능합니다. 여러 단계의 조각의 경우 작업이 약간 복잡해 지므로 제가 말한 가능한 경우에만이 솔루션을 시도하는 것이 좋습니다. =)
주 : 이후 getFragments
메소드는 현재 개인 방법이다,이 솔루션이 작동하지 않습니다. 이 상황에 대한 해결책을 제안하는 링크에 대한 설명을 확인할 수 있습니다.
답변
버그처럼 보입니다. http://code.google.com/p/android/issues/detail?id=40323을 살펴보세요
.
해결 방법을 위해 성공적으로 사용했습니다 (의견에서 제안한대로) :
@Override
public void onBackPressed() {
// If the fragment exists and has some back-stack entry
if (mActivityDirectFragment != null && mActivityDirectFragment.getChildFragmentManager().getBackStackEntryCount() > 0){
// Get the fragment fragment manager - and pop the backstack
mActivityDirectFragment.getChildFragmentManager().popBackStack();
}
// Else, nothing in the direct fragment back stack
else{
// Let super handle the back press
super.onBackPressed();
}
}
답변
이 질문에 대한 실제 답은 setPrimaryNavigationFragment라는 Fragment Transaction의 함수에 있습니다.
/**
* Set a currently active fragment in this FragmentManager as the primary navigation fragment.
*
* <p>The primary navigation fragment's
* {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first
* to process delegated navigation actions such as {@link FragmentManager#popBackStack()}
* if no ID or transaction name is provided to pop to. Navigation operations outside of the
* fragment system may choose to delegate those actions to the primary navigation fragment
* as returned by {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
*
* <p>The fragment provided must currently be added to the FragmentManager to be set as
* a primary navigation fragment, or previously added as part of this transaction.</p>
*
* @param fragment the fragment to set as the primary navigation fragment
* @return the same FragmentTransaction instance
*/
public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment);
활동이 추가 할 때 초기 상위 프래그먼트에이 함수를 설정해야합니다. 내 활동 내에 다음과 같은 replaceFragment 함수가 있습니다.
public void replaceFragment(int containerId, BaseFragment fragment, boolean addToBackstack) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setPrimaryNavigationFragment(fragment);
if (addToBackstack) {
fragmentTransaction.addToBackStack(fragment.TAG);
}
fragmentTransaction.replace(containerId, fragment).commit();
}
이렇게하면 일반 Fragment B에서 Fragment A로 다시 클릭하는 것과 동일한 동작을 제공합니다. 단, 이제 하위 조각에도 적용됩니다!
답변
이 솔루션은 @ismailarilik 답변의 더 나은 버전 일 수 있습니다.
중첩 된 조각 버전
private boolean onBackPressed(FragmentManager fm) {
if (fm != null) {
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return true;
}
List<Fragment> fragList = fm.getFragments();
if (fragList != null && fragList.size() > 0) {
for (Fragment frag : fragList) {
if (frag == null) {
continue;
}
if (frag.isVisible()) {
if (onBackPressed(frag.getChildFragmentManager())) {
return true;
}
}
}
}
}
return false;
}
@Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (onBackPressed(fm)) {
return;
}
super.onBackPressed();
}
답변
이 답변을 사용하면 재귀 적 백 검사를 처리하고 각 조각에 기본 동작을 재정의 할 수있는 기회를 제공합니다. 이것은 ViewPager를 호스팅하는 프래그먼트가 백 스택으로 페이지로 스크롤하거나 홈 페이지로 스크롤 한 다음 다음 백 프레스 종료와 같은 특별한 작업을 수행하도록 할 수 있음을 의미합니다.
AppCompatActivity를 확장하는 활동에 이것을 추가하십시오.
@Override
public void onBackPressed()
{
if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
super.onBackPressed();
}
}
이것을 BaseFragment 또는 모든 조각이 상속받을 수있는 클래스에 추가하십시오.
public static boolean handleBackPressed(FragmentManager fm)
{
if(fm.getFragments() != null){
for(Fragment frag : fm.getFragments()){
if(frag != null && frag.isVisible() && frag instanceof BaseFragment){
if(((BaseFragment)frag).onBackPressed()){
return true;
}
}
}
}
return false;
}
protected boolean onBackPressed()
{
FragmentManager fm = getChildFragmentManager();
if(handleBackPressed(fm)){
return true;
}
else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){
fm.popBackStack();
return true;
}
return false;
}
답변
이 코드는 프래그먼트 관리자 트리를 탐색하고 스택에서 튀어 나올 수있는 프래그먼트가있는 마지막 추가 된 것을 반환합니다.
private FragmentManager getLastFragmentManagerWithBack(FragmentManager fm)
{
FragmentManager fmLast = fm;
List<Fragment> fragments = fm.getFragments();
for (Fragment f : fragments)
{
if ((f.getChildFragmentManager() != null) && (f.getChildFragmentManager().getBackStackEntryCount() > 0))
{
fmLast = f.getFragmentManager();
FragmentManager fmChild = getLastFragmentManagerWithBack(f.getChildFragmentManager());
if (fmChild != fmLast)
fmLast = fmChild;
}
}
return fmLast;
}
메소드를 호출하십시오.
@Override
public void onBackPressed()
{
FragmentManager fm = getLastFragmentManagerWithBack(getSupportFragmentManager());
if (fm.getBackStackEntryCount() > 0)
{
fm.popBackStack();
return;
}
super.onBackPressed();
}
답변
그 이유는 액티비티가 BACK 키 누름을 처리하는 FragmentActivity에서 파생되기 때문입니다 ( FragmentActivity의 173 행 참조) .
우리 응용 프로그램에서는 ViewPager (조각 포함)를 사용하고 있으며 각 조각에는 중첩 조각이있을 수 있습니다. 내가 이것을 처리 한 방법은 다음과 같습니다.
- 단일 메소드로 인터페이스 OnBackKeyPressedListener 정의
void onBackKeyPressed()
- ViewPager가 보여주는 “상위”조각에이 인터페이스를 구현했습니다.
- onKeyDown을 재정의하고 BACK 누르기를 감지하고 뷰 페이저의 현재 활성 프래그먼트에서 onBackKeyPressed를 호출합니다.
또한 getChildFragmentManager()
프래그먼트를 적절하게 중첩하기 위해 프래그먼트에서 사용 하고 있습니다. 이 android-developers 게시물 에서 토론과 설명을 볼 수 있습니다 .