ViewPager를 동적으로 업데이트 하시겠습니까?


316

ViewPager에서 컨텐츠를 업데이트 할 수 없습니다.

FragmentPagerAdapter 클래스에서 instantiateItem () 및 getItem () 메소드의 올바른 사용법은 무엇입니까?

getItem () 만 사용하여 조각을 인스턴스화하고 반환했습니다.

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

이것은 잘 작동했습니다. 내용을 변경할 수 없다는 점만 빼면

그래서 이것을 찾았습니다 : ViewPager PagerAdapter가 View를 업데이트하지 않습니다

"제 접근법은 instantiateItem () 메소드의 인스턴스화 된 뷰에 대해 setTag () 메소드를 사용하는 것입니다."

이제 instantiateItem ()을 구현하고 싶습니다. 그러나 무엇을 반환 해야하는지 (유형은 Object) 모르겠으며 getItem (int position)과의 관계는 무엇입니까?

나는 참조를 읽었다 :

  • 공개 추상 조각 getItem (int position)

    지정된 위치와 관련된 조각을 반환합니다.

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

    주어진 위치에 대한 페이지를 작성하십시오. 어댑터는 여기에 제공된 컨테이너에보기를 추가해야하지만 finishUpdate (ViewGroup)에서 리턴 될 때만 수행해야합니다. 매개 변수

    컨테이너 페이지가 표시 될 포함보기입니다. position 인스턴스화 할 페이지 위치.

    보고

    새 페이지를 나타내는 Object를 리턴합니다. 이것은 뷰일 필요는 없지만 페이지의 다른 컨테이너 일 수 있습니다.

그러나 나는 여전히 그것을 얻지 못한다.

여기 내 코드가 있습니다. 지원 패키지 v4를 사용하고 있습니다.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

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

        pager = (ViewPager)findViewById(R.id.slider);

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

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

    public void setData(String[] data) {
        this.data = data;
    }

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

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

비슷한 문제가있는 사람도 있습니다 .http : //www.mail-archive.com/android-developers@googlegroups.com/msg200477.html


관련된 구성 요소를 더 잘 이해하려면 별도의 후면 탐색 기록이있는 탭 ViewPager에 대한 자습서를 읽는 것이 좋습니다. GitHub 및 추가 리소스에 대한 실제 예제가 제공됩니다. medium.com/@nilan/…
marktani


GitHub 에서이 예제를 확인할 수 있습니다 . 이 예제가 도움이 되길 바랍니다.
Cabezas

좋은 간단한 해결책 : stackoverflow.com/a/35450575/2162226
Gene Bo

Google은 최근 동적 Fragment / View 업데이트를 단순화하는 ViewPager2를 도입했습니다. github.com/googlesamples/android-viewpager2 에 대한 예제를 보거나 stackoverflow.com/a/57393414/1333448
Yves Delerm

답변:


605

FragmentPagerAdapter 또는 FragmentStatePagerAdapter를 사용할 때는 전적으로 getItem()만지거나 전혀 손대지 않는 것이 가장 좋습니다 instantiateItem(). instantiateItem()- destroyItem()- isViewFromObject()PagerAdapter에 대한 인터페이스는 FragmentPagerAdapter의 사용이 훨씬 간단 구현하는 낮은 수준의 인터페이스 getItem()인터페이스를.

이것에 들어가기 전에, 나는 명확히해야합니다

표시되는 실제 조각을 끄려면 FragmentPagerAdapter를 피하고 FragmentStatePagerAdapter를 사용해야합니다.

이 답변의 이전 버전은 FragmentPagerAdapter를 사용하는 실수를 예로 들었습니다. FragmentPagerAdapter는 처음 표시 된 후에는 FragmentPagerAdapter가 조각을 파괴하지 않기 때문에 작동하지 않습니다.

나는 당신이 링크 한 게시물에 제공된 setTag()findViewWithTag()해결 방법을 권장하지 않습니다 . 발견했듯이 조각을 사용 setTag()하고 findViewWithTag()작동하지 않으므로 일치하지 않습니다.

올바른 해결책은 재정의하는 것 getItemPosition()입니다. 경우 notifyDataSetChanged()라고, ViewPager 통화 getItemPosition()가 다른 위치로 이동하거나 제거 할 필요가 있는지 여부를 확인하기 위해 어댑터에있는 모든 항목에.

