programing

ViewPager 및 fragments - fragment의 상태를 저장하는 올바른 방법은 무엇입니까?

showcode 2023. 6. 4. 10:43
반응형

ViewPager 및 fragments - fragment의 상태를 저장하는 올바른 방법은 무엇입니까?

모듈로 하는 데 것 같습니다.fragments UI는 UI를 사용할 수 . 하만그와함께지와 함께.ViewPager그것의 라이프사이클은 여전히 나에게 안개입니다.그래서 구루의 생각이 절실히 필요합니다!

편집

아래의 멍청한 해결책 보기 ;-)

범위

주요 활동은 다음과 같습니다.ViewPager이러한 다른 할 수 fragment의 를 통해 채워집니다.이러한 단편은 다른 (하위) 활동에 대해 약간 다른 논리를 구현할 수 있으므로, 단편의 데이터는 활동 내부의 콜백 인터페이스를 통해 채워집니다.첫 번째 발사에서는 모든 것이 잘 작동합니다. 하지만!

문제

방향 변경 시ViewPager아래에서 찾을) 코드 때마다 활동 위해 노력한다고 말합니다.ViewPagerfragments와 하지만 fragments fragments, , Manager, fragments에 대한 재생 메커니즘을 시작합니다.따라서 레크리에이션 메커니즘은 활동의 구현된 메서드를 통해 데이터를 시작하기 위해 내 콜백 인터페이스 호출과 함께 첨부, CreateView 등에 있는 "오래된" 조각을 호출합니다.그러나 이 메서드는 Activity의 onCreate 메서드를 통해 새로 생성된 조각을 가리킵니다.

쟁점.

제가 잘못된 패턴을 사용하고 있는지도 모르지만 안드로이드 3 프로 책에도 별로 없습니다.그러니 원투 펀치를 주시고 올바른 방법을 알려주세요.감사합니다!

코드

기본 활동

public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {

private MessagesFragment mMessagesFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    setContentView(R.layout.viewpager_container);
    new DefaultToolbar(this);

    // create fragments to use
    mMessagesFragment = new MessagesFragment();
    mStreamsFragment = new StreamsFragment();

    // set titles and fragments for view pager
    Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
    screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
    screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);

    // instantiate view pager via adapter
    mPager = (ViewPager) findViewById(R.id.viewpager_pager);
    mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);

    // set title indicator
    TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
    indicator.setViewPager(mPager, 1);

}

/* set of fragments callback interface implementations */

@Override
public void onMessageInitialisation() {

    Logger.d("Dash onMessageInitialisation");
    if (mMessagesFragment != null)
        mMessagesFragment.loadLastMessages();
}

@Override
public void onMessageSelected(Message selectedMessage) {

    Intent intent = new Intent(this, StreamActivity.class);
    intent.putExtra(Message.class.getName(), selectedMessage);
    startActivity(intent);
}

BasePager

public class BasePagerActivity extends FragmentActivity {

BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}

어댑터

public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {

private Map<String, Fragment> mScreens;

public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {

    super(fm);
    this.mScreens = screenMap;
}

@Override
public Fragment getItem(int position) {

    return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}

@Override
public int getCount() {

    return mScreens.size();
}

@Override
public String getTitle(int position) {

    return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}

// hack. we don't want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {

    // TODO Auto-generated method stub
}

}

파편

public class MessagesFragment extends ListFragment {

private boolean mIsLastMessages;

private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;

private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;

// define callback interface
public interface OnMessageListActionListener {
    public void onMessageInitialisation();
    public void onMessageSelected(Message selectedMessage);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // setting callback
    mListener = (OnMessageListActionListener) activity;
    mIsLastMessages = activity instanceof DashboardActivity;

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    inflater.inflate(R.layout.fragment_listview, container);
    mProgressView = inflater.inflate(R.layout.listrow_progress, null);
    mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
    return super.onCreateView(inflater, container, savedInstanceState);
}

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

    // instantiate loading task
    mLoadMessagesTask = new LoadMessagesTask();

    // instantiate list of messages
    mMessagesList = new ArrayList<Message>();
    mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
    setListAdapter(mAdapter);
}

