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) 인스턴스 생성자 를 다음과 같이 ViewPager내 FragmentManager인스턴스에 저장하십시오 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을 당신은 아마에서 항목을 제거해야하지만, myFragments에 public 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.