두 조각 사이를 전환하는 더미 활동을 작성했습니다. FragmentA에서 FragmentB로 이동하면 FragmentA가 백 스택에 추가됩니다. 그러나 FragmentA로 돌아 가면 (뒤로 누름) 완전히 새로운 FragmentA가 만들어지고 상태가 손실됩니다. 이 질문 과 같은 것을 겪고 있다는 느낌 이 들지만 문제를 근절하기 위해 완전한 코드 샘플을 포함 시켰습니다.
public class FooActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentA());
transaction.commit();
}
public void nextFragment() {
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentB());
transaction.addToBackStack(null);
transaction.commit();
}
public static class FragmentA extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View main = inflater.inflate(R.layout.main, container, false);
main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
((FooActivity) getActivity()).nextFragment();
}
});
return main;
}
@Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save some state!
}
}
public static class FragmentB extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.b, container, false);
}
}
}
일부 로그 메시지가 추가 된 경우 :
07-05 14:28:59.722 D/OMG ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG ( 1260): FragmentA.onCreateView
FragmentA.onSaveInstanceState를 호출하지 않으며 되돌릴 때 새 FragmentA를 만듭니다. 그러나 FragmentA를 사용 중이고 화면을 잠그면 FragmentA.onSaveInstanceState가 호출됩니다. 너무 이상합니다 … 다시 생성 할 필요가 없도록 백 스택에 조각을 추가 할 것으로 잘못 생각합니까? 문서에서 말하는 내용은 다음과 같습니다 .
조각을 제거 할 때 addToBackStack ()을 호출하면 조각이 중지되고 사용자가 다시 탐색하면 조각이 다시 시작됩니다.
답변
당신이 다시 스택에서 단편으로 돌아 경우는 조각을 다시 만듭니다 것이 아니라 함께 같은 인스턴스 시작 재 – 사용 onCreateView()
조각 라이프 사이클에서 볼 조각 수명주기를 .
따라서 상태를 저장하려면 인스턴스 변수를 사용해야하며 의존 하지 않아야 합니다 onSaveInstanceState()
.
답변
Apple UINavigationController
과 및에 비해 UIViewController
Google은 Android 소프트웨어 아키텍처에서 잘하지 않습니다. 그리고 안드로이드의 문서 Fragment
는별로 도움이되지 않습니다.
FragmentA에서 FragmentB를 입력하면 기존 FragmentA 인스턴스가 삭제되지 않습니다. FragmentB에서 뒤로를 누르고 FragmentA로 돌아 가면 새 FragmentA 인스턴스가 생성되지 않습니다. 기존 FragmentA 인스턴스 onCreateView()
가 호출됩니다.
핵심 onCreateView()
은 기존 FragmentA의 인스턴스를 사용하고 있기 때문에 FragmentA의 뷰를 다시 팽창시키지 않아야한다는 것입니다 . rootView를 저장하고 재사용해야합니다.
다음 코드는 잘 작동합니다. 조각 상태를 유지할뿐만 아니라 RAM과 CPU 부하도 줄입니다 (필요한 경우 레이아웃 만 팽창시키기 때문). 나는 구글의 샘플 코드와 문서가 그것을 언급하지는 않지만 항상 레이아웃을 팽창 시킨다고 믿을 수 없다 .
버전 1 (버전 1을 사용하지 마십시오. 버전 2를 사용하십시오)
public class FragmentA extends Fragment {
View _rootView;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (_rootView == null) {
// Inflate the layout for this fragment
_rootView = inflater.inflate(R.layout.fragment_a, container, false);
// Find and setup subviews
_listView = (ListView)_rootView.findViewById(R.id.listView);
...
} else {
// Do not inflate the layout again.
// The returned View of onCreateView will be added into the fragment.
// However it is not allowed to be added twice even if the parent is same.
// So we must remove _rootView from the existing parent view group
// (it will be added back).
((ViewGroup)_rootView.getParent()).removeView(_rootView);
}
return _rootView;
}
}
—— 2005 년 5 월 3 일 업데이트 : ——-
언급 한 바와 같이 때때로 _rootView.getParent()
에서 null onCreateView
이 발생하여 충돌이 발생합니다. dell116이 제안한대로 버전 2는 onDestroyView ()에서 _rootView를 제거합니다. 안드로이드 4.0.3, 4.4.4, 5.1.0에서 테스트되었습니다.
버전 2
public class FragmentA extends Fragment {
View _rootView;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (_rootView == null) {
// Inflate the layout for this fragment
_rootView = inflater.inflate(R.layout.fragment_a, container, false);
// Find and setup subviews
_listView = (ListView)_rootView.findViewById(R.id.listView);
...
} else {
// Do not inflate the layout again.
// The returned View of onCreateView will be added into the fragment.
// However it is not allowed to be added twice even if the parent is same.
// So we must remove _rootView from the existing parent view group
// in onDestroyView() (it will be added back).
}
return _rootView;
}
@Override
public void onDestroyView() {
if (_rootView.getParent() != null) {
((ViewGroup)_rootView.getParent()).removeView(_rootView);
}
super.onDestroyView();
}
}
경고!!!
이것은 해킹입니다! 내 앱에서 사용하고 있지만 주석을주의 깊게 테스트하고 읽어야합니다.
답변
당신이 찾고있는 것을 성취 할 수있는 다른 방법이 있다고 생각합니다. 나는 완전한 해결책을 말하지는 않지만 제 경우에는 목적을 달성했습니다.
내가 한 것은 방금 대상 조각을 추가 한 조각을 바꾸는 것입니다. 따라서 기본적으로 add()
method 를 사용하게 됩니다 replace()
.
내가 뭘했는지 현재 조각을 숨기고 백 스택에 추가합니다.
따라서 뷰를 손상시키지 않고 현재 프래그먼트보다 새로운 프래그먼트와 겹칩니다 ( onDestroyView()
메서드가 호출되지 않는지 확인하십시오 . 플러그를 추가 backstate
하면 프래그먼트를 다시 시작할 수 있다는 이점이 있습니다.
코드는 다음과 같습니다.
Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();
AFAIK 시스템 onCreateView()
은 뷰가 파괴되거나 생성되지 않은 경우 에만 호출 합니다. 그러나 여기서는 메모리에서 뷰를 제거하지 않고 뷰를 저장했습니다. 따라서 새로운보기를 만들지 않습니다.
그리고 당신이 Destination Fragment에서 돌아 왔을 때 가장 최근에 FragmentTransaction
제거 된 Top Fragment가 나타나서 최상위 (SourceFragment) 뷰가 화면 위에 나타납니다.
주석 : 내가 말했듯이 소스 조각의 관점을 제거하지 않으므로 평소보다 더 많은 메모리를 차지하므로 완벽한 솔루션이 아닙니다. 그러나 여전히 목적을 달성하십시오. 또한 기존 방식이 아닌 뷰를 대체하는 대신 완전히 다른 뷰 숨기기 메커니즘을 사용하고 있습니다.
따라서 실제로 상태를 유지 관리하는 방법이 아니라 뷰를 유지 관리하는 방법입니다.
답변
매우 간단한 해결책을 제안합니다.
View 참조 변수를 가져 와서 OnCreateView에서보기를 설정하십시오. 이 변수에 뷰가 이미 있는지 확인한 다음 동일한 뷰를 반환하십시오.
private View fragmentView;
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (fragmentView != null) {
return fragmentView;
}
View view = inflater.inflate(R.layout.yourfragment, container, false);
fragmentView = view;
return view;
}
답변
저장 / 다시로드 할 설정 정보가 너무 많은 맵이 포함 된 조각에서이 문제를 발견했습니다. 내 해결책은 기본적 으로이 조각을 항상 활성 상태로 유지하는 것입니다 (@kaushal이 언급 한 것과 유사).
현재 단편 A가 있고 단편 B를 표시하려고한다고 가정하십시오. 결과 요약 :
- replace ()-Fragment A를 제거하고 Fragment B로 교체합니다. Fragment A는 다시 전면으로 가져온 후 다시 생성됩니다.
- add ()-(생성 및) Fragment B를 추가하면 Fragment A와 겹쳐지며 백그라운드에서 여전히 활성화됩니다.
- remove ()-조각 B를 제거하고 A로 돌아 오는 데 사용할 수 있습니다. 조각 B는 나중에 호출 될 때 다시 작성됩니다.
따라서 두 조각을 모두 “저장 한”상태로 유지하려면 hide () / show ()를 사용하여 조각을 전환하십시오.
장점 : 여러 조각을 계속 실행하는 쉽고 간단한 방법
단점 : 모든 메모리를 계속 사용하려면 더 많은 메모리를 사용하십시오. 많은 큰 비트 맵 표시와 같은 문제가 발생할 수 있음
답변
onSaveInstanceState()
구성 변경이있는 경우에만 호출됩니다.
한 프래그먼트에서 다른 프래그먼트로 변경하기 때문에 구성 변경이 없으므로 호출 onSaveInstanceState()
이 없습니다. 어떤 상태가 저장되지 않습니까? 지정할 수 있습니까?
EditText에 텍스트를 입력하면 자동으로 저장됩니다. ID가없는 UI 항목은보기 상태가 저장되지 않는 항목입니다.
답변
onSaveInstanceState
백 스택에 프래그먼트를 추가하면 프래그먼트가 호출되지 않기 때문에 여기에서 . 가기 backstack의 단편 라이프 사이클 복원을 시작할 때 onCreateView
와 끝 onDestroyView
동안은 onSaveInstanceState
사이라고 onDestroyView
하고 onDestroy
. 내 솔루션은 인스턴스 변수를 만들고 init입니다 onCreate
. 샘플 코드 :
private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
isDataLoading = false;
// init list at once when create fragment
listData = new ArrayList();
}
그리고 그것을 확인하십시오 onActivityCreated
:
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if(isDataLoading){
fetchData();
}else{
//get saved instance variable listData()
}
}
private void fetchData(){
// do fetch data into listData
}