it-source

"PageAdapter:: notifyDataSetChanged"가 보기를 업데이트하지 않는 이유는 무엇입니까?

criticalcode 2023. 6. 5. 23:59
반응형

"PageAdapter:: notifyDataSetChanged"가 보기를 업데이트하지 않는 이유는 무엇입니까?

호환성 라이브러리의 ViewPager를 사용하고 있습니다.페이지를 통해 볼 수 있는 여러 보기를 표시하는 데 성공했습니다.

그러나 새로운 보기 집합으로 ViewPager를 업데이트하는 방법을 찾는 데 어려움을 겪고 있습니다.

저는 전화하는 것과 같은 모든 종류의 것들을 시도했습니다.mAdapter.notifyDataSetChanged(),mViewPager.invalidate()새 데이터 목록을 사용할 때마다 새 어댑터를 만듭니다.

아무런 도움이 되지 않습니다. 텍스트 보기는 원래 데이터에서 변경되지 않습니다.

업데이트: 작은 테스트 프로젝트를 만들었고 보기를 거의 업데이트할 수 있었습니다.아래 수업을 붙여넣겠습니다.

그러나 업데이트되지 않는 것은 두 번째 보기이며, 'B'는 그대로 유지되며, 업데이트 버튼을 누른 후 'Y'로 표시되어야 합니다.

public class ViewPagerBugActivity extends Activity {

    private ViewPager myViewPager;
    private List<String> data;

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

        data = new ArrayList<String>();
        data.add("A");
        data.add("B");
        data.add("C");

        myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
        myViewPager.setAdapter(new MyViewPagerAdapter(this, data));

        Button updateButton = (Button) findViewById(R.id.update_button);
        updateButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                updateViewPager();
            }
        });
    }

    private void updateViewPager() {
        data.clear();
        data.add("X");
        data.add("Y");
        data.add("Z");
        myViewPager.getAdapter().notifyDataSetChanged();
    }

    private class MyViewPagerAdapter extends PagerAdapter {

        private List<String> data;
        private Context ctx;

        public MyViewPagerAdapter(Context ctx, List<String> data) {
            this.ctx = ctx;
            this.data = data;
        }

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

        @Override
        public Object instantiateItem(View collection, int position) {
            TextView view = new TextView(ctx);
            view.setText(data.get(position));
            ((ViewPager)collection).addView(view);
            return view;
        }

        @Override
        public void destroyItem(View collection, int position, Object view) {
             ((ViewPager) collection).removeView((View) view);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

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

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {
        }

        @Override
        public void startUpdate(View arg0) {
        }

        @Override
        public void finishUpdate(View arg0) {
        }
    }
}

이를 달성하기 위한 몇 가지 방법이 있습니다.

첫 번째 옵션은 더 쉽지만, 좀 더 비효율적입니다.

재정의getItemPosition의 신의에PagerAdapter다음과 같이:

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

이쪽으로 전화를 걸 때notifyDataSetChanged()보기 호출기가 모든 보기를 제거하고 다시 로드합니다.따라서 재로드 효과가 얻어집니다.

Alvaro Luis Bustamante(이전의 alvarolb)가 제안한 두 번째 옵션은 다음과 같습니다.setTag().instantiateItem()새 보기를 인스턴스화할 때 사용합니다.그러면 사용하는 대신에notifyDataSetChanged()사용할 수 있습니다.findViewWithTag()업데이트할 보기를 찾습니다.

결론

보기가 많거나 특정 항목 및/또는 보기 수정을 지원하려는 경우(언제든지 빠르게), 두 번째 접근 방식(태그 지정)은 수정되지 않은 보기를 모두 다시 만들지 못하도록 하기 때문에 매우 유연하고 성능이 우수합니다.
를 위한 (원본 연구를 위한 kudos to alvarolb.)

그러나 앱에 "새로 고침" 기능만 있거나(단일 항목 변경도 허용되지 않음) 항목이 거의 없는 경우에는 개발 시간을 절약하기 위해 첫 번째 접근 방식을 사용하십시오.

나는 어떤 종류의 버그도 없다고 생각합니다.PagerAdapter문제는 그것이 어떻게 작동하는지 이해하는 것이 조금 복잡하다는 것입니다.여기에 설명된 해결책을 보면, 오해가 있고 따라서 제 관점에서 인스턴스화된 관점을 제대로 사용하지 못하고 있습니다.

지난 며칠간 함께 일하며PagerAdapter그리고.ViewPager그리고 저는 다음을 발견했습니다.