@Override
public void onResume() {
    mListener.onMessageInitialisation();
    super.onResume();
}

public void onListItemClick(ListView l, View v, int position, long id) {
    Message selectedMessage = (Message) getListAdapter().getItem(position);
    mListener.onMessageSelected(selectedMessage);
    super.onListItemClick(l, v, position, id);
}

/* public methods to load messages from host acitivity, etc... */
}

해결책

멍청한 솔루션은 futFragment를 사용하여 내부의 fragment를 SaveInstanceState(호스트 활동의)에 저장하고 getFragment를 통해 Create(생성)에 fragment를 내부로 가져오는 것입니다.하지만 아직도 이상한 느낌이 들어요. 일이 그렇게 되어서는 안 된다는...아래 코드 참조:

    @Override
protected void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);
    getSupportFragmentManager()
            .putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}

protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    ...
    // create fragments to use
    if (savedInstanceState != null) {
        mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
                savedInstanceState, MessagesFragment.class.getName());
                StreamsFragment.class.getName());
    }
    if (mMessagesFragment == null)
        mMessagesFragment = new MessagesFragment();
    ...
}

때.FragmentPagerAdapterFragment Manager에 fragment를 추가합니다. fragment는 fragment가 배치될 특정 위치에 따라 특수 태그를 사용합니다. FragmentPagerAdapter.getItem(int position)해당 위치의 조각이 없는 경우에만 호출됩니다.회전한 후 Android는 이 특정 위치에 대한 조각을 이미 생성/저장했음을 알게 되므로 단순히 다시 연결하려고 합니다.FragmentManager.findFragmentByTag()새로 생성하는 대신.이 모든 것은 사용 시 무료로 제공됩니다.FragmentPagerAdapter를 fragment initialization 를 fragment interial로 하는 것이 일반적인 이유입니다.getItem(int)방법.

우리가 사용하지 않았더라도FragmentPagerAdapter매번 새로운 조각을 만드는 것은 좋은 생각이 아닙니다.Activity.onCreate(Bundle)Fragment Manager에 fragment가 추가되면 fragment가 회전한 후 다시 생성되므로 다시 추가할 필요가 없습니다.이렇게 하면 fragment 작업 시 오류가 발생하는 일반적인 원인이 됩니다.

조각 작업 시 일반적인 접근 방식은 다음과 같습니다.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    CustomFragment fragment;
    if (savedInstanceState != null) {
        fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
    } else {
        fragment = new CustomFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit(); 
    }

    ...

}

를 사용하는 경우FragmentPagerAdapter관리를 할 필요가 없습니다."라고 말합니다.단, 기적으단앞뒤조프다니됩다각, ).FragmentStatePagerAdapter). 이는 ViewPager.setOffscreenPageLimit(int)의해 제어됩니다.이 때문에 어댑터 외부의 fragment에 직접 호출하는 메서드는 활성화되지 않을 수 있으므로 유효하지 않습니다.

간단히 말해서, 사용할 수 있는 해결책입니다.putFragment나중에 참조를 얻을 수 있다는 것은 그렇게 미친 것도 아니며, 어쨌든 fragment를 사용하는 일반적인 방법과 다르지 않습니다(위).사용자가 아닌 어댑터가 조각을 추가했기 때문에 참조를 얻기가 어렵습니다.꼭 확인해 주세요.offscreenPageLimit이 값은 원하는 조각을 항상 로드할 수 있을 정도로 높습니다. 이 값은 해당 조각이 존재하는 것에 의존하기 때문입니다.이렇게 하면 ViewPager의 느린 로드 기능을 생략할 수 있지만 응용 프로그램에 대해 원하는 것처럼 보입니다.

또 다른 접근 방식은 오버라이드입니다.FragmentPageAdapter.instantiateItem(View, int)슈퍼콜에서 반환된 fragment에 대한 참조를 저장한 후 반환합니다(fragment가 이미 존재하는 경우 fragment를 찾을 수 있는 로직이 있습니다).

전체 그림을 보려면 FragmentPagerAdapter(짧은 길이) ViewPager(긴 길이)의 일부 소스를 살펴봅니다.