기본적 getItemPosition()으로을 반환합니다 POSITION_UNCHANGED. 즉, "이 개체는 아무 곳에 나 있으면 파괴하거나 제거하지 마십시오." 반환 POSITION_NONE대신에, 말을하지 않습니다에 의해 수정에게 문제를 "이 개체는 더 이상 내가 그것을 제거, 표시하고있어 항목입니다." 따라서 어댑터의 모든 단일 항목을 제거하고 다시 만드는 효과가 있습니다.

이것은 완전히 합법적 인 수정입니다! 이 수정 사항은 notifyDataSetChanged가보기 재활용없이 일반 어댑터처럼 작동하도록합니다. 이 수정 프로그램을 구현하고 성능이 만족 스럽다면 경쟁을 시작한 것입니다. 작업이 완료되었습니다.

더 나은 성능이 필요하면 더 멋진 getItemPosition()구현을 사용할 수 있습니다 . 다음은 문자열 목록에서 조각을 만드는 호출기의 예입니다.

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

이 구현에서는 새 제목을 표시하는 조각 만 표시됩니다. 여전히 목록에있는 제목을 표시하는 조각은 대신 목록의 새 위치로 이동하고 더 이상 목록에없는 제목을 가진 조각은 완전히 삭제됩니다.

프래그먼트를 다시 만들지 않았지만 업데이트해야 할 경우 어떻게해야합니까? 살아있는 조각에 대한 업데이트는 조각 자체에 의해 가장 잘 처리됩니다. 그것은 결국 조각을 갖는 이점입니다. 그것은 자체 컨트롤러입니다. 조각은의 다른 객체에 리스너 또는 관찰자를 추가 한 onCreate()다음에서 제거 onDestroy()하여 업데이트 자체를 관리 할 수 ​​있습니다. getItem()ListView 또는 다른 AdapterView 유형의 어댑터에서와 같이 모든 업데이트 코드를 내부에 넣을 필요는 없습니다 .

마지막으로 FragmentPagerAdapter가 프래그먼트를 파괴하지 않는다고해서 FragmentPagerAdapter에서 getItemPosition이 완전히 쓸모 없다는 의미는 아닙니다. 이 콜백을 사용하여 ViewPager에서 프래그먼트를 재정렬 할 수 있습니다. 그러나 FragmentManager에서 완전히 제거하지는 않습니다.


4
작동하지 않지만 여전히 아무것도 업데이트하지 않습니다 ... 내 코드를 게시했습니다.
Ixx

64
좋아, 문제를 알아 냈습니다. FragmentPagerAdapter 대신 FragmentStatePagerAdapter를 사용해야합니다. FragmentPagerAdapter는 FragmentManager에서 프래그먼트를 제거하지 않으며, 분리 및 첨부 만합니다. 자세한 내용은 문서를 확인하십시오. 일반적으로 영구적 인 조각에만 FragmentPagerAdapter를 사용하려고합니다. 수정하여 내 예제를 편집했습니다.
Bill Phillips

1
나중에 검토하겠습니다. 답변 해 주셔서 감사합니다. 그리고 나는 그것이 작동하면 당신의 대답을 받아들입니다. 마지막 의견 전에 매번 새로운 ViewPager를 제거하고 추가하도록 구현했으며 작동합니다. 그다지 좋은 해결책은 아니지만 지금 바꿀 시간이 없습니다.
Ixx

12
맞습니다. 어댑터의 클래스를 FragmentStatePagerAdapter로 변경하면 모든 문제가 해결되었습니다. 감사!
Ixx

2
POSITION_NONE을 반환하더라도을 사용하는 동안 POSITION_NONE이 FragmentStatePagerAdapter조각을 제거하고 새 인스턴스를 만드는 것을 알 수 있습니다. 그러나 새 인스턴스는 이전 조각 상태의 savedInstanceState 번들을받습니다. 번들을 다시 생성 할 때 null이되도록 조각을 완전히 파괴하려면 어떻게해야합니까?
노래

142

전체보기 재생 POSITION_NONE에서 돌아와서 재생하는 대신 다음을 getItemPosition()수행하십시오.

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

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

조각은 UpdateableFragment인터페이스 를 구현해야합니다 .

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

그리고 인터페이스 :

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

데이터 클래스 :

public class UpdateData {
    //whatever you want here
}

1
이것은 MainActivity가 인터페이스를 구현해야한다는 것을 의미합니까 ??? 도와주세요. 현재 구현에서는 MainActivity 클래스를 조각 사이의 커뮤니케이터로 사용하고 있으므로이 솔루션에 대해 혼란 스러웠습니다.
Rakeeb Rajbhandari

