IT story

ViewPager에서 조각 검색

hot-time 2020. 4. 11. 10:16
반응형

ViewPager에서 조각 검색


세 가지 다른 조각을 호스팅 ViewPager하기 FragmentStatePagerAdapter위해 와 함께 사용하고 있습니다 .

  • [Fragment1]
  • [Fragment2]
  • [Fragment3]

나는 싶어 할 때 Fragment1으로부터 ViewPager에서 FragmentActivity.

무엇이 문제이며 어떻게 해결합니까?


주요 대답은 프레임 워크에서 생성되는 이름에 의존합니다. 이것이 변경되면 더 이상 작동하지 않습니다.

오버라이드 (override)이 솔루션에 대해 무엇을, instantiateItem()그리고 destroyItem()당신의 Fragment(State)PagerAdapter:

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

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

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

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

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

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

이것은 사용 가능한 조각을 다룰 때 나에게 효과가있는 것 같습니다. 아직 인스턴스화되지 않은 조각은을 호출 할 때 null을 반환 getRegisteredFragment합니다. 하지만 현재 얻을 대부분이 사용하고 Fragment의 아웃 ViewPager: adapater.getRegisteredFragment(viewPager.getCurrentItem())이것은 반환하지 않습니다 null.

이 솔루션의 다른 단점은 없습니다. 있다면, 알고 싶습니다.


ViewPager에서 조각을 잡으려면 여기 및 기타 관련 SO 스레드 / 블로그에 많은 답변이 있습니다. 내가 본 사람은 모두 부서졌으며 일반적으로 아래에 나열된 두 가지 유형 중 하나에 해당하는 것 같습니다. 당신은 단지처럼, 현재의 단편을 잡아하려는 경우 다른 유효한 해결책이 있습니다 이 스레드에서 다른 대답.

사용하는 경우 FragmentPagerAdapter아래 참조하십시오. 이것FragmentStatePagerAdapter 의 가치를 사용 하는 경우 . FragmentStateAdapter에서 현재 인덱스가 아닌 인덱스를 가져 오는 것은 그 특성에 따라 유용하지 않습니다. 인덱스가 완전히 종료되거나 offScreenLimit 범위를 벗어났습니다.

불행한 길

잘못됨 : FragmentPagerAdapter.getItem()호출 될 때 추가 된 자체 내부 조각 목록 유지

  • 일반적으로 사용 SparseArray또는Map
  • 내가 본 많은 사례 중 하나도 수명주기 이벤트를 설명하지 않으므로이 솔루션은 취약합니다. getItem에서 페이지를 처음 스크롤 할 때만 호출되는 것처럼 (또는 ViewPager.setOffscreenPageLimit(x)0보다 큰 경우) ViewPager호스팅 Activity/ Fragment가 종료되거나 다시 시작 SpaseArray되면 사용자 정의 FragmentPagerActivity를 다시 만들 때 내부 가 지워지지 만 ViewPagers 내부 조각 재 작성됩니다 및 getItem합니다 NOT 인덱스의 요구 될 수 있으므로 인덱스에서 조각을 얻을 수있는 능력은 영원히 손실됩니다. 당신은 밖으로 절약을 통해 이러한 단편 참조를 복원하여 설명 할 수 FragmentManager.getFragment()putFragment하지만 시작은 지저분한 이럴을 얻을 수 있습니다.

잘못 : 후드에서 사용 된 것과 일치하는 자신의 태그 ID를 구성FragmentPagerAdapter 하고 이것을 사용하여 페이지 조각을 검색하십시오.FragmentManager

  • 이 최초의 내부 배열 솔루션에서 패배 한 - 조각 - 참조 문제 대처 이것은 더 나은 지라,하지만 바로 위의 다른 곳에 그물에 대한 답변에서 지적 - 그것은 그것으로 해키 느낌이 개인 내부의 방법 ViewPager즉 수 언제든지 또는 모든 OS 버전을 변경할 수 있습니다.

