안드로이드의 ViewPager에서 조각 페이지 제거
ViewPager에서 Fragments를 동적으로 추가하고 제거하려고하는데 아무런 문제없이 작품을 추가하지만 제거가 예상대로 작동하지 않습니다.
현재 항목을 제거하려고 할 때마다 마지막 항목이 제거됩니다.
또한 FragmentStatePagerAdapter를 사용하거나 어댑터의 getItemPosition 메소드에서 POSITION_NONE을 리턴하려고 시도했습니다.
내가 뭘 잘못하고 있죠?
기본 예는 다음과 같습니다.
MainActivity.java
public class MainActivity extends FragmentActivity implements TextProvider {
private Button mAdd;
private Button mRemove;
private ViewPager mPager;
private MyPagerAdapter mAdapter;
private ArrayList<String> mEntries = new ArrayList<String>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEntries.add("pos 1");
mEntries.add("pos 2");
mEntries.add("pos 3");
mEntries.add("pos 4");
mEntries.add("pos 5");
mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mPager = (ViewPager) findViewById(R.id.pager);
mAdd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
addNewItem();
}
});
mRemove.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
removeCurrentItem();
}
});
mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);
mPager.setAdapter(mAdapter);
}
private void addNewItem() {
mEntries.add("new item");
mAdapter.notifyDataSetChanged();
}
private void removeCurrentItem() {
int position = mPager.getCurrentItem();
mEntries.remove(position);
mAdapter.notifyDataSetChanged();
}
@Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
@Override
public int getCount() {
return mEntries.size();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
@Override
public int getCount() {
return mProvider.getCount();
}
}
}
TextProvider.java
public interface TextProvider {
public String getTextForPosition(int position);
public int getCount();
}
MyFragment.java
public class MyFragment extends Fragment {
private String mText;
public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment(text);
return f;
}
public MyFragment() {
}
public MyFragment(String text) {
this.mText = text;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment, container, false);
((TextView) root.findViewById(R.id.position)).setText(mText);
return root;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add new item" />
<Button
android:id="@+id/remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove current item" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/position"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="35sp" />
</LinearLayout>
Louth의 솔루션은 기존 조각이 파괴되지 않았기 때문에 나를 위해 일하기에 충분하지 않았습니다. 이 답변에 동기를 부여 하여 솔루션 의 예상 위치가 변경 될 때마다 새로운 고유 ID를 제공하는 getItemId(int position)
방법 을 재정의하는 것이 해결책이라는 것을 알았습니다 FragmentPagerAdapter
.
소스 코드:
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
private long baseId = 0;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
@Override
public int getCount() {
return mProvider.getCount();
}
//this is called when notifyDataSetChanged() is called
@Override
public int getItemPosition(Object object) {
// refresh all fragments when data set changed
return PagerAdapter.POSITION_NONE;
}
@Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}
/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
* @param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
}
예를 들어, 단일 탭을 삭제하거나 순서를 변경 한 경우을 호출 notifyChangeInPosition(1)
하기 전에 전화 를 걸어 notifyDataSetChanged()
모든 조각을 다시 작성해야합니다.
이 솔루션이 작동하는 이유
getItemPosition () 재정의 :
경우 notifyDataSetChanged()
라고, 어댑터는 호출 notifyChanged()
의 방법 ViewPager
이 부착되어 있습니다. 그런 ViewPager
다음 getItemPosition()
각 항목에 대해 어댑터가 반환 한 값을 확인하여 반환되는 항목을 제거하고 POSITION_NONE
( 소스 코드 참조 ) 다시 채 웁니다.
getItemId () 재정의 :
이것은 ViewPager
다시 채워질 때 어댑터가 이전 조각을 다시로드하지 못하도록하기 위해 필요합니다 . 에서 instantiateItem () 의 소스 코드 를 보면 이것이 왜 작동하는지 쉽게 이해할 수 있습니다 FragmentPagerAdapter
.
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
보시다시피 getItem()
조각 관리자가 동일한 ID를 가진 기존 조각을 찾지 못한 경우에만 메서드가 호출됩니다. 나에게 그것은 오래된 조각이 여전히 notifyDataSetChanged()
호출 된 후에도 첨부 된 버그처럼 보이지만에 대한 설명서 ViewPager
에는 다음과 같이 명시되어 있습니다.
이 클래스는 현재 초기 설계 및 개발 단계에 있습니다. API는 이후 버전의 호환성 라이브러리에서 변경 될 수 있으며, 최신 버전에 대해 컴파일 될 때 앱의 소스 코드를 변경해야합니다.
따라서 향후 버전의 지원 라이브러리에서는 여기에 제공된 해결 방법이 필요하지 않기를 바랍니다.
ViewPager는 위의 코드를 사용하여 프래그먼트를 제거하지 않습니다. 여러 뷰 (또는 귀하의 경우 프래그먼트)를 메모리에로드하기 때문입니다. 가시적 뷰 외에도 가시적 뷰의 양쪽에 뷰를로드합니다. 이것은 ViewPager를 매우 시원하게 만드는 뷰 간 부드러운 스크롤을 제공합니다.
원하는 효과를 얻으려면 몇 가지 작업을 수행해야합니다.
FragmentPagerAdapter를 FragmentStatePagerAdapter로 변경하십시오. 그 이유는 FragmentPagerAdapter가로드하는 모든 뷰를 메모리에 영원히 유지하기 때문입니다. FragmentStatePagerAdapter가 현재 및 통과 가능한보기를 벗어나는보기를 처리하는 위치입니다.
어댑터 메소드 getItemPosition을 대체하십시오 (아래 참조).
mAdapter.notifyDataSetChanged();
ViewPager 를 호출 하면 어댑터에 질문하여 위치 지정 측면에서 변경된 사항을 확인합니다. 이 방법을 사용하면 모든 것이 변경되었다고 말하고 모든 뷰 위치를 다시 처리하십시오.
그리고 여기 코드가 있습니다 ...
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
@Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
}
뷰 호출기에서 조각 페이지를 제거하는 작업 솔루션
public class MyFragmentAdapter extends FragmentStatePagerAdapter {
private ArrayList<ItemFragment> pages;
public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
super(fragmentManager);
this.pages = pages;
}
@Override
public Fragment getItem(int index) {
return pages.get(index);
}
@Override
public int getCount() {
return pages.size();
}
@Override
public int getItemPosition(Object object) {
int index = pages.indexOf (object);
if (index == -1)
return POSITION_NONE;
else
return index;
}
}
색인으로 일부 페이지를 제거해야 할 때이 작업을 수행합니다.
pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter
여기 내 어댑터 초기화입니다
MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
프래그먼트는 이미 제거되었지만 문제는 viewpager 저장 상태였습니다.
시험
myViewPager.setSaveFromParentEnabled(false);
아무것도 효과가 없었지만 문제가 해결되었습니다!
건배!
소스 코드를에서 android.support.v4.app.FragmentPagerAdpater
라는 사용자 정의 클래스로 복사하는 아이디어를 얻었습니다 CustumFragmentPagerAdapter
. 이렇게 instantiateItem(...)
하면 호출 할 때마다 getItem()
메서드 에서받은 새로운 조각을 추가하기 전에 현재 연결된 조각을 제거 / 파괴 할 수 있습니다 .
instantiateItem(...)
다음과 같이 간단하게 수정하십시오 .
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
// remove / destroy current fragment
if (fragment != null) {
mCurTransaction.remove(fragment);
}
// get new fragment and add it
fragment = getItem(position);
mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
더 나은 두 가지를 결합 할 수 있습니다.
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
@Override
public int getItemPosition(Object object){
if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
return PagerAdapter.POSITION_NONE;
}
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.
}
내 최종 버전 코드는 모든 버그를 수정했습니다. 3 일이 걸렸다
public class FragmentStatePagerAdapterFix extends PagerAdapter {
private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
private static final boolean DEBUG = false;
private WeakReference<FragmentActivity> wrFragmentActivity;
private WeakReference<Fragment> wrParentFragment;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
protected ArrayList<Fragment> mFragments = new ArrayList<>();
protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
protected ArrayList<String> mFragmentTags = new ArrayList<>();
protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();
private Fragment mCurrentPrimaryItem = null;
private boolean[] mTempPositionChange;
@Override
public int getCount() {
return mFragmentClassNames.size();
}
public FragmentActivity getFragmentActivity() {
return wrFragmentActivity.get();
}
public Fragment getParentFragment() {
return wrParentFragment.get();
}
public FragmentStatePagerAdapterFix(FragmentActivity activity) {
mFragmentManager = activity.getSupportFragmentManager();
wrFragmentActivity = new WeakReference<>(activity);
wrParentFragment = new WeakReference<>(null);
}
public FragmentStatePagerAdapterFix(Fragment fragment) {
mFragmentManager = fragment.getChildFragmentManager();
wrFragmentActivity = new WeakReference<>(fragment.getActivity());
wrParentFragment = new WeakReference<>(fragment);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
add(fragClass, null, null);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
add(fragClass, args, null);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
add(fragClass, null, tag);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
add(fragClass, args, tag, getCount());
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
mFragments.add(position, null);
mFragmentStates.add(position, null);
mFragmentTags.add(position, tag);
mFragmentClassNames.add(position, fragClass.getName());
mFragmentArgs.add(position, args);
mTempPositionChange = new boolean[getCount()];
}
public void remove(int position) {
if (position < getCount()) {
mTempPositionChange = new boolean[getCount()];
for (int i = position; i < mTempPositionChange.length; i++) {
mTempPositionChange[i] = true;
}
mFragments.remove(position);
mFragmentStates.remove(position);
mFragmentTags.remove(position);
mFragmentClassNames.remove(position);
mFragmentArgs.remove(position);
}
}
public void clear(){
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
}
@Override
public void startUpdate(ViewGroup container) {
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment;
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
fragment = mFragments.get(position);
if (fragment != null) {
return fragment;
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
FragmentState fs = mFragmentStates.get(position);
if (fs != null) {
fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
// Fix bug
// http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
if (fragment.mSavedFragmentState != null) {
fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
}
}
if (fragment == null) {
fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
}
if (DEBUG) {
Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mFragmentStates.set(position, null);
mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) {
Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment) object).getView());
}
if (position < getCount()) {
FragmentState fragmentState = new FragmentState(fragment);
Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
if (savedState != null) {
fragmentState.mSavedFragmentState = savedState.mState;
}
mFragmentStates.set(position, fragmentState);
mFragments.set(position, null);
}
mCurTransaction.remove(fragment);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
// Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
for (Fragment fragment : mFragments) {
if (fragment != null) {
fixActiveFragment(mFragmentManager, fragment);
}
}
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
@Override
public Parcelable saveState() {
Bundle state = null;
// 目前顯示的 Fragments
for (int i = 0; i < mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
if (mFragmentStates.size() > 0) {
if (state == null) {
state = new Bundle();
}
FragmentState[] fs = new FragmentState[mFragmentStates.size()];
mFragmentStates.toArray(fs);
state.putParcelableArray("states_fragment", fs);
}
return state;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
Parcelable[] fs = bundle.getParcelableArray("states_fragment");
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
if (fs != null) {
for (int i = 0; i < fs.length; i++) {
FragmentState fragmentState = (FragmentState) fs[i];
mFragmentStates.add(fragmentState);
if (fragmentState != null) {
mFragmentArgs.add(fragmentState.mArguments);
mFragmentTags.add(fragmentState.mTag);
mFragmentClassNames.add(fragmentState.mClassName);
} else {
mFragmentArgs.add(null);
mFragmentTags.add(null);
mFragmentClassNames.add(null);
}
mFragments.add(null);
}
}
Iterable<String> keys = bundle.keySet();
for (String key : keys) {
if (key.startsWith("f")) {
int index = Integer.parseInt(key.substring(1));
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
f.setMenuVisibility(false);
mFragments.set(index, f);
mFragmentArgs.set(index, f.mArguments);
mFragmentTags.set(index, f.mTag);
mFragmentClassNames.set(index, f.getClass().getName());
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
// If restore will change
notifyDataSetChanged();
}
}
public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
if (fm.mActive != null) {
int index = fragment.mIndex;
Fragment origin = fm.mActive.get(index);
if (origin != null) {
if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
Log.e(TAG,
String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
origin.getClass().getName(), origin.mIndex,
fragment.getClass().getName(), fragment.mIndex
));
}
}
fm.mActive.set(index, fragment);
}
}
// Fix
// http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
@Override
public int getItemPosition(Object object) {
int index = mFragments.indexOf(object);
if (index < 0) {
return PagerAdapter.POSITION_NONE;
}
boolean isPositionChange = mTempPositionChange[index];
int result = PagerAdapter.POSITION_UNCHANGED;
if (isPositionChange) {
result = PagerAdapter.POSITION_NONE;
}
return result;
}
}
Louth의 대답은 잘 작동합니다. 그러나 항상 POSITION_NONE을 반환한다고 생각하지는 않습니다. POSITION_NONE은 프래그먼트가 파괴되고 새로운 프래그먼트가 생성됨을 의미합니다. ViewPager의 소스 코드에있는 dataSetChanged 함수에서이를 확인할 수 있습니다.
if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
... not related code
mAdapter.destroyItem(this, ii.position, ii.object);
따라서 weakReference의 배열 목록을 사용하여 만든 모든 조각을 저장하는 것이 좋습니다. 그리고 페이지를 추가하거나 제거하면 자신의 배열 목록에서 올바른 위치를 얻을 수 있습니다.
public int getItemPosition(Object object) {
for (int i = 0; i < mFragmentsReferences.size(); i ++) {
WeakReference<Fragment> reference = mFragmentsReferences.get(i);
if (reference != null && reference.get() != null) {
Fragment fragment = reference.get();
if (fragment == object) {
return i;
}
}
}
return POSITION_NONE;
}
주석에 따르면, 호스트보기가 항목의 위치가 변경되었는지 판별하려고 시도 할 때 getItemPosition이 호출됩니다. 그리고 반환 값은 새로운 위치를 의미합니다.
그러나 이것은 충분하지 않습니다. 우리는 여전히 중요한 조치를 취해야합니다. FragmentStatePagerAdapter의 소스 코드에는 "mFragments"라는 배열이 있으며 파괴되지 않은 조각을 캐시합니다. 그리고 instantiateItem 함수에서.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
캐시 된 조각이 null이 아닌 것을 발견하면 캐시 된 조각을 직접 반환했습니다. 문제가 있습니다. 예를 들어, 위치 2에서 한 페이지를 삭제하겠습니다. 먼저 자체 참조 배열 목록에서 해당 조각을 제거합니다. 따라서 getItemPosition에서는 해당 조각에 대해 POSITION_NONE을 반환 한 다음 해당 조각이 "mFragments"에서 제거 및 제거됩니다.
mFragments.set(position, null);
이제 위치 3의 프래그먼트가 위치 2에있게됩니다. 그리고 매개 변수 위치 3을 가진 instantiatedItem이 호출됩니다. 현재 "mFramgents"의 세 번째 항목은 null이 아니므로 직접 반환됩니다. 그러나 실제로 반환 된 것은 위치 2의 조각입니다. 따라서 3 페이지로 넘어 가면 빈 페이지가 나타납니다.
이 문제를 해결하려면 필자는 FragmentStatePagerAdapter의 소스 코드를 자신의 프로젝트에 복사 할 수 있으며 작업을 추가하거나 제거 할 때 "mFragments"배열 목록에서 요소를 추가하고 제거해야합니다.
FragmentStatePagerAdapter 대신 PagerAdapter를 사용하면 상황이 더 간단 해집니다. 행운을 빕니다.
viewpager에서 조각을 동적으로 추가하거나 제거하십시오.
활동 시작시 setupViewPager (viewPager)를 호출하십시오. 다른 조각 호출을로드하려면 setupViewPagerCustom (viewPager)을 호출하십시오. 예를 들어 버튼 클릭 호출시 : setupViewPagerCustom (viewPager);
private void setupViewPager(ViewPager viewPager)
{
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new fragmnet1(), "HOME");
adapter.addFrag(new fragmnet2(), "SERVICES");
viewPager.setAdapter(adapter);
}
private void setupViewPagerCustom(ViewPager viewPager)
{
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new fragmnet3(), "Contact us");
adapter.addFrag(new fragmnet4(), "ABOUT US");
viewPager.setAdapter(adapter);
}
//Viewpageradapter, handles the views
static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager){
super(manager);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
public void addFrag(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
@Override
public CharSequence getPageTitle(int position){
return mFragmentTitleList.get(position);
}
}
에 문제가 FragmentStatePagerAdapter
있었습니다. 항목을 제거한 후 :
- 위치에 사용 된 다른 항목이있었습니다 (위치에 속하지 않고 그 옆에있는 항목).
- 또는 일부 조각이로드되지 않았습니다 (해당 페이지에 빈 배경 만 표시됨)
많은 실험을 한 후 다음과 같은 해결책을 찾았습니다.
public class SomeAdapter extends FragmentStatePagerAdapter {
private List<Item> items = new ArrayList<Item>();
private boolean removing;
@Override
public Fragment getItem(int position) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
// use items.get(position) to configure fragment
fragment.setArguments(args);
return fragment;
}
@Override
public int getCount() {
return items.size();
}
@Override
public int getItemPosition(Object object) {
if (removing) {
return PagerAdapter.POSITION_NONE;
}
Item item = getItemOfFragment(object);
int index = items.indexOf(item);
if (index == -1) {
return POSITION_NONE;
} else {
return index;
}
}
public void addItem(Item item) {
items.add(item);
notifyDataSetChanged();
}
public void removeItem(int position) {
items.remove(position);
removing = true;
notifyDataSetChanged();
removing = false;
}
}
이 솔루션은 아이템을 제거 할 때만 해킹을 사용합니다. 그렇지 않으면 (예 : 항목을 추가 할 때) 원본 코드의 청결도와 성능이 유지됩니다.
물론 어댑터 외부에서 addItem / removeItem 만 호출하고 notifyDataSetChanged ()를 호출 할 필요는 없습니다.
이 솔루션을 사용해보십시오. 바인딩 뷰에 데이터 바인딩을 사용했습니다. 일반적인 "findViewById ()"함수를 사용할 수 있습니다.
public class ActCPExpense extends BaseActivity implements View.OnClickListener, {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;
private ViewPagerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.act_cp_loss);
mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
mContext = ActCPExpense.this;
initViewsAct();
} catch (Exception e) {
LogUtils.LOGE(TAG, e);
}
}
private void initViewsAct() {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
mBinding.viewpager.setAdapter(adapter);
mBinding.tab.setViewPager(mBinding.viewpager);
}
@Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
int i = itemActUtility.getItemId();
if (i == android.R.id.home) {
onBackPressed();
}
return super.onOptionsItemSelected(itemActUtility);
}
@Override
public void onClick(View view) {
super.onClick(view);
int id = view.getId();
if (id == R.id.btnAdd) {
addFragment();
} else if (id == R.id.btnDelete) {
removeFragment();
}
}
private void addFragment(){
adapter.addFragment(FragmentCPPayee.newInstance("Title");
adapter.notifyDataSetChanged();
mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
adapter.removeItem(mBinding.viewpager.getCurrentItem());
mBinding.tab.setViewPager(mBinding.viewpager);
}
class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
@Override
public int getItemPosition(@NonNull Object object) {
return PagerAdapter.POSITION_NONE;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public void removeItem(int pos) {
destroyItem(null, pos, mFragmentList.get(pos));
mFragmentList.remove(pos);
mFragmentTitleList.remove(pos);
adapter.notifyDataSetChanged();
mBinding.viewpager.setCurrentItem(pos - 1, false);
}
@Override
public CharSequence getPageTitle(int position) {
return "Title " + String.valueOf(position + 1);
}
}
}
"clearFragments"기능을 추가했으며 새 조각을 설정하기 전에 해당 기능을 사용하여 어댑터를 지 웁니다. 이것은 조각의 적절한 제거 작업을 호출합니다. 내 pagerAdapter 클래스 :
private class ChartPagerAdapter extends FragmentPagerAdapter{
private ArrayList<Fragment> fragmentList;
ChartPagerAdapter(FragmentManager fm){
super(fm);
fragmentList = new ArrayList<>();
}
void setFragments(ArrayList<? extends Fragment> fragments){
fragmentList.addAll(fragments);
}
void clearFragments(){
for(Fragment fragment:fragmentList)
getChildFragmentManager().beginTransaction().remove(fragment).commit();
fragmentList.clear();
}
@Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
이 단계 로이 문제를 해결했습니다.
1- FragmentPagerAdapter 사용
각 조각에서 2는 임의의 ID를 만듭니다.
fragment.id = new Random().nextInt();
3 어댑터에서 getItemPosition을 대체합니다.
@Override
public int getItemPosition(@NonNull Object object) {
return PagerAdapter.POSITION_NONE;
}
어댑터의 4 개 getItemId 대체
@Override
public long getItemId(int position) {
return mDatasetFragments.get(position).id;
}
5- 이제 코드 삭제가
adapter.mDatasetFragments.remove(< item to delete position >);
adapter.notifyDataSetChanged();
이것은 나를 위해 일했다 나는 도움을 바란다
미래의 독자들을 위해!
이제 ViewPager2 를 사용 하여 viewpager에서 조각을 동적으로 추가하고 제거 할 수 있습니다 .
인용 양식 API 참조
ViewPager2는 ViewPager를 대체하여 오른쪽에서 왼쪽으로 레이아웃 지원, 수직 방향, 수정 가능한 조각 모음 등을 포함하여 이전 버전의 대부분의 어려움을 해결합니다.
봐 가지고 MutableCollectionFragmentActivity.kt
에서 googlesample / 안드로이드 - viewpager2을 , 부가 viewpager에서 동적 조각을 제거하는 예를 들어.
귀하의 정보를 위해 :
조항:
샘플 저장소 : https://github.com/googlesamples/android-viewpager2
destroyItem
메소드를 무시할 수 있습니다.
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
이것이 당신이 원하는 것을 도울 수 있기를 바랍니다.
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
@Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_UNCHANGED;
}
}
참고 URL : https://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
'Programing' 카테고리의 다른 글
T-SQL에서 문자열의 마지막 문자를 제거 하시겠습니까? (0) | 2020.06.28 |
---|---|
연관 PHP 배열에서 첫 번째 항목을 얻는 방법은 무엇입니까? (0) | 2020.06.28 |
Android의 ImageButton에 이미지 맞추기 (0) | 2020.06.28 |
파이썬에서 행렬 전치 (0) | 2020.06.28 |
PHP json_encode 인코딩 숫자를 문자열로 (0) | 2020.06.28 |