notifyDataSetChanged()에 있는 PagerAdapter은 알만표니다됩시만 표시됩니다.ViewPager기본 페이지가 변경되었습니다.를 들어,으로 페이지를에서 항목 ), " " " " " " "/" ("는 "" "/" " "" " " " " " " " " " " " " 입니다.ViewPager그것을 처리해야 합니다.이 경우에 나는 생각합니다.ViewPager합니다.getItemPosition()그리고.getCount()방법들.

나는 생각합니다.ViewPager▁a▁after시.notifyDataSetChanged() 합니다.getItemPosition() 이 는 "" " " " " " 를 반환합니다.POSITION_NONE,ViewPager하고 " 보가삭이호출고다합니해하것을기"라고 부릅니다.destroyItem()이 보기를 제거합니다.

식으로, 런식으로의, 정재이를 getItemPosition()언제나 돌아오다POSITION_NONE입니다. 이전에 되고 페지내업경는려완우하잘것데에다입전는니못히된트용이이만▁▁time▁you▁ones▁is,것▁will▁created다▁call완입▁every니경▁be▁pages잘우▁the▁you▁complete▁new▁if▁views페▁because▁the못전▁bely이▁previous▁of▁created▁want된▁thely를 부를 때마다 새 보기가 생성되기 때문입니다. 이전에 만든 보기가 삭제되고 호출할 때마다 새 보기가 생성되기 때문입니다.notifyDatasetChanged()몇몇 사람들에게는 그렇게 잘못되지 않은 것처럼 보일 수도 있습니다.TextView 것과 같이 보기가 되고 될 수 .s, ListView는 ListView와 ListView로 구성됩니다.

따라서 보기를 다시 제거하고 인스턴스화하지 않고도 보기의 내용을 효율적으로 변경할 수 있는 몇 가지 방법이 있습니다.그것은 당신이 해결하고자 하는 문제에 따라 다릅니다.제 접근 방식은 메소드의 인스턴스화된 보기에 메소드를 사용하는 것입니다. 따라서 데이터를 변경하거나 필요한 보기를 무효화하려는 경우 의 메서드를 호출하여 값을 업데이트할 때마다 새 보기를 삭제/생성하지 않고 이전에 인스턴스화된 보기를 검색하고 원하는 대로 수정/사용할 수 있습니다.

를 들어 에 100 페이지가 .TextViews는 하나의 값만 주기적으로 업데이트하려고 합니다.앞에서으로, 이 100을 것을 합니다.TextView업데이트할 때마다 s.말이 안 돼요

변할내용을 합니다.FragmentPagerAdapterFragmentStatePagerAdapter.

재정의getItemPosition()방법과 반환POSITION_NONE.

결국, 그것은 들을 것입니다.notifyDataSetChanged()호출기에 표시됩니다.

알바롤브가 제시한 답은 분명히 그것을 하는 가장 좋은 방법입니다.그의 답변을 바탕으로 이를 구현하는 쉬운 방법은 포지션별로 활성 뷰를 저장하는 것입니다.

SparseArray<View> views = new SparseArray<View>();

@Override
public Object instantiateItem(View container, int position) {
    View root = <build your view here>;
    ((ViewPager) container).addView(root);
    views.put(position, root);
    return root;
}

@Override
public void destroyItem(View collection, int position, Object o) {
    View view = (View)o;
    ((ViewPager) collection).removeView(view);
    views.remove(position);
    view = null;
}

그리고 나서 한 번은 위에 있는 것을 무시함으로써.notifyDataSetChanged보기를 새로 고칠 수 있는 방법...

@Override
public void notifyDataSetChanged() {
    int key = 0;
    for(int i = 0; i < views.size(); i++) {
       key = views.keyAt(i);
       View view = views.get(key);
       <refresh view with new data>
    }
    super.notifyDataSetChanged();
}

실제로 유사한 코드를 사용할 수 있습니다.instantiateItem그리고.notifyDataSetChanged보기를 새로 고칩니다.나의 코드에서 나는 정확히 같은 방법을 사용합니다.

같은 문제가 있었습니다.저는 FragmentStatePagerAdapter를 확장하고 다음 메서드를 재정의했습니다.

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

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

}

이 문제를 극복하기 위해 위의 모든 해결책을 시도하고 이와 같은 다른 유사한 질문에 대한 많은 해결책을 시도하는 동안 몇 시간 동안 좌절한 후, 이것과 이것은 모두 이 문제를 해결하고 만들기 위해 저와 함께 실패했습니다.ViewPager옛것을 파괴하기 위하여Fragment다음을 채웁니다.pager새로운 것과 함께Fragment저는 다음과 같이 문제를 해결했습니다.

만들기ViewPager 장할클스래FragmentPagerAdapter다음과 같이:

 public class myPagerAdapter extends FragmentPagerAdapter {

에 대한 항목 생성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;
}

}

생성자 만들기ViewPager내 것을 가져가세요FragmentManager 그을내저인스스턴는하장에 .class다음과 같이:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

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

재설정할 메서드를 만듭니다.adapter의 모든 데이터를 가진 fragmentfragmentManager직접적으로 만드는 것 자체.adapter새로운 기록을 세우기 위해fragment다음과 같이 새 목록에서 다시 검색합니다.

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

컨테이너에서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();

도움이 되길 바랍니다.

제 은 나도같문있내해은결책고었제가은은▁i를 사용하는 입니다.FragmentPagerAdapter의 우선권을 가지고FragmentPagerAdapter#getItemId(int position):