1
좋아 나는 내가 업데이트하고 완전한 이행을 가진 다른 장소에서 대답했다 Fragment: 모양이 동적으로 stackoverflow.com/questions/19891828/...
M-WaJeEh

@ M-WaJeEh 어쩌면 예를 들어주세요! 도시 목록이있는 목록보기가 있으며 도시를 선택하면이 도시에 대한 정보가있는 viewpager (3 페이지)가 열립니다. 잘 작동합니다. 그러나 다른 도시보기 d page on refreshes the 1-st page only after swiping pages? thw 2페이지를 선택한 후 3 d 페이지 만 항상 비어 있습니다. 감사합니다
SERG

4
내가 일한 지난 2.5 일 동안 내가 본 모든 것들 중에서 .. 이것은 나를 위해 일한 유일한 것 .. 모든 종류의 arigato, thanks, spaciba, sukran ..
Orkun Ozen

2
드래곤즈의 어머니!, 이것은 매력처럼 작동합니다, 유일한 해결책은 나를 위해 일했습니다 ... 많이 thx!
Sebastián Guerrero

24

내가 ViewPager7 조각으로 가질 때 이전에 직면했던 것과 같은 문제에 여전히 직면하는 사람들을 위해 . 이 조각의 기본값은 API서비스 에서 영어 내용을로드하는 문제이지만 여기서는 설정 활동에서 언어를 변경하고 설정 활동을 마친 후에 ViewPager주요 활동에서 사용자의 언어 선택과 일치하도록 조각을 새로 고치고 싶습니다 . 사용자가 아랍어를 선택하면 아랍어 콘텐츠가 처음부터 작업 한 것

1- 위에서 언급 한대로 FragmentStatePagerAdapter를 사용해야합니다.

mainActivity의 2- onResume을 무시하고 다음을 수행했습니다.

if (!(mPagerAdapter == null)) {
    mPagerAdapter.notifyDataSetChanged();
}

3-i getItemPosition()는 mPagerAdapter에서 재정 의하여 반환 POSITION_NONE합니다.

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

매력처럼 작동합니다


1
한 situtaion을 제외하고 작동합니다. viewPager에서 마지막 항목을 삭제 한 경우 그런 다음 목록에서 제거되지 않습니다.
Vlado Pandžić

viewpager에서 현재 조각의 왼쪽과 오른쪽 모두에 동적으로 조각을 추가하고있었습니다. 이것은 효과가 있었다! 감사.
h8pathak

15

나는이 문제에 직면하여 오늘 그것을 마침내 해결 했으므로 내가 배운 것을 적어두고 Android에 익숙하지 않은 사람에게 도움이되기를 바랍니다 ViewPager. FragmentStatePagerAdapterAPI 레벨 17에서 사용 중이며 현재 2 개의 조각 만 있습니다. 정확하지 않은 것이 있다고 생각합니다. 감사합니다. 여기에 이미지 설명을 입력하십시오

  1. 직렬화 된 데이터는 메모리에로드되어야합니다. 이것은 CursorLoader/ AsyncTask/를 사용하여 수행 할 수 있습니다 Thread. 자동로드 여부는 코드에 따라 다릅니다. 를 사용하는 경우 CursorLoader등록 된 데이터 옵저버가 있으므로 자동으로로드됩니다.

  2. 을 호출 한 후에 viewpager.setAdapter(pageradapter)는 어댑터 getCount()가 지속적으로 호출되어 프래그먼트를 빌드합니다. 따라서 데이터가로드되면 getCount()0을 반환 할 수 있으므로 데이터가 표시되지 않도록 더미 조각을 만들 필요가 없습니다.

  3. 데이터가로드 된 getCount()후에도 여전히 0이므로 어댑터가 프래그먼트를 자동으로 빌드하지 않으므로 실제로로드 된 데이터 번호를로 설정 getCount()하여 어댑터의를 호출 할 수 있습니다 notifyDataSetChanged(). ViewPager메모리의 데이터로 조각 (처음 두 조각 만)을 만들기 시작하십시오. notifyDataSetChanged()반환 되기 전에 완료되었습니다 . 그런 다음 ViewPager필요한 조각이 있습니다.

  4. 데이터베이스와 메모리의 데이터가 모두 업데이트 (쓰기)하거나 메모리의 데이터 만 업데이트 (다시 쓰기)되거나 데이터베이스의 데이터 만 업데이트되는 경우 마지막 두 경우에 데이터가 데이터베이스에서 메모리로 자동로드되지 않는 경우 (위에서 언급 한 바와 같이). ViewPager및 호출기 어댑터는 메모리에 데이터를 처리.

  5. 따라서 메모리의 데이터가 업데이트되면 어댑터를 호출하기 만하면됩니다 notifyDataSetChanged(). 프래그먼트가 이미 작성되었으므로 어댑터가 리턴 onItemPosition()되기 전에 호출됩니다 notifyDataSetChanged(). 에서 수행 할 작업이 없습니다 getItemPosition(). 그런 다음 데이터가 업데이트됩니다.