확장 가능한 솔루션을 제공하고 싶습니다.antonyt멋진 답변과 우선순위에 대한 언급.FragmentPageAdapter.instantiateItem(View, int)된 성된참저면에 합니다.Fragments나중에 작업할 수 있도록 해주세요.이 작업은 다음 작업에도 사용할 수 있습니다.FragmentStatePagerAdapter자세한 내용은 참고를 참조하십시오.


다음은 에 대한 참조를 얻는 방법에 대한 간단한 예입니다.Fragments 된환반이 했습니다.FragmentPagerAdapter그은것니지하않습다의존내부.tags을설한정에 .Fragments중요한 것은 참조를 재정의하고 저장하는 입니다.getItem().

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

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

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

또는 당신이 함께 일하는 것을 선호한다면.tags 변수 " " /신조refe클래버"에 대한Fragments당신은 또한 그것을 잡을 수 있습니다.tags에 의해 설정된.FragmentPagerAdapter는 다음과 같은 경우에는.FragmentStatePagerAdapter날이 저물지 않아서tags를 할 때Fragments.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

이 방법은 내부를 모방하는 데 의존하지 않습니다.tag에 의해 설정된.FragmentPagerAdapter대신 적절한 API를 사용하여 검색합니다.이런 식으로 해도 말입니다.tag의 향후 의 변경 SupportLibrary당신은 여전히 안전할 것입니다.


당신의 디자인에 따라 다르다는 것을 잊지 마세요.Activity,Fragments당신은 아직 존재할 수도 있고 존재하지 않을 수도 있으므로, 당신은 그것을 함으로써 설명해야 합니다.null참조를 사용하기 전에 확인합니다.

또한, 만약 당신이 대신 일을 한다면,FragmentStatePagerAdapter그러면 당신은 당신의 엄격한 언급을 유지하고 싶지 않을 것입니다.Fragments왜냐하면 당신은 그것들 중 많은 것들을 가지고 있을 수 있고 하드 레퍼런스는 그것들을 불필요하게 기억에 간직할 것이기 때문입니다. 신대저를 합니다.Fragment에어의언급에 있는 참고 WeakReference표준 변수 대신 변수를 입력합니다.다음과 같이:

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}

당신의 질문에 대한 다른 비교적 쉬운 해결책을 찾았습니다.

FragmentPagerAdapter 소스 코드에서 알 수 있듯이, fragments는 다음과 같이 관리됩니다.FragmentPagerAdapter에저에 합니다.FragmentManager다음을 사용하여 생성된 태그 아래:

String tag="android:switcher:" + viewId + ":" + index;

viewId는 것은입니다.container.getId(),container의 신의입니다.ViewPager사례.index조각의 위치입니다.를 서라객를 ID 수할있다에 할 수 .outState:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("viewpagerid" , mViewPager.getId() );
}

@Override
    protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null)
        viewpagerid=savedInstanceState.getInt("viewpagerid", -1 );  

    MyFragmentPagerAdapter titleAdapter = new MyFragmentPagerAdapter (getSupportFragmentManager() , this);        
    mViewPager = (ViewPager) findViewById(R.id.pager);
    if (viewpagerid != -1 ){
        mViewPager.setId(viewpagerid);
    }else{
        viewpagerid=mViewPager.getId();
    }
    mViewPager.setAdapter(titleAdapter);

만약 당신이 이 조각과 통신하고 싶다면, 당신은 다음과 같은 것을 얻을 수 있습니다.FragmentManager를 들어 다음과 같습니다.

getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewpagerid + ":0")

저는 아마도 약간 다른 사례에 대한 대안을 제시하고 싶습니다. 왜냐하면 저의 답변에 대한 많은 검색이 저를 계속 이 스레드로 이끌었기 때문입니다.

경우 - 동적으로 페이지를 생성/추가하고 ViewPager로 이동하지만(구성 변경 시) 회전하면 OnCreate가 다시 호출되기 때문에 새 페이지가 나타납니다.하지만 저는 회전하기 전에 만들어진 모든 페이지를 계속 참조하고 싶습니다.