@Override
public long getItemId(int position) {
    return mPages.get(position).getId();
}

기본적으로 이 메서드는 항목의 위치를 반환합니다.라고 생각이 듭니다.ViewPager 여부를 확인itemId변경된 경우에만 페이지를 다시 만듭니다.그러나 재정의되지 않은 버전은 다음과 같은 위치를 반환합니다.itemId페이지가 실제로 다르더라도 ViewPager가 해당 페이지를 하나로 바꾸도록 정의하지 않고 다시 만들어야 합니다.

이걸 사용하려면,long id각 페이지에 필요합니다.일반적으로 고유할 것으로 예상되지만, 이 경우 동일한 페이지에 대한 이전 값과 다를 것을 제안합니다.따라서 여기서는 어댑터의 연속 카운터 또는 (넓은 분포를 가진) 랜덤 정수를 사용할 수 있습니다.

저는 이 주제에서 해결책으로 언급된 관점의 태그를 사용하는 것보다 더 일관성 있는 방법이라고 생각합니다.하지만 모든 경우에는 그렇지 않을 것입니다.

ViewPager는 동적 보기 변경을 지원하도록 설계되지 않았습니다.

저는 이 https://issuetracker.google.com/issues/36956111 , 특히 https://issuetracker.google.com/issues/36956111#comment56 과 관련된 다른 버그를 찾는 동안 이것을 확인했습니다.

이 질문은 약간 오래된 질문이지만 Google은 최근 ViewPager2로 이 문제를 해결했습니다.이를 통해 핸드메이드(유지보수되지 않고 잠재적으로 버그가 있는) 솔루션을 표준 솔루션으로 대체할 수 있습니다.또한 일부 답변처럼 불필요하게 보기를 다시 작성할 수 없습니다.

ViewPager2 예제의 경우 https://github.com/googlesamples/android-viewpager2 을 확인할 수 있습니다.

ViewPager2를 사용하려면 build.gradle 파일에 다음과 같은 종속성을 추가해야 합니다.

  dependencies {
     implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
  }

그런 다음 xml 파일의 ViewPager를 다음으로 바꿀 수 있습니다.

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

그런 다음 활동에서 ViewPager를 ViewPager2로 교체해야 합니다.

ViewPage2에는 RecyclerView가 필요합니다.어댑터 또는 FragmentStateAdapter의 경우 RecyclerView일 수 있습니다.어댑터

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<String> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<String> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvName.setText(arrayList.get(position));
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
        }
    }
} 

TabLayout을 사용하는 경우 TabLayoutMediator를 사용할 수 있습니다.

        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
            @Override
            public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
                // configure your tab here
                tab.setText(tabs.get(position).getTitle());
            }
        });

        tabLayoutMediator.attach();

그러면 어댑터의 데이터를 수정하고 notifyDataSetChanged 메서드를 호출하여 보기를 새로 고칠 수 있습니다.

저는 이 문제에 대한 매우 흥미로운 결정을 내렸습니다.모든 fragment를 메모리에 저장하는 FragmentPagerAdapter를 사용하는 대신 FragmentStatePagerAdapter(안드로이드)를 사용할 수 있습니다.support.v4.app.fragmentStatePagerAdapter)를 선택할 때마다 fragment를 다시 로드합니다.

두 어댑터의 인식은 동일합니다.따라서 "extend FragmentStatePagerAdapter"에서 "extend FragmentPagerAdapter"를 변경하기만 하면 됩니다.

은 저에게이 되지 . 저는 해결책을 . 그래서 저는 작동하는 솔루션을 찾았습니다.넌 할 수 있다.setAdapter어댑터에 다음 어댑터를 변경하기 전에 다음 작업을 수행해야 합니다.

FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
List<Fragment> fragments = fragmentManager.getFragments();
for (Fragment f : fragments) {
    transaction.remove(f);
}
transaction.commit();

그리고 그 이후로는:

viewPager.setAdapter(adapter);

이 문제에 대해 많은 연구를 한 후, 저는 이 문제를 해결하기 위한 올바른 방법이라고 생각하는 정말 좋은 해결책을 발견했습니다.기본적으로 인스턴스화항목은 뷰가 인스턴스화될 때만 호출되며 뷰가 제거되지 않는 한 다시는 호출되지 않습니다(이것은 getItemPosition 함수를 재정의하여 POSITION_NONE을 반환할 때 발생합니다).대신 생성된 보기를 저장하고 어댑터에서 업데이트하거나, 다른 사용자가 업데이트할 수 있도록 가져오기 기능을 생성하거나, 어댑터(내가 가장 좋아하는)를 업데이트하는 설정 기능을 생성합니다.

따라서 MyViewPagerAdapter에서 다음과 같은 변수를 추가합니다.

private View updatableView;

당신의 순간에 있는 a.항목:

 public Object instantiateItem(View collection, int position) {
        updatableView = new TextView(ctx); //My change is here
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);
        return view;
    }

