FragmentPagerAdapter 지원은 이전 조각에 대한 참조를 보유합니다. public Fragment

최신 정보 :

내 문제를 이전 조각의 인스턴스를 유지하는 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 ()을 살펴보면 사용중인 프래그먼트를 얻을 수 있습니다.

누구에게나 도움이되기를 바랍니다.