이 솔루션에 대해 재현 된 방법은

private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

행복한 길 : ViewPager.instantiateItem()

getItem()위와 비슷 하지만 수명주기를 벗어나지 않는 비슷한 방법 은 인덱스를 만들거나 액세스 할 때마다 전자가 호출되므로 instantiateItem()대신 연결하는 것입니다 getItem(). 이 답변을 참조하십시오

행복한 길 : 자신 만의 길을 만드십시오 FragmentViewPager

최신 지원 libFragmentViewPager소스 에서 사용자 고유의 클래스를 구성 하고 프래그먼트 태그를 생성하기 위해 내부적으로 사용되는 메소드를 변경하십시오. 아래와 같이 교체 할 수 있습니다. 이것은 태그 생성이 절대 바뀌지 않으며 개인 API / 메소드에 의존하지 않는 것이 항상 위험하다는 이점이 있습니다.

/**
 * @param containerViewId the ViewPager this adapter is being supplied to
 * @param id pass in getItemId(position) as this is whats used internally in this class
 * @return the tag used for this pages fragment
 */
public static String makeFragmentName(int containerViewId, long id) {
    return "android:switcher:" + containerViewId + ":" + id;
}

doc이 말했듯이 인덱스에 사용 된 조각을 가져 오려면 getItem이 아직 호출되지 않은 경우 FragmentPagerAdapter결과 가 null수 있음을 인식 하면서이 메소드와 같은 것을 호출하십시오 (사용자 정의 또는 하위 클래스에 넣을 수 있음) 해당 페이지 즉, 아직 작성되지 않은 페이지입니다.

/**
 * @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
 * yet OR is a sibling covered by {@link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
 * the current positions fragment.
 */
public @Nullable Fragment getFragmentForPosition(int position)
{
    String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    return fragment;
}

이것은 간단한 솔루션이며 웹 어디에서나 발견되는 다른 두 가지 솔루션의 문제를 해결합니다.


FragmentPagerAdapter에 다음 메소드를 추가하십시오.

public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return  mFragmentManager.findFragmentByTag(name);
}

private static String makeFragmentName(int viewId, int index) {
    return "android:switcher:" + viewId + ":" + index;
}

getActiveFragment (0)이 작동해야합니다.

다음은 ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d에 구현 된 솔루션 입니다. 무언가 실패하면 좋은 충돌 로그가 표시됩니다.


또 다른 간단한 해결책 :

    public class MyPagerAdapter extends FragmentPagerAdapter {
        private Fragment mCurrentFragment;

        public Fragment getCurrentFragment() {
            return mCurrentFragment;
        }
//...    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (getCurrentFragment() != object) {
                mCurrentFragment = ((Fragment) object);
            }
            super.setPrimaryItem(container, position, object);
        }
    }

나는 이것이 약간의 답변을 가지고 있다는 것을 알고 있지만 아마도 이것은 누군가를 도울 것입니다. 내가 얻을 필요로 할 때 나는 비교적 간단한 솔루션을 사용하고 있습니다 Fragment내에서 ViewPager. 당신에 Activity또는 Fragment을 잡고 ViewPager, 당신은 모든 순환이 코드를 사용할 수 Fragment는 보유하고 있습니다.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
    if(viewPagerFragment != null) {
        // Do something with your Fragment
        // Check viewPagerFragment.isResumed() if you intend on interacting with any views.
    }
}
  • 당신이 당신의 위치를 알고있는 경우 Fragment의를 ViewPager, 당신은 단지 호출 할 수 있습니다 getItem(knownPosition).

  • 당신이 당신의 위치를 모르는 경우 Fragment의를 ViewPager, 당신은 당신의 아이들이 수 Fragments와 같은 방법으로 인터페이스를 구현 getUniqueId()하고 구별하기 위해 그것을 사용합니다. 또는 전체를 순환하고 다음 Fragments과 같은 클래스 유형을 확인할 수 있습니다.if(viewPagerFragment instanceof FragmentClassYouWant)