이렇게 하면 보기를 업데이트하는 기능을 만들 수 있습니다.

public updateText(String txt)
{
    ((TextView)updatableView).setText(txt);
}

이것이 도움이 되길 바랍니다!

OP가 그의 질문을 던진 지 2년 반이 지났지만, 이 문제는 여전히, 글쎄요, 여전히 쟁점입니다.이것에 대한 구글의 우선순위가 특별히 높지 않은 것은 분명합니다. 그래서 저는 해결책을 찾기보다는 해결책을 찾았습니다.저에게 가장 큰 돌파구는 문제의 진짜 원인이 무엇인지 알아내는 것이었습니다(이 게시물의 수락된 답변 참조).활성 페이지가 제대로 새로 고쳐지지 않은 것이 문제임이 분명해지자 해결 방법은 다음과 같습니다.

My Fragment(페이지)에서:

  • 저는 CreateView에서 양식을 채우는 모든 코드를 가져와서 프레임워크가 아닌 어디에서나 호출할 수 있는 PopulateForm이라는 함수에 넣었습니다.이 함수는 getView를 사용하여 현재 보기를 가져오려고 시도하며, 이 값이 null이면 그냥 반환됩니다.FocusChange 리스너 등을 생성하는 다른 모든 코드는 OnCreate에 있습니다.
  • 양식을 다시 로드해야 함을 나타내는 플래그로 사용할 수 있는 부울을 만듭니다.내 이름은 mbReloadForm입니다.
  • mbReloadForm이 설정된 경우 OnResume()를 재정의하여 PopulateForm()을 호출합니다.

페이지 로드를 수행하는 내 활동:

  • 변경하기 전에 0페이지로 이동합니다.저는 FragmentStatePagerAdapter를 사용하고 있기 때문에 최대 2~3페이지가 영향을 받는 것으로 알고 있습니다.0페이지로 변경하면 0, 1, 2페이지에서만 문제가 발생합니다.
  • 이전 목록을 삭제하기 전에 해당 목록의 크기()를 선택합니다.이렇게 하면 버그의 영향을 받는 페이지 수를 알 수 있습니다.> 3일 경우, 3으로 줄이세요 - 다른 PagerAdapter를 사용하는 경우, 처리해야 할 페이지 수를 확인해야 합니다(아마도 모두?).
  • 데이터를 다시 로드하고 페이지Adapter.notifyDataSetChanged()를 호출합니다.
  • 이제 영향을 받는 각 페이지에 대해 pager.getChildAt(i)를 사용하여 페이지가 활성화되어 있는지 확인합니다. 그러면 보기가 있는지 알 수 있습니다.그렇다면 호출기를 호출합니다.보기 채우기().그렇지 않은 경우 양식 다시 로드 플래그를 설정합니다.

이 후에도 두 번째 페이지 집합을 다시 로드할 때 버그로 인해 일부는 여전히 이전 데이터를 표시합니다.그러나 이제 이 페이지가 새로 고쳐지고 새 데이터가 표시됩니다. 페이지를 보기 전에 새로 고침이 수행되므로 사용자는 페이지가 잘못되었음을 알 수 없습니다.

이것이 누군가에게 도움이 되기를 바랍니다!

씬훨더쉬방법: 사용운을 합니다.FragmentPagerAdapter페이지에 있는 보기를 조각으로 감쌉니다..

Rui.araujo와 Alvaro Luis Bustamante에게 감사드립니다.처음에는 루이.아라우조의 방식을 사용하려고 합니다. 왜냐하면 쉽기 때문입니다.작동하지만 데이터가 변경되면 페이지가 분명히 다시 그려집니다.그것은 나쁘기 때문에 나는 Alvaro Luis Bustamante의 방법을 사용하려고 합니다.그것은 완벽해요.코드는 다음과 같습니다.

@Override
protected void onStart() {
    super.onStart();
}

private class TabPagerAdapter extends PagerAdapter {
    @Override
    public int getCount() {
        return 4;
    }

    @Override
    public boolean isViewFromObject(final View view, final Object object) {
        return view.equals(object);
    }

    @Override
    public void destroyItem(final View container, final int position, final Object object) {
        ((ViewPager) container).removeView((View) object);
    }

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View view = LayoutInflater.from(
                getBaseContext()).inflate(R.layout.activity_approval, null, false);
        container.addView(view);
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        view.setTag(position);
        new ShowContentListTask(listView, position).execute();
        return view;
    }
}

데이터가 변경되는 경우:

for (int i = 0; i < 4; i++) {
    View view = contentViewPager.findViewWithTag(i);
    if (view != null) {
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        new ShowContentListTask(listView, i).execute();
    }
}

저는 비슷한 문제를 겪었는데, 4페이지를 가지고 있었고, 한 페이지는 나머지 3페이지에 대한 뷰를 업데이트했습니다.현재 페이지에 인접한 페이지에서 위젯(SeekBars, TextView 등)을 업데이트할 수 있었습니다.마지막 두 페이지는 호출할 때 초기화되지 않은 위젯을 가지고 있습니다.mTabsAdapter.getItem(position).