내가 사용 notifyDataSetChanged()하는 프래그먼트 (chartview 포함)가 자체적으로 새로 고쳐지기 때문에 데이터를 업데이트 한 후에 할 필요가 없다는 것을 알았습니다 . 이 정적 조각처럼 아니라면, 내가 전화를해야합니다 onItemPositon()반환 POSITION_NONE 미안
oscarthecat

누구든지 그 다이어그램을 만드는 데 사용 된 도구에 대해 알고 있습니까?
JuiCe

12

코드 destroyDrawingCache()에서 ViewPager를 사용해보십시오 notifyDataSetChanged().


3
여기도 마찬가지입니다. 잘 작동합니다. 감사합니다. Fragments뷰 페이저에서 사용하지 않는 특히 힘든 시간이 되었습니다. 감사합니다!
세스

11

좌절의 시간이 문제를 극복하기 위해 위의 모든 솔루션을 시도 또한 같은 다른 유사한 문제에 많은 솔루션을하는 동안 후 , 그리고 모두가이 문제를 해결하고 만들기 위해 나와 함께 실패하는 ViewPager기존의 파괴 Fragment와 채우기 pager로 새로운 Fragments. 다음과 같이 문제를 해결했습니다.

1) 만들기 기능 ViewPager확장에 클래스를 FragmentPagerAdapter다음과 같은 :

 public class myPagerAdapter extends FragmentPagerAdapter {

2) 에 대한 항목을 작성 ViewPager가게가 title와를 fragment다음과 같은 :

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

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

}

3) 인스턴스 생성자 를 다음과 같이 ViewPagerFragmentManager인스턴스에 저장하십시오 class.

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}

4) 다음과 같이 새 목록에서 새 항목을 다시 설정 하기 위해 직접 adapter이전 항목 fragment을 모두 삭제하여 새 데이터로 데이터 를 재설정하는 방법을 작성하십시오 .fragmentManageradapterfragment

public void setPagerItems(ArrayList<PagerItem> pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}

5) 컨테이너에서 Activity또는 Fragment새 데이터로 어댑터를 다시 초기화하지 마십시오. setPagerItems다음과 같이 새 데이터로 메소드 를 통해 새 데이터를 설정하십시오 .

ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

    mPagerAdapter.setPagerItems(pagerItems);
    mPagerAdapter.notifyDataSetChanged();

도움이 되길 바랍니다.


내 문제에 당신을 도울 수 있습니까 stackoverflow.com/questions/40149039/…
albert

이 문제를 도와 주시겠습니까? stackoverflow.com/questions/41547995/…
viper

10

어떤 이유로 든 대답이 나를 위해 일하지 않았으므로 fragmentStatePagerAdapter에서 super를 호출하지 않고 restoreState 메서드를 재정의해야했습니다. 암호:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

}

1
이 SO 답변에서 다른 모든 것을 시도한 후에 이것은 나를 위해 일한 것입니다. 해당 PagerAdapter를 호스팅하는 활동으로 돌아가는 활동을 끝내고 있습니다. 방금 완료 한 활동이 조각을 그리는 데 사용되는 상태가 변경되었을 수 있으므로이 경우 모든 조각을 다시 작성하고 싶습니다.
JohnnyLambada

세상에 .. 작동합니다. 작동합니다. kudos : D 제 경우에는 viewpager 현재 항목 객체를 제거해야합니다. 그러나 위의 모든 방법 / 단계를 시도한 후에 현재 조각이 업데이트되지 않습니다.
Rahul Khurana

3

Bill Phillips가 제공 한 솔루션을 필요에 맞게 약간 수정했습니다.

private class PagerAdapter extends FragmentStatePagerAdapter{
    Bundle oBundle;
    FragmentManager oFragmentManager;
    ArrayList<Fragment> oPooledFragments;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    oFragmentManager=fm;

    }