!!! 편집하다 !!!

나는 처음에 각각 을 생성해야 할 getItem만 호출 된다는 것을 발견했다. 그 후에 는를 사용하여 재활용 된 것처럼 보인다 . 이 방법은 많은 구현은 만들 에들 . 위의 방법을 사용하면의 모든 항목을 살펴볼 때마다 호출 할 때마다 새을 만듭니다 . 이 때문에 나는 대신에 (허용 된 답변 사용하여) 사용하는 더 나은 접근 방식을 찾았습니다 . 이것은보다 완벽한 솔루션이며 나를 위해 잘 작동했습니다.FragmentPagerAdapterFragmentFragmentsFragmentManagerFragmentPagerAdapter FragmentgetItemFragmentgetItemFragmentPagerAdapterFragmentManagerFragment

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    String name = makeFragmentName(mViewPager.getId(), i);
    Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
    // OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
    if(viewPagerFragment != null) {

        // Do something with your Fragment
        if (viewPagerFragment.isResumed()) {
            // Interact with any views/data that must be alive
        }
        else {
            // Flag something for update later, when this viewPagerFragment
            // returns to onResume
        }
    }
}

이 방법이 필요합니다.

private static String makeFragmentName(int viewId, int position) {
    return "android:switcher:" + viewId + ":" + position;
}

필자의 경우 위의 해결책 중 어느 것도 효과가 없었습니다.

그러나 Fragment에서 Child Fragment Manager를 사용하고 있으므로 다음이 사용되었습니다.

Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());

Manager의 조각이 viewpager 항목과 일치하는 경우에만 작동합니다.


ViewPager에서 현재 보이는 조각 을 얻으려면 . 이 간단한 진술을 사용하고 있으며 잘 작동합니다.

      public Fragment getFragmentFromViewpager()  
      {
         return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
      }

이것은 위의 Steven의 답변을 기반으로합니다. 부모 활동에 이미 첨부 된 조각의 실제 인스턴스를 반환합니다.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
    for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {

        Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
        if(viewPagerFragment != null && viewPagerFragment.isAdded()) {

            if (viewPagerFragment instanceof FragmentOne){
                FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
                if (oneFragment != null){
                    oneFragment.update(); // your custom method
                }
            } else if (viewPagerFragment instanceof FragmentTwo){
                FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;

                if (twoFragment != null){
                    twoFragment.update(); // your custom method
                }
            }
        }
    }

나는 이것을 할 수있는 간단하고 깨끗한 방법을 찾을 수 없었습니다. 그러나 ViewPager 위젯은 조각을 호스팅하는 또 다른 ViewGroup입니다. ViewPager는 이러한 조각을 직계 자식으로 갖습니다. 따라서 .getChildCount () 및 .getChildAt ()를 사용하여 반복하여 찾고있는 조각 인스턴스가 현재 ViewPager에로드되어 참조를 얻을 수 있는지 확인할 수 있습니다. 예를 들어 정적 고유 ID 필드를 사용하여 조각을 구별 할 수 있습니다.

ViewPager는 ListView와 같은 가상화 컨테이너이므로 찾고있는 조각을로드하지 않았을 수 있습니다.


FragmentPagerAdapter는 프래그먼트의 팩토리입니다. 메모리에 여전히 위치에 따라 조각을 찾으려면 다음을 사용하십시오.

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

v4 지원 API의 샘플 코드.


먼저 사용할 모든 조각 ( List<Fragment> fragments;) 목록을 만든 다음 호출기에 추가하여 현재 본 조각을보다 쉽게 ​​처리 할 수 ​​있도록 처리했습니다.

그래서:

@Override
onCreate(){
    //initialise the list of fragments
    fragments = new Vector<Fragment>();

    //fill up the list with out fragments
    fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));


    //Set up the pager
    pager = (ViewPager)findViewById(R.id.pager);
    pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
    pager.setOffscreenPageLimit(4);
}

