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


109

최신 정보 :

내 문제를 이전 조각의 인스턴스를 유지하는 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가 마지막으로 알려진 페이지로 이동합니다. 이상하게 보이지만 앱이 기본 프래그먼트로로드되어야하지 않나요?


27
Android의 Fragment는 짜증납니다!
Hamidreza Sadegh

13
God I love your log messages : D
oli.G

답변:


120

외부에서 조각에 대한 참조를 인스턴스화 및 유지 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)이 모든 일이 발생한 후 호출 하면 목록의 조각이 반환되며, 이는 아직 조각 관리자에 추가 되지 않았으므로 활동 참조가 없습니다. 업데이트 방법이 일부 공유 데이터 구조 (활동에 의해 관리 될 수 있음)를 수정 한 다음 특정 페이지로 이동할 때이 업데이트 된 데이터를 기반으로 자신을 그릴 수 있다고 제안합니다.


1
나는 당신의 솔루션이 매우 우아하고 코드를 리팩토링 할 것이기 때문에 당신의 솔루션이 마음에 들지만, 당신이 말했듯이 조각 내에 논리와 데이터를 100 % 유지하고 싶었습니다. 솔루션은 FragmentActivity의 모든 데이터를 유지하고 각 Fragment를 사용하여 단순히 디스플레이 논리를 처리해야합니다. 내가 이것을 선호한다고 말했듯이 조각 간의 상호 작용이 매우 무겁고 관리하기가 귀찮을 수 있습니다. 어쨌든 자세한 설명 감사합니다. 당신은 내가 할 수있는 것보다 훨씬 더 잘 설명했습니다.
Maurycy

28
짧게 : 어댑터 외부의 단편에 대한 참조를 보유하지 마십시오
passsy

2
그렇다면 FragmentPagerAdapter를 인스턴스화하지 않은 경우 Activity 인스턴스가 FragmentPagerAdapter 인스턴스에 어떻게 액세스합니까? 미래 인스턴스는 FragmentPagerAdapter와 모든 조각 인스턴스를 다시 인스턴스화하지 않습니까? FragmentPagerAdapter는 모든 조각 인터페이스를 구현하여 조각 간의 통신을 관리해야합니까?
Eric H.

"손잡이를 얻는 것은 또한 당신에게 알려지지 않은 태그와 함께 추가 되었기 때문에 그것에 대한 참조를 얻는 것은 꽤 어렵습니다. 이것은 의도적으로 설계된 것입니다; 당신은 뷰 페이저가 관리하고있는 조각들을 엉망으로 만들지 않을 것입니다. . " 거짓입니다. 호출하여 참조를 얻기 위해 매우 간단 instantiateItem하고 실제로 해야 그렇게 할 onCreate활동의. 자세한 내용은 여기를 참조하십시오 : stackoverflow.com/questions/14035090/…
morgwai

일반적으로 "파괴 및 재생성"시나리오에서는 실제로 재생성 및 표시되는 조각에 대해 별도의 조각을 처리하게 될 수 있으므로로드하지 않고 조각을 직접 인스턴스화하는 것은
어리석은

108

나는 나를 위해 일한 간단한 해결책을 찾았습니다.

조각 어댑터가 FragmentPagerAdapter 대신 FragmentStatePagerAdapter를 확장하고 onSave 메서드를 재정 의하여 null을 반환하도록합니다.

@Override
public Parcelable saveState()
{
    return null;
}

이것은 안드로이드가 조각을 다시 만드는 것을 방지합니다.


어느 날 더 나은 해결책을 찾았습니다.

setRetainInstance(true)모든 조각을 호출 하고 어딘가에 참조를 저장하십시오. singleTask로 선언되고 조각이 항상 동일하게 유지 될 수 있기 때문에 내 활동의 정적 변수에서 그렇게했습니다.

이런 식으로 안드로이드는 조각을 다시 만들지 않지만 동일한 인스턴스를 사용합니다.


4
감사, 감사, 감사합니다 그래서 많이, 난 정말이 지난 10 일부터이 문제를 쫓고 시도했다, 당신에게 미케 감사 할 단어가없는이 네 개의 마법의 라인 내 인생 :) 저장 methods.But 많은