문제 - 생성하는 각 조각에 대해 고유한 식별자가 없으므로 참조할 수 있는 유일한 방법은 회전/구성 변경 후 복원할 배열에 참조를 저장하는 것이었습니다.

해결 방법 - 핵심 개념은 활동(Fragments 표시)에서 기존 Fragment에 대한 참조 배열도 관리하는 것이었습니다. 이 활동은 SaveInstanceState의 번들을 활용할 수 있기 때문입니다.

public class MainActivity extends FragmentActivity

따라서 이 활동에서 열려 있는 페이지를 추적할 개인 구성원을 선언합니다.

private List<Fragment> retainedPages = new ArrayList<Fragment>();

이는 SaveInstanceState가 호출되고 Create에서 복원될 때마다 업데이트됩니다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    retainedPages = _adapter.exportList();
    outState.putSerializable("retainedPages", (Serializable) retainedPages);
    super.onSaveInstanceState(outState);
}

일단 저장되면 검색할 수 있습니다

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

    if (savedInstanceState != null) {
        retainedPages = (List<Fragment>) savedInstanceState.getSerializable("retainedPages");
    }
    _mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
    _adapter = new ViewPagerAdapter(getApplicationContext(), getSupportFragmentManager());
    if (retainedPages.size() > 0) {
        _adapter.importList(retainedPages);
    }
    _mViewPager.setAdapter(_adapter);
    _mViewPager.setCurrentItem(_adapter.getCount()-1);
}

이것들은 주요 활동에 필요한 변경 사항들이었고, 그래서 저는 이것이 작동하기 위해 fragmentPagerAdapter 내의 구성원들과 방법들이 필요했습니다, 그래서 안에서.

public class ViewPagerAdapter extends FragmentPagerAdapter

동일한 구성(위의 주 활동에 표시됨)

private List<Fragment> _pages = new ArrayList<Fragment>();

그리고 이 동기화(위의 onSaveInstanceState에서 사용된 바와 같이)는 특히 메소드에 의해 지원됩니다.

public List<Fragment> exportList() {
    return _pages;
}

public void importList(List<Fragment> savedPages) {
    _pages = savedPages;
}

그리고 마지막으로 단편 수업에서

public class CustomFragment extends Fragment

이 모든 것이 작동하기 위해, 첫 번째, 두 가지 변화가 있었습니다.

public class CustomFragment extends Fragment implements Serializable

그런 다음 조각이 파괴되지 않도록 만들기에 추가합니다.

setRetainInstance(true);

저는 아직 Fragments와 Android 라이프 사이클에 대해 고민하고 있습니다. 따라서 이 방법에는 중복성/비효율성이 있을 수 있습니다.하지만 그것은 저에게 효과가 있고 저와 비슷한 사례를 가진 다른 사람들에게 도움이 되기를 바랍니다.

제 솔루션은 매우 무례하지만 효과적입니다. 보존된 데이터에서 동적으로 생성된 제 조각이기 때문에, 저는 단순히 모든 조각을 제거합니다.PageAdapter전화하기 super.onSaveInstanceState()활동을 만들 때 다시 작성합니다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt("viewpagerpos", mViewPager.getCurrentItem() );
    mSectionsPagerAdapter.removeAllfragments();
    super.onSaveInstanceState(outState);
}

수없다니습에서 할 수 onDestroy()그렇지 않으면 다음과 같은 예외가 발생합니다.

java.lang.IllegalStateException:다음 이후에는 이 작업을 수행할 수 없습니다.onSaveInstanceState

페이지 어댑터의 코드는 다음과 같습니다.

public void removeAllfragments()
{
    if ( mFragmentList != null ) {
        for ( Fragment fragment : mFragmentList ) {
            mFm.beginTransaction().remove(fragment).commit();
        }
        mFragmentList.clear();
        notifyDataSetChanged();
    }
}

하고 재현페만복저다니원합에 합니다.onCreate()조각이 생성된 후.

if (savedInstanceState != null)
    mViewPager.setCurrentItem( savedInstanceState.getInt("viewpagerpos", 0 ) );  