@Override
public int getItemPosition(Object object) {

    Fragment oFragment=(Fragment)object;
    oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
    if(oPooledFragments.contains(oFragment))
        return POSITION_NONE;
    else
        return POSITION_UNCHANGED;
    } 
}

따라서 현재 시점 에있는 단편에 대해서만 getItemPosition()리턴 이 호출됩니다. (이것 과 그 와 관련된 것은 활동이 아닌 조각에 포함되어 있습니다)POSITION_NONEFragmentManagergetItemPositionFragmentStatePagerViewPager


2

비슷한 문제가 있었지만 기존 솔루션 (하드 코딩 된 태그 이름 등)을 신뢰하고 싶지 않아 M-WaJeEh의 솔루션을 작동시킬 수 없었습니다. 내 해결책은 다음과 같습니다.

getItem에서 생성 된 조각에 대한 참조를 배열로 유지합니다. configurationChange 또는 메모리 부족 또는 활동으로 돌아올 때 (-> 활동으로 돌아올 때 조각이 'getItem'을 다시 호출하지 않고 배열을 업데이트하지 않고 마지막 상태로 돌아갑니다. ).

이 문제를 피하기 위해 instantiateItem (ViewGroup, int)을 구현하고 다음과 같이 배열을 업데이트했습니다.

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

따라서 한편으로는 저에게 맞는 솔루션을 찾아서 공유하고 싶었지만 다른 누군가가 비슷한 것을 시도했는지 여부와 내가하지 말아야 할 이유가 있는지 묻고 싶었습니다. 그렇게합니까? 지금까지 그것은 나를 위해 아주 잘 작동합니다 ...


나는에 관련된 완료 뭔가했습니다 stackoverflow.com/a/23427215/954643을 당신은 아마에서 항목을 제거해야하지만, myFragmentspublic void destroyItem(ViewGroup container, int position, Object object)잘 그래서 당신은 오래된의 fragment 참조를 잡아하지 않는.
qix

1

EventBus 라이브러리를 사용 하여의 Fragment콘텐츠 를 업데이트 합니다 ViewPager. 로직은 EventBus 문서 와 마찬가지로 간단 합니다 . FragmentPagerAdapter인스턴스 를 제어 할 필요가 없습니다 . 코드는 다음과 같습니다.

1 : 이벤트 정의

업데이트해야 할 메시지를 정의하십시오.

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2. 구독자 준비

업데이트가 필요한 조각에 아래 코드를 작성하십시오.

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3. 포스트 이벤트

매개 변수를 업데이트 해야하는 다른 코드 Activity또는 다른 코드로 아래 코드를 작성하십시오.Fragment

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));

1

FragmentStatePagerAdapter를 사용하려면 https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%참조하십시오. 20Stars & groupby = & sort = & id = 37990 입니다. FragmentStatePagerAdapter에 사용 사례에 문제가 있거나 없을 수있는 문제가 있습니다.

또한 링크에는 솔루션이 거의 없습니다. 요구 사항에 거의 맞지 않을 수 있습니다.


그 URL에서 해결책을 찾았습니다. 내 코드 에서이 gist.github.com/ypresto/8c13cb88a0973d071a64 에서 수정 된 FragmentStatePagerAdapter를 사용했으며 예상대로 작동하기 시작했습니다.
Rafael

멋있는. 예. 링크도 해결책이 거의 없습니다.
cgr

1

나는 같은 문제를 겪었고 너무 많은 시간을 검색했습니다. stackoverflow 또는 Google을 통해 주어진 답변은 내 문제에 대한 해결책이 아닙니다. 내 문제는 쉬웠다. 목록이 있는데 viewpager와 함께이 목록을 보여줍니다. 목록의 머리에 새 요소를 추가하고 viewpager를 새로 고치면 아무것도 변경되지 않습니다. 내 최종 솔루션은 누구나 쉽게 사용할 수 있습니다. 새 요소가 목록에 추가되고 목록을 새로 고치려고 할 때 먼저 viewpager adapter를 null로 설정 한 다음 어댑터를 다시 작성하고 i를 viewpager로 설정하십시오.

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

어댑터가 FragmentStatePagerAdapter를 확장해야합니다


1

다음은 @Bill Phillips One의 정보를 통합 한 구현으로, 데이터가 변경된 경우를 제외하고 대부분 조각 캐싱을 가져옵니다. 간단하고 잘 작동하는 것 같습니다.

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);