7
프래그먼트 및 / 또는 활동에 대한 정적 참조를 갖는 것은 메모리 누수를 매우 쉽게 유발할 수 있기 때문에 매우 위험한 일입니다. 물론 조심한다면 더 이상 필요하지 않을 때 null로 설정하여 매우 쉽게 처리 할 수 ​​있습니다.
안드로이드 개발자

이것은 나를 위해 일했습니다. 프래그먼트와 해당 뷰는 앱이 충돌하고 재부팅 된 후에도 링크를 유지합니다. 감사!
swebal

내가 사용하고 setRetainInstance(true)FragmentPagerAdapter. 모두 잘 작동합니다. 그러나 장치를 회전 할 때 어댑터에는 여전히 조각이 있지만 조각은 표시되지 않습니다. 조각의 수명주기 메서드도 호출되지 않습니다. 누구든지 도울 수 있습니까?
Jonas

1
당신은 내 하루를 구했습니다! 지금까지 3 일 동안이 버그를 검색했습니다. SingleTask 활동 내부에 2 개의 조각이 있고 "Dont keep Activities"플래그가 활성화 된 Viewpager가 있습니다. 고마워 !!!!
매트릭스

29

이 문제는 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에서 해당 키를 잡고 필요에 따라 조각의 상태를 복원합니다. 이해하기 어려웠던 (적어도 나를 위해) 쉬운 해결책.


비슷한 문제가 있지만 설명을 잘 이해하지 못합니다. 자세한 내용을 제공 할 수 있습니까? 나는 또한 조각을 목록에 저장하는 어댑터를 가지고 있지만 최근 앱에서 앱을 다시 시작할 때 일부 분리 된 조각이 있기 때문에 앱이 충돌합니다. 문제는 여기 stackoverflow.com/questions/11631408/...
게오르기 Gobozov

3
참조하는 조각에서 '내'는 무엇입니까?
조쉬

우리 모두는이 솔루션이 좋은 방법이 아니라는 것을 알고 있지만 FragmentByTagViewPager 에서 사용하는 가장 쉬운 방법 입니다.
Youngjae

다음은 태그를 할당하는 내부 방법과의 호환성에 의존하지 않고 조각에 액세스하는 방법입니다. stackoverflow.com/questions/14035090/…
morgwai

26

글로벌 작업 테스트 솔루션.

getSupportFragmentManager()null 참조를 몇 번 유지하고 View pager는 동일한 조각에 대한 참조를 찾기 때문에 새로 만들지 않습니다. 그래서이 사용 getChildFragmentManager()은 간단한 방법으로 문제를 해결합니다.

이러지 마세요 :

new PagerAdapter(getSupportFragmentManager(), fragments);

이 작업을 수행:

new PagerAdapter(getChildFragmentManager() , fragments);


6
이것은 활동 내부가 아닌 Fragment 내부에서 pagerAdapter를 호스팅 (및 인스턴스화)하는 경우에만 가능합니다. 이 경우에는 기본적으로 childFragmentManager를 사용해야합니다. SupportFragmentManager를 사용하면 기본적으로 잘못된 것
Klitos G.

매우 정확합니다. 해킹을 사용하지 않고 내 문제를 해결했습니다. 감사! 내 Kotlin 버전이 FragmentStatePagerAdapter(activity!!.supportFragmentManager)보기 쉽게 변경되었습니다. FragmentStatePagerAdapter(childFragmentManager):)
ecth

7

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);
    }
}

물론 지금 코드를 포함 할 것입니다 ... 이미 해당 메서드를 로깅하고 있습니다. onDetach는 fragmentActivity에서 onStop이 호출 될 때 호출됩니다. onAttach는 FragmentActivity의 onCreate 직전에 호출됩니다.
Maurycy 2012 년

흠 감사하지만 문제는 여전히 지속됩니다. 대신 이름을 콜백으로 설정했습니다. 조각에 논리를 넣을 수 없습니까? 내 조각이 활동에 대한 참조가 필요한 서비스 및 기타 요소를 실행합니다. 내가 겪고있는 문제를 피하기 위해 모든 애플리케이션 로직을 주요 활동으로 이동해야합니다. 이것은 약간 불필요한 것 같습니다.
Maurycy 2012 년