내 문제를 해결하기 위해, 나는 사용했습니다.setSelectedPage(index)전화하기 getItem(position)이렇게 하면 페이지가 인스턴스화되어 각 페이지의 값과 위젯을 변경할 수 있습니다.

는 모든업트후사수있습니다할용에데이다있니▁use습▁after를 사용할 것입니다.setSelectedPage(position)에 뒤에notifyDataSetChanged().

기본 업데이트 페이지의 목록 보기에서 약간 깜박이는 것을 볼 수 있지만 눈에 띄는 것은 없습니다.저는 그것을 완전히 테스트하지는 않았지만, 그것은 저의 당면한 문제를 해결해줍니다.

FragmentStatePagerAdapter 기반 어댑터를 사용하는 사람이 있는 경우(이 경우 ViewPager는 디스플레이 목적에 필요한 최소 페이지를 만들 수 있습니다. 이 경우 최대 2개), @rui.araujo의 덮어쓰기 답변은 어댑터의 getItemPosition을 크게 낭비하지는 않지만 그래도 개선할 수 있습니다.

유사 코드:

public int getItemPosition(Object object) {
    YourFragment f = (YourFragment) object;
    YourData d = f.data;
    logger.info("validate item position on page index: " + d.pageNo);

    int dataObjIdx = this.dataPages.indexOf(d);

    if (dataObjIdx < 0 || dataObjIdx != d.pageNo) {
        logger.info("data changed, discard this fragment.");
        return POSITION_NONE;
    }

    return POSITION_UNCHANGED;
}

다른 분들이 유용하다고 생각하실까봐 이 답변을 올립니다.동일한 작업을 수행하기 위해 호환성 라이브러리에서 ViewPager 및 PagerAdapter의 소스 코드를 가져와 내 코드 내에서 컴파일하기만 하면 됩니다(모든 오류를 정리하고 직접 가져올 필요가 있지만 반드시 수행할 수 있습니다).

그런 다음 CustomViewPager에서 updateViewAt(int position)라는 메서드를 만듭니다.뷰 자체는 ViewPager 클래스에 정의된 ArrayList mItems에서 가져올 수 있습니다. 인스턴스 항목에서 뷰의 ID를 설정하고 이 ID를 updateViewAt() 메서드의 위치와 비교해야 합니다.그런 다음 필요에 따라 보기를 업데이트할 수 있습니다.

세 단계에서 볼 수 있듯이 모든 조각을 동적으로 업데이트할 수 있습니다.

어댑터 내:

public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mFragmentTags = new HashMap<Integer, String>();
}

// Returns total number of pages
@Override
public int getCount() {
    return NUM_ITEMS;
}

// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return FirstFragment.newInstance();
        case 1:
            return SecondFragment.newInstance();
        case 2:
            return ThirdFragment.newInstance();
        default:
            return null;
    }
}

// Returns the page title for the top indicator
@Override
public CharSequence getPageTitle(int position) {
    return "Page " + position;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        mFragmentTags.put(position, tag);
    }
    return object;
}

public Fragment getFragment(int position) {
    Fragment fragment = null;
    String tag = mFragmentTags.get(position);
    if (tag != null) {
        fragment = mFragmentManager.findFragmentByTag(tag);
    }
    return fragment;
}}

이제 활동을 시작합니다.

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{

MyPagerAdapter mAdapterViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager);
    mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(mAdapterViewPager);
    viewPager.addOnPageChangeListener(this);
}

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

}

@Override
public void onPageSelected(int position) {

    Fragment fragment = mAdapterViewPager.getFragment(position);
    if (fragment != null) {
        fragment.onResume();
    }
}

@Override
public void onPageScrollStateChanged(int state) {

}}

마지막으로, 당신의 단편에는 다음과 같은 것이 있습니다.

public class YourFragment extends Fragment {

// newInstance constructor for creating fragment with arguments
public static YourFragment newInstance() {

    return new YourFragment();
}

// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment, container, false);
}


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

    //to refresh your view
    refresh();

}}

여기서 전체 코드를 볼 수 있습니다.

Alvaro Luis Bustamante에게 감사합니다.

저는 ViewPager의 논리를 알고 있는 것 같습니다.

페이지 집합을 새로 고치고 새 데이터 집합을 기준으로 표시해야 하는 경우 notifyDataSetChanged()를 호출합니다.그런 다음 ViewPager는 ItemPosition()을 얻기 위해 여러 번 호출하여 Fragment를 Object로 전달합니다.이 조각은 이전 데이터 집합(삭제할 것) 또는 새 데이터 집합(표시할 것)에서 가져온 것일 수 있습니다.따라서 getItemPosition()을(를) 재정의하고 내 Fragment가 이전 데이터 세트에서 가져온 것인지 새 데이터 세트에서 가져온 것인지 어떻게든 결정해야 합니다.

