UFO ET IT

popBackStack 후 ViewPager의 조각이 복원되지 않음

ufoet 2021. 1. 5. 08:25
반응형

popBackStack 후 ViewPager의 조각이 복원되지 않음


문제

Fragment는 다른 조각에서 반환 된 후 호스팅 ViewPager에 다시 연결되지 않습니다.

상태

레이아웃에 ViewPager가있는 Fragment를 호스팅하는 하나의 활동 ( PageListFragment아래 예 참조). ViewPager는 FragmentStateViewPagerAdapter로 채워집니다. 호출기 내부에서 호스팅되는 단일 프래그먼트 ( PageFragment아래 예)는 새로운 페이지 세트를 포함하는 하위 페이지 목록을 열 수 있습니다.

행동

뒤로 버튼을 누르지 않는 한 모두 정상적으로 작동합니다. 사용자가 하위 페이지 목록 중 하나를 닫으면 이전 목록이 다시 생성되지만 이전에 표시된 페이지는 표시되지 않습니다. 상위 페이지 목록의 다른 페이지를 스 와이프해도 여전히 작동합니다.

암호

샘플 애플리케이션은 github 에서 찾을 수 있습니다 .

활동

public class MainActivity extends FragmentActivity {

private static final String CURRENT_FRAGMENT = MainActivity.class.getCanonicalName() + ".CURRENT_FRAGMENT";

public static final String ARG_PARENTS = "Parents";

public void goInto(String mHostingLevel, String mPosition) {
    Fragment hostingFragment = newHostingFragment(mHostingLevel, mPosition);
    addFragment(hostingFragment);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    addBaseFragment();
}

private void addBaseFragment() {
    Fragment hostingFragment = newHostingFragment("", "");
    addFragment(hostingFragment);
}

private Fragment newHostingFragment(String mHostingLevel, String oldPosition) {
    Fragment hostingFragment = new PageListFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARENTS, mHostingLevel + oldPosition +" > ");
    hostingFragment.setArguments(args);
    return hostingFragment;
}

private void addFragment(Fragment hostingFragment) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.fragmentSpace, hostingFragment, CURRENT_FRAGMENT);
    transaction.addToBackStack(null);
    transaction.commit();
}

}

PageListFragment

public class PageListFragment extends Fragment {

private String mParentString;

public PageListFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_hosting, container, false);
}

@Override
public void onResume() {
    mParentString = getArguments().getString(MainActivity.ARG_PARENTS);
    ViewPager viewPager = (ViewPager) getView().findViewById(R.id.viewPager);
    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentString));
    super.onResume();
}

private static class SimpleFragmentStatePagerAdapter extends FragmentStatePagerAdapter {

    private String mHostingLevel;

    public SimpleFragmentStatePagerAdapter(FragmentManager fm, String hostingLevel) {
        super(fm);
        this.mHostingLevel = hostingLevel;
    }

    @Override
    public android.support.v4.app.Fragment getItem(int position) {
        PageFragment pageFragment = new PageFragment();
        Bundle args = new Bundle();
        args.putString(MainActivity.ARG_PARENTS, mHostingLevel);
        args.putInt(PageFragment.ARG_POSITION, position);
        pageFragment.setArguments(args);
        return pageFragment;
    }

    @Override
    public int getCount() {
        return 5;
    }
}
}

PageFragment

public class PageFragment extends Fragment {

public static final String ARG_POSITION = "Position";

private String mHostingLevel;
private int mPosition;

public PageFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View contentView = inflater.inflate(R.layout.fragment_page, container, false);
    setupTextView(contentView);
    setupButton(contentView);
    return contentView;
}

private void setupTextView(View contentView) {
    mPosition = getArguments().getInt(ARG_POSITION);
    mHostingLevel = getArguments().getString(MainActivity.ARG_PARENTS);
    TextView text = (TextView) contentView.findViewById(R.id.textView);
    text.setText("Parent Fragments " + mHostingLevel + " \n\nCurrent Fragment "+ mPosition);
}