0

나는 많은 다른 접근법을 시도했지만 아무도 내 문제를 해결하지 못했습니다. 다음은 모두가 제공하는 다양한 솔루션으로 해결하는 방법입니다. 모두 감사합니다.

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

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

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

onResume () 후에 페이지 0 만 새로 고치고 싶습니다.

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

@Override
protected void onResume() {
    super.onResume();

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

내 FragmentsMain에는 공용 정수 "page"가 있는데, 이것이 새로 고칠 페이지인지 여부를 알려줍니다.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;

0

나는 당에 늦었다는 것을 안다. TabLayout#setupWithViewPager(myViewPager);방금 전화를 걸어 문제를 해결했습니다.FragmentPagerAdapter#notifyDataSetChanged();


0

이 솔루션은 모든 사람에게 적용되지는 않지만 제 경우에는 ViewPager의 모든 조각이 다른 클래스이며 한 번에 하나만 존재합니다.

이 제약 조건으로 인해이 솔루션은 안전하며 프로덕션 환경에서 사용하기에 안전해야합니다.

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

동일한 프래그먼트의 여러 버전이있는 경우이 동일한 전략을 사용하여 해당 프래그먼트에서 메소드를 호출하여 업데이트하려는 프래그먼트인지 판별 할 수 있습니다.


0

이것은 누군가에게 도움이 될 수 있습니다-제 경우에는 뷰 페이저가 기존 조각의 위치를 ​​두 번 요구했지만 새 항목의 위치를 ​​요구하지 않아 잘못된 동작과 데이터가 표시되지 않습니다.

  1. FragmentStatePagerAdapter의 소스를 복사하십시오 (연령이 업데이트되지 않은 것으로 보임).

  2. notifyDataSetChanged () 재정의

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. 충돌을 방지하기 위해 destroyItem ()에 위생 검사를 추가하십시오.

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }

0

위의 모든 답변과 여러 다른 게시물을 살펴 보았지만 여전히 다른 조각 유형과 함께 동적으로 탭을 추가 및 제거하는 나에게 도움이되는 것을 찾을 수 없었습니다. FWIW 다음 접근법은 나를 위해 일한 것입니다 (다른 사람이 같은 문제가있는 경우).

public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
    private static final String TAB1_TITLE = "Tab 1";
    private static final String TAB2_TITLE = "Tab 2";
    private static final String TAB3_TITLE = "Tab 3";

    private ArrayList<String> titles = new ArrayList<>();
    private Map<Fragment, Integer> fragmentPositions = new HashMap<>();


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


    public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
        titles.clear();

        if (showTab1) {
            titles.add(TAB1_TITLE);
        }
        if (showTab2) {
            titles.add(TAB2_TITLE);
        }
        if (showTab3) {
            titles.add(TAB3_TITLE);
        }
        notifyDataSetChanged();
    }


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

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;

        String tabName = titles.get(position);
        if (tabName.equals(TAB1_TITLE)) { 
            fragment =  Tab1Fragment.newInstance();
        } else if (tabName.equals(TAB2_TITLE)) {
            fragment =  Tab2Fragment.newInstance();
        } else if (tabName.equals(TAB3_TITLE)) {
            fragment =  Tab3Fragmen.newInstance();
        } 

        ((BaseFragment)fragment).setTitle(tabName);
        fragmentPositions.put(fragment, position);
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object item) {
        BaseFragment fragment = (BaseFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        Integer fragmentPosition = fragmentPositions.get(item);
        if (fragmentPosition != null && position == fragmentPosition) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }

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

0

인덱스 기반으로 프래그먼트를 다시 작성하거나 다시로드하려면 FragmentPagerAdapter 대신 FragmentStatePagerAdapter를 사용하십시오. 예를 들어 FirstFragment 이외의 프래그먼트를 다시로드하려는 경우 인스턴스를 확인하고 다음과 같이 위치를 리턴 할 수 있습니다.

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }

0

instantiateItem의 mFragments 요소 getItemPosition을 변경해야합니다.

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

mFragmentsnotifyDataSetChanged ()를 호출 할 때의 요소 위치가 변경되지 않기 때문에 AndroidX FragmentStatePagerAdapter.java 기반 .

출처 : https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

예 : https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

이 프로젝트를 실행하여 작업 방법을 확인할 수 있습니다. https://github.com/cuichanghao/infivt

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.