제 경우 왼쪽 창에는 상단 항목 목록이 있고 오른쪽 창에는 스와이프 보기(ViewPager)가 있는 2개의 창 레이아웃이 있습니다.따라서 현재 최상위 항목에 대한 링크를 호출기 어댑터 내부와 각 인스턴스화된 페이지 조각 내부에 저장합니다.목록에서 선택한 상위 항목이 변경되면 새 상위 항목을 PagerAdapter에 저장하고 notifyDataSetChanged()를 호출합니다.그리고 재정의된 getItemPosition()에서 어댑터의 최상위 항목과 조각의 최상위 항목을 비교합니다.동일하지 않은 경우에만 POSITION_NONE을 반환합니다.그런 다음 POSITION_NONE을 반환한 모든 fragment를 다시 시작합니다.

참고. 참조 대신 상위 항목 ID를 저장하는 것이 더 좋은 방법일 수 있습니다.

아래 코드 스니펫은 약간 도식적이지만 실제 작동하는 코드에서 수정했습니다.

public class SomeFragment extends Fragment {
  private TopItem topItem;
}

public class SomePagerAdapter extends FragmentStatePagerAdapter {
  private TopItem topItem;

  public void changeTopItem(TopItem newTopItem) {
    topItem = newTopItem;
    notifyDataSetChanged();
  }

  @Override
  public int getItemPosition(Object object) {
    if (((SomeFragment) object).getTopItemId() != topItem.getId()) {
      return POSITION_NONE;
    }
    return super.getItemPosition(object);
  }
}

이전의 모든 연구원들에게 감사드립니다!

아래 코드가 저에게 효과가 있었습니다.

아래와 같이 FragmentPagerAdapter 클래스를 확장하는 클래스를 만듭니다.

public class Adapter extends FragmentPagerAdapter {

private int tabCount;
private Activity mActivity;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private int container_id;
private ViewGroup container;
private List<Object> object;

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

public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) {
    super(fm);
    mActivity = mA;
    mFragmentManager = fm;
    object = new ArrayList<>();
    mFragmentTags = new HashMap<Integer, String>();
    this.tabCount = numberOfTabs;
}

@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return Fragment0.newInstance(mActivity);
        case 1:
            return Fragment1.newInstance(mActivity);
        case 2:
            return Fragment2.newInstance(mActivity);
        default:
            return null;
    }}


@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Log.e("Already defined","Yes");
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        Log.e("Fragment Tag","" + position + ", " + tag);
        mFragmentTags.put(position, tag);
    }else{
        Log.e("Already defined","No");
    }
    container_id = container.getId();
    this.container = container;
    if(position == 0){
        this.object.add(0,object);
    }else if(position == 1){
        this.object.add(1,object);
    }else if(position == 2){
        this.object.add(2,object);
    }
    return object;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    if (object instanceof Fragment) {
        Log.e("Removed" , String.valueOf(position));
    }
}

@Override
public int getItemPosition (Object object)
{   int index = 0;
    if(this.object.get(0) == object){
        index = 0;
    }else if(this.object.get(1) == object){
        index = 1;
    }else if(this.object.get(2) == object){
        index = 2;
    }else{
        index = -1;
    }
    Log.e("Index" , "..................." + String.valueOf(index));
    if (index == -1)
        return POSITION_NONE;
    else
        return index;
}

public String getFragmentTag(int pos){
    return "android:switcher:"+R.id.pager+":"+pos;
}

public void NotifyDataChange(){
    this.notifyDataSetChanged();
}

public int getcontainerId(){
    return container_id;
}

public ViewGroup getContainer(){
    return this.container;
}

public List<Object> getObject(){
    return this.object;
}

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

그런 다음 작성한 각 조각 내부에 updateFragment 메서드를 만듭니다.이 방법에서는 fragment에서 변경해야 할 항목을 변경합니다.예를 들어, 제 경우 Fragment0에는 .ply 파일의 경로를 기반으로 3d 개체를 표시하는 GLSSurfaceView가 포함되어 있으므로, updateFragment 메서드 내에서 이 ply 파일의 경로를 변경합니다.

그런 다음 ViewPager 인스턴스를 생성합니다.

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

그리고 애드패터 인스턴스,

adapter = new Adapter(getSupportFragmentManager(), 3, this);

그럼 이렇게 해봐요

viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);

그런 다음 클래스 내부에서 위의 어댑터 클래스를 초기화하고 viewPager를 만들었습니다. fragment 중 하나(Fragment0의 경우)를 업데이트할 때마다 다음을 사용합니다.

adapter.NotifyDataChange();

adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager.

fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0.

fragment0.updateFragment() method which include the updates on this fragment

adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0.

이 솔루션은 Alvaro Luis Bustamante가 제안한 기술을 기반으로 합니다.

제가 늦었다는 건 알지만 그래도 누군가에게 도움이 될 수 있습니다.저는 단지 승인된 답변을 연장하는 것이고, 그에 대한 코멘트도 추가했습니다.

글쎄요, 그 대답 자체가 비효율적이라고 말합니다.