private void setupButton(View contentView) {
    Button button = (Button) contentView.findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            openNewLevel();
        }
    });
}

protected void openNewLevel() {
    MainActivity activity = (MainActivity) getActivity();
    activity.goInto(mHostingLevel, Integer.toString(mPosition));
}

}

오랜 조사 끝에 조각 관리자의 문제로 판명되었습니다.

조각 트랜잭션 위에있는 것과 같은 구조를 사용하여 조각을 페이지 목록에 다시 연결하면 자동으로 삭제됩니다. 기본적으로 원인이되는 동일한 문제입니다

java.lang.IllegalStateException: Recursive entry to executePendingTransactions 

FragmentPager 내부의 조각을 변경하려고 할 때.

이 오류의 문제와 동일한 솔루션이 여기에도 적용됩니다. FragmentStatePagerAdapter를 구성 할 때 올바른 자식 조각 관리자를 제공합니다.

대신에

    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentString));

하다

    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getChildFragmentManager(),mParentString));

참조 : github


Paul이 언급하지 않은 것은 getChildFragmentManager 를 사용 하면 "뒤로 누를 때 빈 화면"문제가 발생한다는 것입니다.


내 경우 계층 구조는 다음과 같습니다.

MainActivity-> MainFragment-> TabLayout+ ViewPager-> AccountsFragment+ SavingsFragment+ InvestmentsFragment

내가 가진 문제는 내가 사용할 수 없다고했다 childFragmentManager항목에 클릭하는 이유 Account view(의 내부에 하나의 존재 Fragment의의 ViewPager필요를) 대체 할 MainFragment예를 전체 화면을.

여기에 이미지 설명 입력

사용 MainFragment의 호스트 Fragment즉, 통과는 getFragmentManager()일 교체,하지만 백 스택 터지는 때, 나는이 화면으로 돌아가 셨습니다을 사용할 수 :

여기에 이미지 설명 입력

이것은 ViewPager비어 있는 레이아웃 검사기를 보면 알 수 있습니다 .

여기에 이미지 설명 입력

복원 된 Fragments를 보면 View복원되었지만 팝된 상태의 계층 구조와 일치하지 않는 것을 알 수 있습니다. 최소한의 영향을 미치고 강제로 다시 작성하지 않기 위해 다음 변경 사항으로 Fragment다시 작성 FragmentStatePagerAdapter했습니다.

전체 코드를 복사 FragmentStatePagerAdapter하고 변경했습니다.

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
    // If we already have this item instantiated, there is nothing
    // to do.  This can happen when we are restoring the entire pager
    // from its saved state, where the fragment manager has already
    // taken care of restoring the fragments we previously had instantiated.
    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }
...
}

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
    // If we already have this item instantiated, there is nothing
    // to do.  This can happen when we are restoring the entire pager
    // from its saved state, where the fragment manager has already
    // taken care of restoring the fragments we previously had instantiated.
    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }

            mCurTransaction.detach(f);
            mCurTransaction.attach(f);

            return f;
        }
    }
...
}

이렇게하면 복원 된 파일 Fragment이에 다시 연결 되었는지 효과적으로 확인할 수 ViewPager있습니다.


모든 페이지 조각을 삭제하여 나중에 다시 추가 할 수 있습니다.

페이지 조각은 FragmentStatePagerAdapter가 다시 연결하지 않기 때문에 뷰 페이저 화면으로 돌아갈 때 연결되지 않습니다. 해결 방법으로 popbackstack ()이 호출 된 후 viewpager에서 모든 조각을 삭제하면 초기 코드에서 다시 추가 할 수 있습니다.

[이 예제는 Kotlin으로 작성되었습니다.]

//Clear all fragments from the adapter before they are re-added.
for (i: Int in 0 until adapter.count) {
    val item = childFragmentManager.findFragmentByTag("f$i")
    if (item != null) {
        adapter.destroyItem(container!!, i, item)
    }
}

참조 URL : https://stackoverflow.com/questions/16600156/fragment-in-viewpager-not-restored-after-popbackstack

반응형