최신 정보 :
내 문제를 이전 조각의 인스턴스를 유지하는 fragmentManager와 내 FragmentManager와 동기화되지 않은 뷰 페이저의 문제로 좁혔습니다. 이 문제를 참조하세요 … http://code.google.com/p/android/issues/detail?id=19211#makechanges . 나는 이것을 해결하는 방법을 아직 모른다. 모든 제안 …
나는 이것을 오랫동안 디버깅하려고 시도했으며 어떤 도움이라도 대단히 감사하겠습니다. 다음과 같은 조각 목록을 허용하는 FragmentPagerAdapter를 사용하고 있습니다.
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
...
new PagerAdapter(getSupportFragmentManager(), fragments);
구현은 표준입니다. Fragments에 ActionBarSherlock 및 v4 컴퓨팅 가능성 라이브러리를 사용하고 있습니다.
내 문제는 앱을 종료하고 다른 여러 응용 프로그램을 열고 다시 돌아 오면 조각이 FragmentActivity에 대한 참조를 다시 잃는다는 것입니다 (예 🙂 getActivity() == null
. 왜 이런 일이 일어나는지 알 수 없습니다. 수동으로 설정하려고했지만 setRetainInstance(true);
도움이되지 않습니다. FragmentActivity가 파괴되면 이런 일이 발생한다고 생각했지만 로그 메시지를 받기 전에 앱을 열면 여전히 발생합니다. 아이디어가 있습니까?
@Override
protected void onDestroy(){
Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY");
super.onDestroy();
}
어댑터 :
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
@Override
public int getCount() {
return this.fragments.size();
}
}
내 조각 중 하나가 벗겨졌지만 제거되었지만 여전히 작동하지 않는 모든 것을 칭찬했습니다.
public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener {
...
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
handler = new Handler();
setHasOptionsMenu(true);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH");
context = activity;
if(context== null){
Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL");
}else{
Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT");
}
}
@Override
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.my_fragment,container, false);
return v;
}
@Override
public void onResume(){
super.onResume();
}
private void callService(){
// do not call another service is already running
if(startLoad || !canSet) return;
// set flag
startLoad = true;
canSet = false;
// show the bottom spinner
addFooter();
Intent intent = new Intent(context, MyService.class);
intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver);
context.startService(intent);
}
private ResultReceiver resultReceiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, final Bundle resultData) {
boolean isSet = false;
if(resultData!=null)
if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){
if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){
removeFooter();
startLoad = false;
isSet = true;
}
}
switch(resultCode){
case MyService.STATUS_FINISHED:
stopSpinning();
break;
case SyncService.STATUS_RUNNING:
break;
case SyncService.STATUS_ERROR:
break;
}
}
};
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.activity, menu);
}
@Override
public void onPause(){
super.onPause();
}
public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) {
boolean loadMore = /* maybe add a padding */
firstVisible + visibleCount >= totalCount;
boolean away = firstVisible+ visibleCount <= totalCount - visibleCount;
if(away){
// startLoad can now be set again
canSet = true;
}
if(loadMore)
}
public void onScrollStateChanged(AbsListView arg0, int state) {
switch(state){
case OnScrollListener.SCROLL_STATE_FLING:
adapter.setLoad(false);
lastState = OnScrollListener.SCROLL_STATE_FLING;
break;
case OnScrollListener.SCROLL_STATE_IDLE:
adapter.setLoad(true);
if(lastState == SCROLL_STATE_FLING){
// load the images on screen
}
lastState = OnScrollListener.SCROLL_STATE_IDLE;
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
adapter.setLoad(true);
if(lastState == SCROLL_STATE_FLING){
// load the images on screen
}
lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;
break;
}
}
@Override
public void onDetach(){
super.onDetach();
if(this.adapter!=null)
this.adapter.clearContext();
Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED");
}
public void update(final int id, String name) {
if(name!=null){
getActivity().getSupportActionBar().setTitle(name);
}
}
}
사용자가 다른 조각과 상호 작용하고 getActivity가 null을 반환 할 때 update 메서드가 호출됩니다. 다음은 다른 조각이 호출하는 메서드입니다.
((MyFragment) pagerAdapter.getItem(1)).update(id, name);
앱이 파괴되면 앱을 기본 프래그먼트까지 시작하는 대신 다시 생성하면 앱이 시작된 다음 viewpager가 마지막으로 알려진 페이지로 이동합니다. 이상하게 보이지만 앱이 기본 프래그먼트로로드되어야하지 않나요?
답변
외부에서 조각에 대한 참조를 인스턴스화 및 유지 PagerAdapter.getItem
하고 ViewPager와 독립적으로 해당 참조를 사용하려고하기 때문에 문제가 발생 합니다. Seraph가 말했듯이 특정 시간에 ViewPager에서 조각이 인스턴스화 / 추가되었음을 보장합니다. 이것은 구현 세부 사항으로 간주되어야합니다. ViewPager는 페이지를 지연로드합니다. 기본적으로 현재 페이지 만로드하고 왼쪽 및 오른쪽 페이지 만로드합니다.
앱을 백그라운드에 배치하면 프래그먼트 관리자에 추가 된 프래그먼트가 자동으로 저장됩니다. 앱이 종료 된 경우에도 앱을 다시 실행하면이 정보가 복원됩니다.
이제 몇 페이지 (Fragments A, B 및 C)를 보았다고 가정합니다. 이러한 항목이 조각 관리자에 추가되었음을 알고 있습니다. 를 사용 FragmentPagerAdapter
하고 있지 않기 때문에 FragmentStatePagerAdapter
다른 페이지로 스크롤 할 때 이러한 조각이 계속 추가되지만 잠재적으로 분리됩니다.
그런 다음 응용 프로그램을 백그라운드로 처리하면 종료됩니다. 돌아 오면 Android는 프래그먼트 관리자에 프래그먼트 A, B 및 C가 있었음을 기억하므로이를 다시 생성 한 다음 추가합니다. 그러나 이제 프래그먼트 관리자에 추가 된 것은 활동의 프래그먼트 목록에있는 것이 아닙니다.
FragmentPagerAdapter는 getPosition
해당 특정 페이지 위치에 대해 이미 조각이 추가 된 경우 호출을 시도하지 않습니다 . 실제로 Android에서 다시 만든 조각은 제거되지 않으므로 getPosition
. 그것에 대한 핸들을 얻는 것은 또한 당신에게 알려지지 않은 태그와 함께 추가 되었기 때문에 그것에 대한 참조를 얻기가 매우 어렵습니다. 이것은 의도적으로 설계된 것입니다. 뷰 페이저가 관리하는 조각을 엉망으로 만드는 것은 권장되지 않습니다. 프래그먼트 내에서 모든 작업을 수행하고, 활동과 통신하고, 필요한 경우 특정 페이지로 전환하도록 요청해야합니다.
이제 누락 된 활동에 대한 문제로 돌아갑니다. pagerAdapter.getItem(1)).update(id, name)
이 모든 일이 발생한 후 호출 하면 목록의 조각이 반환되며, 이는 아직 조각 관리자에 추가 되지 않았으므로 활동 참조가 없습니다. 업데이트 방법이 일부 공유 데이터 구조 (활동에 의해 관리 될 수 있음)를 수정 한 다음 특정 페이지로 이동할 때이 업데이트 된 데이터를 기반으로 자신을 그릴 수 있다고 제안합니다.
답변
나는 나를 위해 일한 간단한 해결책을 찾았습니다.
조각 어댑터가 FragmentPagerAdapter 대신 FragmentStatePagerAdapter를 확장하고 onSave 메서드를 재정 의하여 null을 반환하도록합니다.
@Override
public Parcelable saveState()
{
return null;
}
이것은 안드로이드가 조각을 다시 만드는 것을 방지합니다.
어느 날 더 나은 해결책을 찾았습니다.
setRetainInstance(true)
모든 조각을 호출 하고 어딘가에 참조를 저장하십시오. singleTask로 선언되고 조각이 항상 동일하게 유지 될 수 있기 때문에 내 활동의 정적 변수에서 그렇게했습니다.
이런 식으로 안드로이드는 조각을 다시 만들지 않지만 동일한 인스턴스를 사용합니다.
답변
이 문제는 FragmentPagerAdapter 대신 FragmentManager를 통해 직접 내 프래그먼트에 액세스하여 해결했습니다. 먼저 FragmentPagerAdapter에 의해 자동 생성 된 조각의 태그를 알아 내야합니다.
private String getFragmentTag(int pos){
return "android:switcher:"+R.id.viewpager+":"+pos;
}
그런 다음 해당 조각에 대한 참조를 얻고 필요한 작업을 수행합니다.
Fragment f = this.getSupportFragmentManager().findFragmentByTag(getFragmentTag(1));
((MyFragmentInterface) f).update(id, name);
viewPager.setCurrentItem(1, true);
조각 내 setRetainInstance(false);
에서 savedInstanceState 번들에 수동으로 값을 추가 할 수 있도록을 설정했습니다 .
@Override
public void onSaveInstanceState(Bundle outState) {
if(this.my !=null)
outState.putInt("myId", this.my.getId());
super.onSaveInstanceState(outState);
}
그런 다음 OnCreate에서 해당 키를 잡고 필요에 따라 조각의 상태를 복원합니다. 이해하기 어려웠던 (적어도 나를 위해) 쉬운 해결책.
답변
글로벌 작업 테스트 솔루션.
getSupportFragmentManager()
null 참조를 몇 번 유지하고 View pager는 동일한 조각에 대한 참조를 찾기 때문에 새로 만들지 않습니다. 그래서이 사용 getChildFragmentManager()
은 간단한 방법으로 문제를 해결합니다.
이러지 마세요 :
new PagerAdapter(getSupportFragmentManager(), fragments);
이 작업을 수행:
new PagerAdapter(getChildFragmentManager() , fragments);
답변
ViewPager에서 조각간에 상호 작용하지 마십시오. 다른 조각이 연결되었거나 존재한다고 보장 할 수 없습니다. 조각에서 작업 표시 줄 제목을 변경하는 대신 활동에서 수행 할 수 있습니다. 이를 위해 표준 인터페이스 패턴을 사용하십시오.
public interface UpdateCallback
{
void update(String name);
}
public class MyActivity extends FragmentActivity implements UpdateCallback
{
@Override
public void update(String name)
{
getSupportActionBar().setTitle(name);
}
}
public class MyFragment extends Fragment
{
private UpdateCallback callback;
@Override
public void onAttach(SupportActivity activity)
{
super.onAttach(activity);
callback = (UpdateCallback) activity;
}
@Override
public void onDetach()
{
super.onDetach();
callback = null;
}
public void updateActionbar(String name)
{
if(callback != null)
callback.update(name);
}
}
답변
viewpager를 파괴 할 때 조각을 제거 할 수 있습니다. 제 경우에는 onDestroyView()
조각 에서 제거했습니다 .
@Override
public void onDestroyView() {
if (getChildFragmentManager().getFragments() != null) {
for (Fragment fragment : getChildFragmentManager().getFragments()) {
getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
}
}
super.onDestroyView();
}
답변
비슷한 문제를 찾은 후 몇 시간 후에 다른 해결책이 있다고 생각합니다. 이것은 적어도 나를 위해 일했으며 몇 줄만 변경하면됩니다.
이것이 내가 가진 문제입니다. 두 개의 Fragment와 함께 FragmentStatePagerAdapter를 사용하는 뷰 페이저가있는 활동이 있습니다. 활동을 강제로 파괴하거나 (개발자 옵션) 화면을 회전 할 때까지 모든 것이 잘 작동합니다. getItem 메서드 내에서 생성 된 두 조각에 대한 참조를 유지합니다.
이 시점에서 활동이 다시 생성되고이 시점에서 모든 것이 잘 작동하지만 getItem이 다시 호출되지 않으므로 내 fragmetns에 대한 참조가 손실되었습니다.
이것이 FragmentStatePagerAdapter 내에서 해당 문제를 해결 한 방법입니다.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object aux = super.instantiateItem(container, position);
//Update the references to the Fragments we have on the view pager
if(position==0){
fragTabOne = (FragOffersList)aux;
}
else{
fragTabTwo = (FragOffersList) aux;
}
return aux;
}
어댑터에 이미 내부적으로 참조가있는 경우 getItem에 대한 호출이 다시 발생하지 않으며이를 변경해서는 안됩니다. 대신 각 프래그먼트에 대해 호출 될 다른 메서드 인 instantiateItem ()을 살펴보면 사용중인 프래그먼트를 얻을 수 있습니다.
누구에게나 도움이되기를 바랍니다.