그래서 이것을 호출 할 수 있습니다 :

public Fragment getFragment(ViewPager pager){   
    Fragment theFragment = fragments.get(pager.getCurrentItem());
    return theFragment;
}

그래서 올바른 조각에있는 경우에만 실행되는 if 문에서 척킹 할 수 있습니다.

Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
    MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
    //then you can do whatever with the fragment
    theFrag.costomFunction();
}

그러나 그것은 내 핵 및 슬래시 접근 방식이지만 저에게 효과적이었습니다. 뒤 버튼을 누를 때 현재 표시된 조각에 관련 변경을 수행합니다.


당신은 하지 않습니다 호출 할 필요가 getItem()a의 참조 얻을 이후 단계에서 다른 방법 Fragment호스팅 내부 ViewPager. 내부의 일부 데이터를 업데이트 Fragment하려면 다음 방법을 사용하십시오. ViewPager를 동적으로 업데이트 하시겠습니까?

열쇠는 내부에 새로운 데이터를 설정 Adaper하고 호출 하여을 notifyDataSetChanged()호출 getItemPosition()하여 참조를 전달 Fragment하고 업데이트 할 수있는 기회를 제공합니다. 다른 모든 방법을 사용하려면 자신이나 좋은 해결책이 아닌 다른 핵을 참조해야합니다.

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(xyzData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

FragmentPagerAdapterViewPager 어댑터 클래스로 확장해야 합니다.
당신이 사용 FragmentStatePagerAdapter하면 당신은 Fragment그것으로 당신을 찾을 수 없습니다ID

public static String makeFragmentName(int viewPagerId, int index) {
  return "android:switcher:" + viewPagerId + ":" + index;
}

이 방법을 사용하는 방법 :-

Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
       AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;

이봐, 난이 질문에 대답했다 여기 . 기본적으로 재정의해야합니다

공용 객체 instantiateItem (ViewGroup 컨테이너, int 위치)

FragmentStatePagerAdapter의 메소드입니다.


가장 좋은 해결책은 CodePath 에서 만든 SmartFragmentStatePagerAdapter 라는 확장을 사용하는 것입니다 . 이 가이드에 따라 ViewPager에서 조각 및 현재 선택된 조각을 훨씬 쉽게 검색 할 수 있습니다. 또한 어댑터에 임베드 된 프래그먼트의 메모리를 관리하는 데 도움이됩니다.


가장 쉽고 가장 간결한 방법입니다. 모든 조각 ViewPager이 다른 클래스 인 경우 다음과 같이 검색하고 구분할 수 있습니다.

public class MyActivity extends Activity
{

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment.getClass() == MyFragment.class) {
            mMyFragment = (MyFragment) fragment;
        }
    }

}

나는 조금 다른 접근 방식으로 이것을 쉽게 구현했습니다.

내 사용자 정의 FragmentAdapter.getItem 메소드가 새 MyFragment ()가 아니라 FragmentAdapter 생성자에서 작성된 MyFragment 인스턴스를 리턴했습니다.

내 활동에서 어댑터에서 조각을 가져 와서 instanceOf 필요한 조각인지 확인한 다음 필요한 메소드를 캐스팅하고 사용하십시오.


/values/integers.xml에 정수 자원 ID 작성

<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>

그런 다음 PagerAdapter getItem 함수에서 :

public Fragment getItem(int position) {

        Fragment fragment = null;

        if (position == 0) {
            fragment = FragmentOne.newInstance();
            mViewPager.setTag(R.integer.page1,fragment);

        }
        else if (position == 1) {

            fragment = FragmentTwo.newInstance();
            mViewPager.setTag(R.integer.page2,fragment);

        } else if (position == 2) {

            fragment = FragmentThree.newInstance();
            mViewPager.setTag(R.integer.page3,fragment);

        }

        return fragment;
        }

