在需要动态添加和从viewpager中删除列表片段的应用程序中工作,当点击列表中的一个项目时,新的片段会添加到viewpager中,当需要删除swipe back片段时,这工作效果很好,但当我旋转屏幕时,页面中的片段会有两个实例,再旋转一次,实例会增加四倍。
同时,我需要持久化片段的状态(列表位置、加载的项数等),这意味着重写片段中的onsaveinstancestate并将相关数据保存在捆绑包中,以便在重新创建时恢复。我通过清除适配器并调用notifydatasetchanged来解决了双重实例的问题,但由于显而易见的原因没有调用onsaveinstance,因此我会丢失碎片中的保存状态,如果不清除适配器,则只会使实例加倍。我曾在Dropbox app中看到过进出文件夹时同样的行为。
这是我正在使用的自定义寻呼机适配器实现
/**
* Implementation of {@link PagerAdapter} that
* uses a {@link Fragment} to manage each page. This class also handles
* saving and restoring of fragment's state.
*
* <p>This version of the pager is more useful when there are a large number
* of pages, working more like a list view. When pages are not visible to
* the user, their entire fragment may be destroyed, only keeping the saved
* state of that fragment. This allows the pager to hold on to much less
* memory associated with each visited page as compared to
* {@link FragmentPagerAdapter} at the cost of potentially more overhead when
* switching between pages.
*
* <p>When using FragmentPagerAdapter the host ViewPager must have a
* valid ID set.</p>
*
* <p>Subclasses only need to implement {@link #getItem(int)}
* and {@link #getCount()} to have a working adapter. They also should
* override {@link #getItemId(int)} if the position of the items can change.
*/
public abstract class UpdatableFragmentPagerAdapter extends PagerAdapter {
private final FragmentManager fragmentManager;
private final LongSparseArray<Fragment> fragmentList = new LongSparseArray<>();
private final LongSparseArray<Fragment.SavedState> savedStatesList = new LongSparseArray<>();
@Nullable private FragmentTransaction currentTransaction = null;
@Nullable private Fragment currentPrimaryItem = null;
public UpdatableFragmentPagerAdapter(@NonNull FragmentManager fm) {
this.fragmentManager = fm;
}
/**
* Return the Fragment associated with a specified position.
*/
public abstract Fragment getItem(int position);
@Override public void startUpdate(@NonNull ViewGroup container) {
if (container.getId() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this + " requires a view id");
}
}
@Override @NonNull public Object instantiateItem(ViewGroup container, int position) {
long tag = getItemId(position);
Fragment fragment = fragmentList.get(tag);
// 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.
if (fragment != null) {
return fragment;
}
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
fragment = getItem(position);
// restore state
final Fragment.SavedState savedState = savedStatesList.get(tag);
if (savedState != null) {
fragment.setInitialSavedState(savedState);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
fragmentList.put(tag, fragment);
currentTransaction.add(container.getId(), fragment, "f" + tag);
return fragment;
}
@Override public void destroyItem(ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment) object;
int currentPosition = getItemPosition(fragment);
int index = fragmentList.indexOfValue(fragment);
long fragmentKey = -1;
if (index != -1) {
fragmentKey = fragmentList.keyAt(index);
fragmentList.removeAt(index);
}
//item hasn't been removed
if (fragment.isAdded() && currentPosition != POSITION_NONE) {
savedStatesList.put(fragmentKey, fragmentManager.saveFragmentInstanceState(fragment));
} else {
savedStatesList.remove(fragmentKey);
}
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
currentTransaction.remove(fragment);
}
@Override public void setPrimaryItem(ViewGroup container, int position, @Nullable Object object) {
Fragment fragment = (Fragment) object;
if (fragment != currentPrimaryItem) {
if (currentPrimaryItem != null) {
currentPrimaryItem.setMenuVisibility(false);
currentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
currentPrimaryItem = fragment;
}
}
@Override public void finishUpdate(ViewGroup container) {
if (currentTransaction != null) {
currentTransaction.commitNowAllowingStateLoss();
currentTransaction = null;
}
}
@Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return ((Fragment) object).getView() == view;
}
@Override public Parcelable saveState() {
Bundle state = null;
if (savedStatesList.size() > 0) {
// save Fragment states
state = new Bundle();
long[] stateIds = new long[savedStatesList.size()];
for (int i = 0; i < savedStatesList.size(); i++) {
Fragment.SavedState entry = savedStatesList.valueAt(i);
stateIds[i] = savedStatesList.keyAt(i);
state.putParcelable(Long.toString(stateIds[i]), entry);
}
state.putLongArray("states", stateIds);
}
for (int i = 0; i < fragmentList.size(); i++) {
Fragment f = fragmentList.valueAt(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + fragmentList.keyAt(i);
fragmentManager.putFragment(state, key, f);
}
}
return state;
}
@Override public void restoreState(@Nullable Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
long[] fss = bundle.getLongArray("states");
savedStatesList.clear();
fragmentList.clear();
if (fss != null) {
for (long fs : fss) {
savedStatesList.put(fs, bundle.getParcelable(Long.toString(fs)));
}
}
Iterable<String> keys = bundle.keySet();
for (String key : keys) {
if (key.startsWith("f")) {
Fragment f = fragmentManager.getFragment(bundle, key);
if (f != null) {
f.setMenuVisibility(false);
fragmentList.put(Long.parseLong(key.substring(1)), f);
} else {
Timber.w("Bad fragment at key %s", key);
}
}
}
}
}
/**
* Return a unique identifier for the item at the given position.
* <p>
* <p>The default implementation returns the given position.
* Subclasses should override this method if the positions of items can change.</p>
*
* @param position Position within this adapter
* @return Unique identifier for the item at position
*/
public long getItemId(int position) {
return position;
}
}
这是适配器的实现
class FolderPagerAdapter extends UpdatableFragmentPagerAdapter {
private final FragmentManager fragmentManager;
// Sparse array to keep track of registered fragments in memory
private List<Fragment> addedFragments;
FolderPagerAdapter(FragmentManager fm) {
super(fm);
this.fragmentManager = fm;
}
void init() {
if (addedFragments == null) {
addedFragments = new ArrayList<>();
}
addedFragments.clear();
addedFragments.add(CollectionsListFragment.newInstance());
notifyDataSetChanged();
}
@Override public Fragment getItem(int position) {
return addedFragments.get(position);
}
@Override public long getItemId(int position) {
return addedFragments.get(position).hashCode();
}
@Override public int getCount() {
return addedFragments.size();
}
//this is called when notifyDataSetChanged() is called
@Override public int getItemPosition(Object object) {
//// refresh all fragments when data set changed
Fragment fragment = (Fragment) object;
if (fragment instanceof CollectionFragment) {
return POSITION_UNCHANGED;
} else {
int hashCode = fragment.hashCode();
for (int i = 0; i < addedFragments.size(); i++) {
if (addedFragments.get(i).hashCode() == hashCode) {
return i;
}
}
}
return PagerAdapter.POSITION_NONE;
}
void removeLastPage() {
addedFragments.remove(addedFragments.size() - 1);
notifyDataSetChanged();
}
void addCollectionFragment(CollectionFragment collectionFragment) {
addedFragments.add(collectionFragment);
notifyDataSetChanged();
}
void addFolderFragment(FolderFragment folderFragment) {
addedFragments.add(folderFragment);
notifyDataSetChanged();
}
void restoreFragments(List<PagerFolderCollectionModel> pagesList) {
if (!pagesList.isEmpty()) {
for (int i = 0; i < pagesList.size(); i++) {
if (i == 0) {
addedFragments.add(CollectionFragment.newInstance(pagesList.get(0).getItemId()));
} else {
addedFragments.add(FolderFragment.newInstance(pagesList.get(i).getItemName()));
}
}
notifyDataSetChanged();
}
}
void removeAll() {
addedFragments.clear();
notifyDataSetChanged();
}
}
和一个持有者pojo,我正在使用它保存在activity的onsaveinstancestate中,并在循环时恢复
public class PagerFolderCollectionModel implements Parcelable {
public static final Parcelable.Creator<PagerFolderCollectionModel> CREATOR =
new Parcelable.Creator<PagerFolderCollectionModel>() {
@Override public PagerFolderCollectionModel createFromParcel(Parcel source) {
return new PagerFolderCollectionModel(source);
}
@Override public PagerFolderCollectionModel[] newArray(int size) {
return new PagerFolderCollectionModel[size];
}
};
private String itemId;
private String itemName;
public PagerFolderCollectionModel(String itemId, String itemName) {
this.itemId = itemId;
this.itemName = itemName;
}
protected PagerFolderCollectionModel(Parcel in) {
this.itemId = in.readString();
this.itemName = in.readString();
}
public String getItemId() {
return itemId;
}
public String getItemName() {
return itemName;
}
@Override public int describeContents() {
return 0;
}
@Override public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.itemId);
dest.writeString(this.itemName);
}
}
activity中的onsaveinstance方法
@Override protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_SELECTED_OPTION, selectedDrawerOption);
outState.putBoolean(STATE_SHOW_GRID_OPTION, isShowGridOption);
outState.putParcelableArrayList(STATE_SHOWN_FRAGMENTS,
(ArrayList<PagerFolderCollectionModel>) adapteritemslist);
Timber.e("save");
}
要求是适配器中的第一项始终是集合片段,文件夹片段按需添加和删除(点击或向后滑动)
对此有解决方案吗(以不同的方式实现寻呼机适配器,在适配器中使用自定义视图...)?有没有人知道Dropbox应用程序是怎么做到这一点的?
请尝试在片段的onCreateView方法中使用setRetainInstance(布尔保留)。将其设置为true。它控制片断实例是否在activity重新创建过程中保留(例如从配置更改)。
我有个问题。我有一个片段,里面包含了viewpager。对于viewpager,我使用FragmentPagerAdapter扩展。每个寻呼机片段内部都有回收器视图。问题是,在
我使用和来显示片段。最初增加了2个片段,Fragment1和Fragment2,在接收到服务器的响应后,在第二个位置增加了第三个片段Fragment3。因此,在添加所有页面之后,这应该是ViewPager中的片段序列-->Fragment1,Fragment3,fragment2。 下面是解决方案代码 代码:
我试图探索Android框架是如何管理碎片的,通过我的研究,我了解了很多关于碎片的新知识,但我有一次陷入了困境,无法理解这是如何发生的。 请先试着理解我的场景。它是这样的:我有一个活动,一个接一个地添加两个片段。首次加载活动时,使用以下代码将片段A附加到该活动: 片段A的这些回调方法在加载时被调用 FirstDummyFragment:onCreate:savedInstanceState---
让我们称它们为片段A和B。片段B只是A的一个详细视图,它在点击片段A中的按钮时替换了片段A。 更换代码: 当我现在旋转片段B中的屏幕一次并按下后,旧片段A会毫无问题地恢复(它在由savedInstanceState包创建的OnActivityCreate中恢复其状态)。 现在到了有趣的部分... 当我在片段B中多次旋转屏幕并按下Back键时,我得到一个NullPointerException,因为
您好,我创建了一个带有默认“导航抽屉活动”的项目。所以我有一个MainActivity,其中有一个片段,用于替换菜单上的每个项目。 菜单之一是“客户”,显示客户列表。 从客户片段中我可以看到这个客户的利益,其中是一个调用利益的片段。 还有更多的层次,但要简短,这就足够了。 这是MainActivity上的代码,我使用它从片段中调用片段并在之间传递数据 我用的是: 问题是,当我旋转手机时,无论我的活
我搜索了很多信息,但没有一个明确的。有人能给出一个明确的解决方案和一个例子吗?