은 입니까?BasePagerAdapter은 표준 호출기 중 . -- " 표 호 하 사 야 합 니 다 해 용 를 준 나 중 출 어 기 댑 터 다 합 니 ▁you ▁-- ▁adapters ▁one ▁either ▁use 표 ▁pagerFragmentPagerAdapter또는FragmentStatePagerAdapter.ViewPager(전자) 상태를 유지하거나 (전자) 상태를 저장하고 (전자) 필요한 경우 다시 생성합니다.

을 사용하기 위한 ViewPager여기에서 확인할 수 있습니다.

간 입니다.FragmentManager이 프레임워크에서는 상태를 저장하고 호출기가 만든 활성 조각을 복원합니다.이 모든 것은 초기화할 때 어댑터가 복원된 조각과 다시 연결되는지 확인해야 한다는 것을 의미합니다.를 보면 .FragmentPagerAdapter또는FragmentStatePagerAdapter어떻게 하는지 확인할 수 있습니다.

fragmentStatePagerAdapter에서 fragment의 상태를 제대로 복원하지 못하는 문제가 있는 경우... 즉, 새 fragment는 fragmentStatePagerAdapter에서 상태에서 복원하는 대신 생성됩니다.

꼭전세요에 전화하세요.ViewPager.setOffscreenPageLimit()하기 ViewPager.setAdapter(fragmentStatePagerAdapter)

에 할 때ViewPager.setOffscreenPageLimit()...ViewPager는 즉시 어댑터를 찾아 fragment를 가져옵니다.이 문제는 ViewPager가 저장된 인스턴스 상태에서 조각을 복원하기 전에 발생할 수 있습니다(따라서 새 조각이므로 저장된 인스턴스 상태에서 다시 초기화할 수 없는 새 조각을 만듭니다).

저는 이 간단하고 우아한 해결책을 생각해냈습니다.이는 활동이 Fragment를 생성하고 어댑터가 Fragment를 제공하는 것으로 가정합니다.

이것은 어댑터의 코드입니다. (여기서 이상한 것은 없습니다. 단,mFragments활동에 의해 유지 관리되는 조각 목록입니다.)

class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {

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

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        TabFragment fragment = (TabFragment)mFragments.get(position);
        return fragment.getTitle();
    }
} 

이 스레드의 전체 문제는 "오래된" 조각의 참조를 얻는 것이므로, 이 코드를 활동의 작성에 사용합니다.

    if (savedInstanceState!=null) {
        if (getSupportFragmentManager().getFragments()!=null) {
            for (Fragment fragment : getSupportFragmentManager().getFragments()) {
                mFragments.add(fragment);
            }
        }
    }

물론 필요한 경우 이 코드를 더 미세하게 조정할 수 있습니다. 예를 들어 조각이 특정 클래스의 인스턴스인지 확인하는 것이 좋습니다.

방향 변경 후 fragment를 가져오려면 .getTag()를 사용해야 합니다.

    getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewPagerId + ":" + positionOfItemInViewPager)

조금 더 다루기 위해 나는 페이지 어댑터에 대해 나만의 배열 목록을 작성하여 fragment를 viewPagerId로 가져오고 fragmentClass를 임의의 위치에서 가져옵니다.

public class MyPageAdapter extends FragmentPagerAdapter implements Serializable {
private final String logTAG = MyPageAdapter.class.getName() + ".";

private ArrayList<MyPageBuilder> fragmentPages;

public MyPageAdapter(FragmentManager fm, ArrayList<MyPageBuilder> fragments) {
    super(fm);
    fragmentPages = fragments;
}

@Override
public Fragment getItem(int position) {
    return this.fragmentPages.get(position).getFragment();
}

@Override
public CharSequence getPageTitle(int position) {
    return this.fragmentPages.get(position).getPageTitle();
}

@Override
public int getCount() {
    return this.fragmentPages.size();
}


public int getItemPosition(Object object) {
    //benötigt, damit bei notifyDataSetChanged alle Fragemnts refrehsed werden

    Log.d(logTAG, object.getClass().getName());
    return POSITION_NONE;
}

public Fragment getFragment(int position) {
    return getItem(position);
}

public String getTag(int position, int viewPagerId) {
    //getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.shares_detail_activity_viewpager + ":" + myViewPager.getCurrentItem())

    return "android:switcher:" + viewPagerId + ":" + position;
}

public MyPageBuilder getPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
    return new MyPageBuilder(pageTitle, icon, selectedIcon, frag);
}