좋아 그래서 나는 이것을 완전히 테스트하고 제목을 변경하는 콜백을 제외하고는 모든 코드를 주석 처리했습니다. 앱이 처음 실행되고 해당 화면으로 이동하면 제목 이름이 np로 변경됩니다. 여러 응용 프로그램을로드 한 후 다시 돌아 오면 제목이 더 이상 변경되지 않습니다. 콜백이 이전 활동 인스턴스에서 수신되는 것 같습니다. 나는 다른 설명이 생각하지 못할
Maurycy

나는 이것을 fragmentManager 및이 문제의 문제로 세분화했습니다 ... code.google.com/p/android/issues/detail?id=19211 . 나는 아직도이 문제를 해결하는 방법을 단서가 없다
Maurycy

5

viewpager를 파괴 할 때 조각을 제거 할 수 있습니다. 제 경우에는 onDestroyView()조각 에서 제거했습니다 .

@Override
public void onDestroyView() {

    if (getChildFragmentManager().getFragments() != null) {
        for (Fragment fragment : getChildFragmentManager().getFragments()) {
            getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
        }
    }

    super.onDestroyView();
}

감사합니다. 솔루션이 작동합니다. 어댑터를 ViewPager기반으로하는 것도 필요합니다 childFragmentManager(아님 fragmentManager). 또한 다른 변형이 작동합니다. 사용하지 않고 어댑터를 만들기 onDestroyView전에 자식 조각을 제거 ViewPager합니다.
CoolMind

4

비슷한 문제를 찾은 후 몇 시간 후에 다른 해결책이 있다고 생각합니다. 이것은 적어도 나를 위해 일했으며 몇 줄만 변경하면됩니다.

이것이 내가 가진 문제입니다. 두 개의 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 ()을 살펴보면 사용중인 프래그먼트를 얻을 수 있습니다.

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


조각이 많으면 어떨까요? 각각에 대한 참조를 저장 하시겠습니까? 기억력에 좋지 않습니까?
안드로이드 개발자

그것은 모두 당신이 정확히 달성하려는 것에 달려 있습니다. 일반적으로 이러한 뷰 페이저에서는 한 번에 3 개 이상의 조각을 보유하지 않습니다. 중간에 하나와 양쪽에 하나. 내가 원하는 것을 위해서는 조각에 대한 참조가 필요하지만 두 개만 처리하면된다는 것을 알고 있습니다.
Lancelot 2014

0

FragmentManager가 onResume () 메서드가 호출 되 자마자 Fragment를 복원하는 작업을 처리하므로 조각이 활동에 호출되고 목록에 추가됩니다. 제 경우에는이 모든 것을 PagerAdapter 구현에 저장하고 있습니다. 각 조각은 생성시 조각 인수에 추가되기 때문에 위치를 알고 있습니다. 이제 특정 인덱스에서 조각을 조작해야 할 때마다 어댑터의 목록을 사용하기 만하면됩니다.

다음은 초점으로 이동함에 따라 조각이 커지고 초점이 맞지 않으면 축소되는 사용자 정의 ViewPager 용 어댑터의 예입니다. Adapter 및 Fragment 클래스 외에도 여기에 필요한 것은 부모 활동이 어댑터 변수를 참조 할 수 있고 설정되는 것입니다.

어댑터

public class GrowPagerAdapter extends FragmentPagerAdapter implements OnPageChangeListener, OnScrollChangedListener {

public final String TAG = this.getClass().getSimpleName();

private final int COUNT = 4;

public static final float BASE_SIZE = 0.8f;
public static final float BASE_ALPHA = 0.8f;

private int mCurrentPage = 0;
private boolean mScrollingLeft;

private List<SummaryTabletFragment> mFragments;

public int getCurrentPage() {
    return mCurrentPage;
}

public void addFragment(SummaryTabletFragment fragment) {
    mFragments.add(fragment.getPosition(), fragment);
}

public GrowPagerAdapter(FragmentManager fm) {
    super(fm);

    mFragments = new ArrayList<SummaryTabletFragment>();
}

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

@Override
public Fragment getItem(int position) {
    return SummaryTabletFragment.newInstance(position);
}

@Override
public void onPageScrollStateChanged(int state) {}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    adjustSize(position, positionOffset);
}