따라서 필요한 경우에만 새로 고치려면 다음 작업을 수행할 수 있습니다.

private boolean refresh;

public void refreshAdapter() {
    refresh = true;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(@NonNull Object object) {
    if (refresh) {
        refresh = false;
        return POSITION_NONE;
    } else {
        return super.getItemPosition(object);
    }
}

ViewPager2에서 어댑터를 다시 초기화하여 호출기 목록을 새 보기로 새로 고칠 수 있습니다. viewPager2.adapter = myPagerAdapter

1.먼저 호출기 어댑터 클래스 2에서 getItemposition 메서드를 설정해야 합니다.View Pager 3의 정확한 위치를 읽은 다음 해당 위치를 새 Pager 4의 데이터 위치로 전송해야 합니다.페이지 변경 수신기의 집합 내에서 클릭 수신기에 업데이트 쓰기 버튼

그 프로그램 코드는 특정 위치 요소만 설정하도록 약간 수정되었습니다.

public class MyActivity extends Activity {

private ViewPager myViewPager;
private List<String> data;
public int location=0;
public Button updateButton;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    data = new ArrayList<String>();
    data.add("A");
    data.add("B");
    data.add("C");
    data.add("D");
    data.add("E");
    data.add("F");

    myViewPager = (ViewPager) findViewById(R.id.pager);
    myViewPager.setAdapter(new MyViewPagerAdapter(this, data));

      updateButton = (Button) findViewById(R.id.update);

    myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int i, float v, int i2) {
             //Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onPageSelected( int i) {
          // here you will get the position of selected page
            final int k = i;
             updateViewPager(k);

        }

        @Override
        public void onPageScrollStateChanged(int i) {

        }
    });
}

private void updateViewPager(final int i) {  
    updateButton.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
            data.set(i, "Replaced "+i);         
            myViewPager.getAdapter().notifyDataSetChanged();
        }
    });

}

private class MyViewPagerAdapter extends PagerAdapter {

    private List<String> data;
    private Context ctx;

    public MyViewPagerAdapter(Context ctx, List<String> data) {
        this.ctx = ctx;
        this.data = data;
    }

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

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

    @Override
    public Object instantiateItem(View collection, int position) {          

        TextView view = new TextView(ctx);
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);            
        return view;
    }

    @Override
    public void destroyItem(View collection, int position, Object view) {
         ((ViewPager) collection).removeView((View) view);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

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

    @Override
    public void restoreState(Parcelable arg0, ClassLoader arg1) {
    }

    @Override
    public void startUpdate(View arg0) {
    }

    @Override
    public void finishUpdate(View arg0) {
    }
}
}

내게 효과가 있었던 것은 가는 것이었습니다.viewPager.getAdapter().notifyDataSetChanged();

합니다.getItemPosition

@Override
public int getItemPosition(Object object) {

    if (object instanceof YourViewInViewPagerClass) { 
        YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
        view.setData(data);
    }

    return super.getItemPosition(object);
}

가 있었습니다.return POSITION_NONE트릭은 나에게 충돌을 일으켰기 때문에 옵션이 아니었습니다.)

항상 되돌아가기POSITION_NONE이미 인스턴스화된 모든 페이지의 인스턴스화를 불러오기 때문에 단순하지만 약간 비효율적인 방법입니다.

ArrayPagerAdapter 라이브러리를 만들어 PagerAdapter의 항목을 동적으로 변경했습니다.

이 가 내부적이리의어반가다니환됩터댑로으라이를 반환합니다.POSITION_NONEgetItemPosiition()필요한 경우에만

이 라이브러리를 사용하면 다음과 같이 항목을 동적으로 변경할 수 있습니다.

@Override
protected void onCreate(Bundle savedInstanceState) {
        /** ... **/
    adapter = new MyStatePagerAdapter(getSupportFragmentManager()
                            , new String[]{"1", "2", "3"});
    ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter);
     adapter.add("4");
     adapter.remove(0);
}

class MyPagerAdapter extends ArrayViewPagerAdapter<String> {

    public MyPagerAdapter(String[] data) {
        super(data);
    }

    @Override
    public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) {
        View v = inflater.inflate(R.layout.item_page, container, false);
        ((TextView) v.findViewById(R.id.item_txt)).setText(item);
        return v;
    }
}

Thils 라이브러리는 Fragments로 작성된 페이지도 지원합니다.

이것은 끔찍한 문제이고 저는 간단하고 효율적이며 효과적인 훌륭한 해결책을 제시하게 되어 기쁩니다!

아래를 참조하십시오. 코드는 POSITION_NONE을 반환할 시기를 나타내는 플래그를 사용하여 표시됩니다.

public class ViewPagerAdapter extends PagerAdapter
{
    // Members
    private boolean mForceReinstantiateItem = false;

    // This is used to overcome terrible bug that Google isn't fixing
    // We know that getItemPosition() is called right after notifyDataSetChanged()
    // Therefore, the fix is to return POSITION_NONE right after the notifyDataSetChanged() was called - but only once
    @Override
    public int getItemPosition(Object object)
    {
        if (mForceReinstantiateItem)
        {
            mForceReinstantiateItem = false;
            return POSITION_NONE;
        }
        else
        {
            return super.getItemPosition(object);
        }
    }