그런 다음 활동 에서이 함수를 작성하여 조각 참조를 얻으십시오.

private Fragment getFragmentByPosition(int position) {
    Fragment fragment = null;

    switch (position) {
        case 0:
            fragment = (Fragment) mViewPager.getTag(R.integer.page1);
            break;

        case 1:

            fragment = (Fragment) mViewPager.getTag(R.integer.page2);
            break;

        case 2:
            fragment = (Fragment) mViewPager.getTag(R.integer.page3);
            break;
            }

            return fragment;
    }

위 함수를 호출하여 조각 참조를 가져온 다음 사용자 지정 조각으로 캐스트하십시오.

Fragment fragment = getFragmentByPosition(position);

        if (fragment != null) {
                    FragmentOne fragmentOne = (FragmentOne) fragment;
                    }

프래그먼트 관리자에서 프래그먼트를 반복하는 쉬운 방법. public static PlaceholderFragment newInstance (int sectionNumber)에 배치 된 섹션 위치 인수가있는 viewpager를 찾으십시오 .

public PlaceholderFragment getFragmentByPosition(Integer pos){
    for(Fragment f:getChildFragmentManager().getFragments()){
        if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
            return (PlaceholderFragment) f;
        }
    }
    return null;
}

조각으로

public int getArgument(){
   return mPage;
{
public void update(){

}

FragmentActivity에서

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
    if((f instanceof PageFragment)&&(!f.isDetached())){
          PageFragment pf = (PageFragment)f;   
          if(pf.getArgument()==pager.getCurrentItem())pf.update();
    }
}

TabLayout에는 Fragment에 대한 여러 탭이 있습니다. 조각의 색인을 사용하여 태그별로 조각을 찾을 수 있습니다.

예를 들어. Fragment1의 인덱스는 0이므로 findFragmentByTag()메소드에서 조각 페이지를 추가하고 조각을 대체 할 수있는 fragmentTransaction을 사용하여 Viewpager의 태그를 전달하십시오.

String tag = "android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);


어댑터에 FragmentStatePagerAdapter대한 해결책은 다음과 같습니다.

FragmentActivity에서 :

ActionBar mActionBar = getSupportActionBar(); 
mActionBar.addTab(mActionBar.newTab().setText("TAB1").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment1.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB2").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment2.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB3").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment3.class.getName())));

viewPager = (STViewPager) super.findViewById(R.id.viewpager);
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mActionBar);
viewPager.setAdapter(this.mPagerAdapter);

그리고 클래스 FragmentActivity에서 메소드를 작성하십시오-그래서 메소드는 당신이 당신의 Fragment에 액세스 할 수 있도록, 당신이 원하는 조각의 위치를 ​​지정해야합니다 :

public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}

어댑터에서 :

public class MyPagerAdapter extends FragmentStatePagerAdapter {

private final ActionBar actionBar;
private final FragmentManager fragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager, com.actionbarsherlock.app.ActionBarActionBar mActionBar) {super(fragmentManager);
this.actionBar = mActionBar;
this.fragmentManager = fragmentManager;
}

@Override
public Fragment getItem(int position) {
getSupportFragmentManager().beginTransaction().add(mTchatDetailsFragment, makeFragmentName(position)).commit();
return (Fragment)this.actionBar.getTabAt(position);
}

@Override
public int getCount() {
return this.actionBar.getTabCount();
}

@Override
public CharSequence getPageTitle(int position) {
return this.actionBar.getTabAt(position).getText();
}

private static String makeFragmentName(int viewId, int index) {
return "android:fragment:" + index;
}

}

Fragment yourFragment = yourviewpageradapter.getItem(int index);

indexfragment1먼저 추가 한 것처럼 어댑터에서 조각의 장소 이므로 나머지는 0으로 검색 fragment1패스 index반복하십시오.

참고 URL : https://stackoverflow.com/questions/8785221/retrieve-a-fragment-from-a-viewpager

반응형