@Override
public void onPageSelected(int position) {
    mCurrentPage = position;
}

/**
 * Used to adjust the size of each view in the viewpager as the user
 * scrolls.  This provides the effect of children scaling down as they
 * are moved out and back to full size as they come into focus.
 * 
 * @param position
 * @param percent
 */
private void adjustSize(int position, float percent) {

    position += (mScrollingLeft ? 1 : 0);
    int secondary = position + (mScrollingLeft ? -1 : 1);
    int tertiary = position + (mScrollingLeft ? 1 : -1);

    float scaleUp = mScrollingLeft ? percent : 1.0f - percent;
    float scaleDown = mScrollingLeft ? 1.0f - percent : percent;

    float percentOut = scaleUp > BASE_ALPHA ? BASE_ALPHA : scaleUp;
    float percentIn = scaleDown > BASE_ALPHA ? BASE_ALPHA : scaleDown;

    if (scaleUp < BASE_SIZE)
        scaleUp = BASE_SIZE;

    if (scaleDown < BASE_SIZE)
        scaleDown = BASE_SIZE;

    // Adjust the fragments that are, or will be, on screen
    SummaryTabletFragment current = (position < mFragments.size()) ? mFragments.get(position) : null;
    SummaryTabletFragment next = (secondary < mFragments.size() && secondary > -1) ? mFragments.get(secondary) : null;
    SummaryTabletFragment afterNext = (tertiary < mFragments.size() && tertiary > -1) ? mFragments.get(tertiary) : null;

    if (current != null && next != null) {

        // Apply the adjustments to each fragment
        current.transitionFragment(percentIn, scaleUp);
        next.transitionFragment(percentOut, scaleDown);

        if (afterNext != null) {
            afterNext.transitionFragment(BASE_ALPHA, BASE_SIZE);
        }
    }
}

@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {

    // Keep track of which direction we are scrolling
    mScrollingLeft = (oldl - l) < 0;
}
}

파편

public class SummaryTabletFragment extends BaseTabletFragment {

public final String TAG = this.getClass().getSimpleName();

private final float SCALE_SIZE = 0.8f;

private RelativeLayout mBackground, mCover;
private TextView mTitle;
private VerticalTextView mLeft, mRight;

private String mTitleText;
private Integer mColor;

private boolean mInit = false;
private Float mScale, mPercent;

private GrowPagerAdapter mAdapter;
private int mCurrentPosition = 0;

public String getTitleText() {
    return mTitleText;
}

public void setTitleText(String titleText) {
    this.mTitleText = titleText;
}

public static SummaryTabletFragment newInstance(int position) {

    SummaryTabletFragment fragment = new SummaryTabletFragment();
    fragment.setRetainInstance(true);

    Bundle args = new Bundle();
    args.putInt("position", position);
    fragment.setArguments(args);

    return fragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    mRoot = inflater.inflate(R.layout.tablet_dummy_view, null);

    setupViews();
    configureView();

    return mRoot;
}

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

    if (savedInstanceState != null) {
        mColor = savedInstanceState.getInt("color", Color.BLACK);
    }

    configureView();
}

@Override
public void onSaveInstanceState(Bundle outState)  {

    outState.putInt("color", mColor);

    super.onSaveInstanceState(outState);
}

@Override
public int getPosition() {
    return getArguments().getInt("position", -1);
}

@Override
public void setPosition(int position) {
    getArguments().putInt("position", position);
}

public void onResume() {
    super.onResume();

    mAdapter = mActivity.getPagerAdapter();
    mAdapter.addFragment(this);
    mCurrentPosition = mAdapter.getCurrentPage();

    if ((getPosition() == (mCurrentPosition + 1) || getPosition() == (mCurrentPosition - 1)) && !mInit) {
        mInit = true;
        transitionFragment(GrowPagerAdapter.BASE_ALPHA, GrowPagerAdapter.BASE_SIZE);
        return;
    }

    if (getPosition() == mCurrentPosition && !mInit) {
        mInit = true;
        transitionFragment(0.00f, 1.0f);
    }
}