    public void setData(ArrayList<DisplayContent> newContent)
    {
        mDisplayContent = newContent;
        mForceReinstantiateItem = true;
        notifyDataSetChanged();
    }

}

이는 서비스(또는 다른 백그라운드 스레드)에서 View 페이지를 업데이트해야 하는 저와 같은 모든 사람을 위한 것으로, 제안서가 제대로 작동하지 않았습니다. 로그를 조금 확인한 후에는 notifyDataSetChanged() 메서드가 반환되지 않습니다.getItemPosition(개체 개체)은 추가 처리 없이 모든 끝이라고 합니다.그런 다음 상위 PagerAdapter 클래스의 문서(하위 클래스의 문서에 없음)에서 "데이터 세트 변경이 메인 스레드에서 발생해야 하며 DataSetChanged()를 알리기 위한 호출로 끝나야 합니다."라는 것을 발견했습니다. 따라서 이 경우의 작업 솔루션은 (FragmentStatePagerAdapter 및 getItemPosition(Object)을 사용하여 POSITION_NONE을 반환하도록 설정했습니다.

그런 다음 DataSetChanged()를 알리기 위한 호출:

runOnUiThread(new Runnable() {
         @Override
         public void run() {
             pager.getAdapter().notifyDataSetChanged();
         }
     });

내 경우에는 내 보기 페이지에 텍스트 보기가 있습니다. 메인 활동에서 단추를 클릭합니다. 해당 텍스트의 색을 변경합니다. 페이지 어댑터를 보고 업데이트합니다.공유 환경설정에서 가져온 색상을 업데이트할 수 있도록 공유 환경설정 및 호출기 어댑터에 색상을 저장했습니다 버튼을 클릭합니다.그래서 다음과 같은 방법으로 viewPager view를 업데이트합니다.

btn_purple.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int color = ContextCompat.getColor(mContext, R.color.colorPrimaryDark2);
                editor.putInt("sahittoFontColor", color);
                editor.apply();
                toNotifyDatasetChanged();
            }
        });

이제 업데이트 방법:

 private  void  toNotifyDatasetChanged (){
        if(viewPager!=null&& pagerAdapter!=null) {
            viewPager.setAdapter(null);
            viewPager.setAdapter(pagerAdapter);
           
        }

    }

그리고 내 호출기 어댑터:

pagerAdapter = new  Sahitto_ViewPagerAdapter (mContext, filenameParameter, 30, lineList);
                        viewPager.setAdapter(pagerAdapter);

그리고 순간적으로항목(PagerAdapter에서):

    SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mContext);
    int   bnfntcolor=settings.getInt("sahittoFontColor", 0);
if (bnfntcolor!=0){
    textView.setTextColor(bnfntcolor);
}

따라서 단추를 누르면 호출기 어댑터의 텍스트 보기에서 색이 즉시 변경됩니다.

해피 코딩.

돌아오는 대신에POSITION_NONE모든 조각을 다시 만들면 여기서 제안한 대로 ViewPager를 동적으로 업데이트할 수 있습니다.

데이터 세트 변경을 알리는 간단한 방법을 만들었다고 생각합니다.

먼저, 인스턴스화 방법을 조금 변경합니다.항목 기능 작동:

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View rootView = mInflater.inflate(...,container, false);
        rootView.setTag(position);
        updateView(rootView, position);
        container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        mViewPager.setObjectForPosition(rootView, position);
        return rootView;
    }

"updateView"의 경우 채울 모든 데이터(setText, setBitmapImage 등)로 보기를 채웁니다.

destroyView가 다음과 같이 작동하는지 확인합니다.

    @Override
    public void destroyItem(final ViewGroup container, final int position, final Object obj) {
        final View viewToRemove = (View) obj;
        mViewPager.removeView(viewToRemove);
    }

이제 데이터를 변경하고, 변경한 다음 PagerAdapter에서 다음 기능을 호출해야 한다고 가정합니다.

    public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos,
            final NotifyLocation toPos) {
        final int offscreenPageLimit = viewPager.getOffscreenPageLimit();
        final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        if (fromPosInt <= toPosInt) {
            notifyDataSetChanged();
            for (int i = fromPosInt; i <= toPosInt; ++i) {
                final View pageView = viewPager.findViewWithTag(i);
                mPagerAdapter.updateView(pageView, i);
            }
        }
    }

public enum NotifyLocation {
    MOST_LEFT, CENTER, MOST_RIGHT
}

예를 들어, viewPager에 의해 표시되는 모든 보기에 무언가가 변경되었음을 알리려면 다음을 호출할 수 있습니다.

notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT);

바로 그겁니다.

언급URL : https://stackoverflow.com/questions/7263291/why-pageradapternotifydatasetchanged-is-not-updating-the-view

반응형