public static class MyPageBuilder {

    private Fragment fragment;

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    private String pageTitle;

    public String getPageTitle() {
        return pageTitle;
    }

    public void setPageTitle(String pageTitle) {
        this.pageTitle = pageTitle;
    }

    private int icon;

    public int getIconUnselected() {
        return icon;
    }

    public void setIconUnselected(int iconUnselected) {
        this.icon = iconUnselected;
    }

    private int iconSelected;

    public int getIconSelected() {
        return iconSelected;
    }

    public void setIconSelected(int iconSelected) {
        this.iconSelected = iconSelected;
    }

    public MyPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
        this.pageTitle = pageTitle;
        this.icon = icon;
        this.iconSelected = selectedIcon;
        this.fragment = frag;
    }
}

public static class MyPageArrayList extends ArrayList<MyPageBuilder> {
    private final String logTAG = MyPageArrayList.class.getName() + ".";

    public MyPageBuilder get(Class cls) {
        // Fragment über FragmentClass holen
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return super.get(indexOf(item));
            }
        }
        return null;
    }

    public String getTag(int viewPagerId, Class cls) {
        // Tag des Fragment unabhängig vom State z.B. nach bei Orientation change
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return "android:switcher:" + viewPagerId + ":" + indexOf(item);
            }
        }
        return null;
    }
}

따라서 다음 조각으로 MyPageArrayList를 만듭니다.

    myFragPages = new MyPageAdapter.MyPageArrayList();

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_data_frag),
            R.drawable.ic_sd_storage_24dp,
            R.drawable.ic_sd_storage_selected_24dp,
            new WidgetDataFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_color_frag),
            R.drawable.ic_color_24dp,
            R.drawable.ic_color_selected_24dp,
            new WidgetColorFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_textsize_frag),
            R.drawable.ic_settings_widget_24dp,
            R.drawable.ic_settings_selected_24dp,
            new WidgetTextSizeFrag()));

페이지 보기에 추가합니다.

    mAdapter = new MyPageAdapter(getSupportFragmentManager(), myFragPages);
    myViewPager.setAdapter(mAdapter);

이 후에는 클래스를 사용하여 올바른 fragment를 변경한 후에 얻을 수 있습니다.

        WidgetDataFrag dataFragment = (WidgetDataFrag) getSupportFragmentManager()
            .findFragmentByTag(myFragPages.getTag(myViewPager.getId(), WidgetDataFrag.class));

Fragments를 직접 저장하는 대신 Fragment Manager에게 맡겨두고 Fragment를 처리해야 할 경우 Fragment Manager에서 Fragment를 찾습니다.

//make sure you have the right FragmentManager 
//getSupportFragmentManager or getChildFragmentManager depending on what you are using to manage this stack of fragments
List<Fragment> fragments = fragmentManager.getFragments();
if(fragments != null) {
   int count = fragments.size();
   for (int x = 0; x < count; x++) {
       Fragment fragment = fragments.get(x);
       //check if this is the fragment we want, 
       //it may be some other inspection, tag etc.
       if (fragment instanceof MyFragment) {
           //do whatever we need to do with it
       }
   }
}

Fragments가 많고 검사 인스턴스 비용이 원하는 것이 아닐 수도 있지만 Fragment Manager가 Fragments를 이미 보관하고 있다는 점을 염두에 두는 것이 좋습니다.

추가:

   @SuppressLint("ValidFragment")

수업 전에

다음과 같은 것을 하는 것은 효과가 없습니다.

@SuppressLint({ "ValidFragment", "HandlerLeak" })

언급URL : https://stackoverflow.com/questions/7951730/viewpager-and-fragments-whats-the-right-way-to-store-fragments-state

반응형