private void setupViews() {

    mCover = (RelativeLayout) mRoot.findViewById(R.id.cover);
    mLeft = (VerticalTextView) mRoot.findViewById(R.id.title_left);
    mRight = (VerticalTextView) mRoot.findViewById(R.id.title_right);
    mBackground = (RelativeLayout) mRoot.findViewById(R.id.root);
    mTitle = (TextView) mRoot.findViewById(R.id.title);
}

private void configureView() {

    Fonts.applyPrimaryBoldFont(mLeft, 15);
    Fonts.applyPrimaryBoldFont(mRight, 15);

    float[] size = UiUtils.getScreenMeasurements(mActivity);
    int width = (int) (size[0] * SCALE_SIZE);
    int height = (int) (size[1] * SCALE_SIZE);

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
    mBackground.setLayoutParams(params);

    if (mScale != null)
        transitionFragment(mPercent, mScale);

    setRandomBackground();

    setTitleText("Fragment " + getPosition());

    mTitle.setText(getTitleText().toUpperCase());
    mLeft.setText(getTitleText().toUpperCase());
    mRight.setText(getTitleText().toUpperCase());

    mLeft.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showNextPage();
        }
    });

    mRight.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showPrevPage();
        }
    });
}

private void setRandomBackground() {

    if (mColor == null) {
        Random r = new Random();
        mColor = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255));
    }

    mBackground.setBackgroundColor(mColor);
}

public void transitionFragment(float percent, float scale) {

    this.mScale = scale;
    this.mPercent = percent;

    if (getView() != null && mCover != null) {

        getView().setScaleX(scale);
        getView().setScaleY(scale);

        mCover.setAlpha(percent);
        mCover.setVisibility((percent <= 0.05f) ? View.GONE : View.VISIBLE);
    }
}

@Override
public String getFragmentTitle() {
    return null;
}
}

0

내 솔루션 : 거의 모든 뷰를 static. 이제 내 앱이 완벽하게 상호 작용합니다. 모든 곳에서 정적 메서드를 호출 할 수 있다는 것은 좋은 스타일이 아닐 수 있지만 작동하지 않는 코드를 가지고 놀아야하는 이유는 무엇입니까? 나는 여기에서 많은 질문과 대답을 읽었으며 어떤 해결책도 성공하지 못했습니다.

메모리가 누출되고 힙이 낭비 될 수 있다는 것을 알고 있으며 내 코드가 다른 프로젝트에 적합하지 않을 것입니다.하지만 이것에 대해 무서워하지 않습니다. 다른 장치와 조건에서 앱을 테스트했습니다. 전혀 문제가 없었습니다. Android 플랫폼이이를 처리 할 수있을 것 같습니다. UI는 매초마다 새로 고쳐지며 S2 ICS (4.0.3) 장치에서도 앱이 수천 개의 지리적 마커를 처리 할 수 ​​있습니다.


0

나는 같은 문제에 직면했지만 내 ViewPager는 .NET을 사용하여 어댑터를 만들고 설정하는 TopFragment 내부에있었습니다 setAdapter(new FragmentPagerAdapter(getChildFragmentManager())).

onAttachFragment(Fragment childFragment)다음과 같이 TopFragment에서 재정 의하여이 문제를 해결했습니다 .

@Override
public void onAttachFragment(Fragment childFragment) {
    if (childFragment instanceof OnboardingDiamondsFragment) {
        mChildFragment = (ChildFragment) childFragment;
    }

    super.onAttachFragment(childFragment);
}

이미 알려진 것처럼 (위의 답변 참조) childFragmentManager가 자신을 다시 만들 때 viewPager 내부에 있던 조각도 만듭니다.
중요한 부분은 그 후에 그는 onAttachFragment 를 호출 하고 이제 새로 생성 된 조각에 대한 참조를 갖게 된다는 것입니다 !

이것이 나처럼이 오래된 Q를 얻는 데 도움이되기를 바랍니다. :)


0

SparceArray에 조각을 저장하여 문제를 해결했습니다.

public abstract class SaveFragmentsPagerAdapter extends FragmentPagerAdapter {

    SparseArray<Fragment> fragments = new SparseArray<>();

    public SaveFragmentsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.append(position, fragment);
        return fragment;
    }

    @Nullable
    public Fragment getFragmentByPosition(int position){
        return fragments.get(position);
    }

}

0

아시다시피 ...

이 클래스에 대한 많은 비애에 추가하여 공유 할 가치가있는 다소 흥미로운 버그가 있습니다.

ViewPager를 사용하여 항목 트리를 탐색하고 있습니다 (항목을 선택하면 뷰 페이저가 오른쪽으로 스크롤 애니메이션되고 다음 분기가 나타나고 뒤로 이동하며 ViewPager가 반대 방향으로 스크롤하여 이전 노드로 돌아갑니다). .

FragmentStatePagerAdapter의 끝에서 조각을 푸시하고 팝할 때 문제가 발생합니다. 항목이 변경되는 것을 알아 차릴만큼 똑똑하고 항목이 변경되었을 때 조각을 만들고 교체 할 수있을만큼 똑똑합니다. 그러나 조각 상태를 버릴만큼 똑똑하지 않거나 어댑터 크기가 변경 될 때 내부적으로 저장된 조각 상태를 잘라낼만큼 똑똑하지 않습니다. 따라서 항목을 팝하고 새 항목을 끝으로 푸시하면 새 항목의 조각이 이전 항목에 대한 조각의 저장된 상태를 가져 와서 내 코드에 절대적인 혼란을 초래했습니다. 내 조각은 인터넷에서 다시 가져 오기 위해 많은 작업이 필요할 수있는 데이터를 전달하므로 상태를 저장하지 않는 것은 실제로 선택 사항이 아닙니다.

깨끗한 해결 방법이 없습니다. 나는 다음과 같은 것을 사용했습니다.

  public void onSaveInstanceState(Bundle outState) {
    IFragmentListener listener = (IFragmentListener)getActivity();
    if (listener!= null)
    {
        if (!listener.isStillInTheAdapter(this.getAdapterItem()))
        {
            return; // return empty state.
        }

    }
    super.onSaveInstanceState(outState);

    // normal saving of state for flips and 
    // paging out of the activity follows
    ....
  }

새 조각 인스턴스는 여전히 savedState 번들을 가져 오지만 적어도 오래된 데이터를 전달하지 않기 때문에 불완전한 솔루션입니다.


0

사람들은 댓글을 읽는 경향이 없기 때문에 여기에 내가 쓴 내용과 대부분 중복되는 답변이 있습니다 .

문제의 근본 원인은 Android 시스템 getItem이 실제로 표시되는 조각을 얻기 위해 호출하지 않고 instantiateItem. 이 메서드는 먼저에서 주어진 탭에 대한 조각 인스턴스를 조회하고 재사용하려고합니다 FragmentManager. 이 조회가 실패하는 경우 ( FragmentManager새로 생성 될 때만 발생 ) getItem가 호출됩니다. 예를 들어 사용자가 기기를 회전 할 때마다 조각 (무거울 수 있음)을 다시 만들지 않는 것은 분명한 이유입니다.
이 문제를 해결하려면 Fragment.instantiate활동에서 조각을 만드는 대신 조각 트랜잭션을 시작 / 커밋 pagerAdapter.instantiateItem하는 startUpdate/finishUpdate메서드 호출 로 이러한 모든 호출을 둘러싸 야합니다 .getItem 각각의 생성자를 사용하여 조각이 실제로 생성되는 곳이어야합니다.

List<Fragment> fragments = new Vector<Fragment>();

@Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        fragments.add(adapter.instantiateItem(viewPager, 0));
        fragments.add(adapter.instantiateItem(viewPager, 1));
        // and so on if you have more tabs...
        adapter.finishUpdate(viewPager);
}

class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

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

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.