问题描述
我的 ViewPager 和我自己的 FragmentStatePagerAdapter 出现了一些奇怪的行为.
I've been seeing some strange behavior with my ViewPager along with my own FragmentStatePagerAdapter.
我的视图层次结构是这样的:
My View hierarchy goes like this:
-> (1) Fragment root view (RelativeLayout)
-> (2) ViewPager
-> (3) ViewPager's current fragment view
当负责 Fragment 根视图 (1) 的 Fragment 隐藏(在片段事务中使用 .hide())然后显示(使用 .show())时,当前显示在ViewPager (3) 变为 null,尽管片段仍然存在.基本上,我的 ViewPager 变得完全空白/透明.
When the Fragment that is responsible for the Fragment root view (1) gets hidden (using .hide() in a fragment transaction) and then shown (with .show()), the fragment view that was currently showing in the ViewPager (3) becomes null, although the fragment still exists. Basically, my ViewPager becomes completely blank/transparent.
我发现解决这个问题的唯一方法是调用
The only way I have found to fix this is to call
int current = myViewPager.getCurrentItem();
myViewPager.setAdapter(myAdapter);
myViewPager.setCurrentItem(current);
在显示父片段之后.这会以某种方式触发视图被重新创建并出现在屏幕上.不幸的是,这偶尔会导致处理在旧观察者上两次调用 unregisterDataSetObserver()
的寻呼机适配器的异常.
after the parent fragment is shown. This somehow triggers the views to be recreated and appear on screen. Unfortunately, this occasionally causes exceptions dealing with the pager adapter calling unregisterDataSetObserver()
twice on an old observer.
有没有更好的方法来做到这一点?我想我要问的是:
Is there a better way to do this? I guess what I am asking is:
当 ViewPager 的父片段被隐藏时,为什么我的 ViewPager 中的片段视图会被破坏?
更新:当应用程序最小化"然后恢复"(通过按主操作键然后返回)时也会发生这种情况.
Update: this also happens when the application is "minimized" and then "restored" (by pressing the home action key and then returning).
根据请求,这是我的寻呼机适配器类:
Per request, here's my pager adapter class:
public class MyInfoSlidePagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<MyInfo> infos = new ArrayList<MyInfo>();
public MyInfoSlidePagerAdapter (FragmentManager fm) {
super(fm);
}
public MyInfoSlidePagerAdapter (FragmentManager fm, MyInfo[] newInfos) {
super(fm);
setInfos(newInfos);
}
@Override
public int getItemPosition(Object object) {
int position = infos.indexOf(((MyInfoDetailsFragment)object).getMyInfo());
return position > 0 ? position : POSITION_NONE;
}
@Override
public CharSequence getPageTitle(int position) {
return infos.get(position).getName();
}
@Override
public Fragment getItem(int i) {
return infos.size() > 0 ? MyInfoDetailsFragment.getNewInstance(infos.get(i)) : null;
}
@Override
public int getCount() {
return infos.size();
}
public Location getMyInfoAtPosition(int i) {
return infos.get(i);
}
public void setInfos(MyInfo[] newInfos) {
infos = new ArrayList<MyInfo>(Arrays.asList(newInfos));
}
public int getPositionOfMyInfo(MyInfo info) {
return infos.indexOf(info);
}
}
我已经重命名了一些变量,但除此之外,它正是我所拥有的.
I've renamed some variables but other than that it is exactly what I have.
推荐答案
您没有为您的特定问题提供足够的信息,因此我构建了一个示例项目来尝试重现您的问题:该应用程序有一个包含片段(PagerFragment
)在相对布局中,在此布局下方我有一个隐藏 &上面显示了 PagerFragment
.PagerFragment
有一个 ViewPager
并且分页器适配器中的每个片段只显示一个标签 - 这个片段被命名为 DataFragment
.标签列表在父活动中创建并传递给 PagerFragment,然后通过其适配器传递给每个 DataFragment
.更改 PagerFragment
可见性没有任何问题,并且每次它再次变得可见时,它都会显示先前显示的标签.
You're not providing enough info for your specific issue, so I built a sample project that tries to reproduce your issue: the app has an activity that holds a fragment (PagerFragment
) within a relative layout and below this layout I have a button that hides & shows above PagerFragment
. PagerFragment
has a ViewPager
and each fragment within pager adapter simply displays a label - this fragment is named DataFragment
. The label list is created in parent activity and passed to PagerFragment and then through its adapter to each DataFragment
. Changing the PagerFragment
visibility is done with no issues and each time it's becoming visible again it shows the previous shown label.
问题的关键:使用 Fragment#getChildFragmentManager() 当您创建 viewpager 适配器而不是 getFragmentManager 时!
The key of the issue: Use Fragment#getChildFragmentManager() when you're creating the viewpager adapter and not getFragmentManager!
也许您可以将这个简单的项目与您现有的项目进行比较,并检查差异在哪里.所以这里(自上而下):
Maybe you can compare this simple project with what you have and check where are the differences. So here goes (top-down):
PagerActivity(项目中唯一的Activity):
PagerActivity (the only activity in the project):
public class PagerActivity extends FragmentActivity {
private static final String PAGER_TAG = "PagerActivity.PAGER_TAG";
@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.pager_activity);
if (savedInstance == null) {
PagerFragment frag = PagerFragment.newInstance(buildPagerData());
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().add(R.id.layout_fragments, frag, PAGER_TAG).commit();
}
findViewById(R.id.btnFragments).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
changeFragmentVisibility();
}
});
}
private List<String> buildPagerData() {
ArrayList<String> pagerData = new ArrayList<String>();
pagerData.add("Robert de Niro");
pagerData.add("John Smith");
pagerData.add("Valerie Irons");
pagerData.add("Metallica");
pagerData.add("Rammstein");
pagerData.add("Zinedine Zidane");
pagerData.add("Ronaldo da Lima");
return pagerData;
}
protected void changeFragmentVisibility() {
Fragment frag = getSupportFragmentManager().findFragmentByTag(PAGER_TAG);
if (frag == null) {
Toast.makeText(this, "No PAGER fragment found", Toast.LENGTH_SHORT).show();
return;
}
boolean visible = frag.isVisible();
Log.d("APSampler", "Pager fragment visibility: " + visible);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (visible) {
ft.hide(frag);
} else {
ft.show(frag);
}
ft.commit();
getSupportFragmentManager().executePendingTransactions();
}
}
它的布局文件pager_activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp" >
<Button
android:id="@+id/btnFragments"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="Hide/Show fragments" />
<RelativeLayout
android:id="@+id/layout_fragments"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/btnFragments"
android:layout_marginBottom="4dp" >
</RelativeLayout>
</RelativeLayout>
请注意,我在首次显示活动时添加了 PagerFragment
- 以及 PagerFragment
类:
Observe that I am adding the PagerFragment
when the activity is first shown - and the PagerFragment
class:
public class PagerFragment extends Fragment {
private static final String DATA_ARGS_KEY = "PagerFragment.DATA_ARGS_KEY";
private List<String> data;
private ViewPager pagerData;
public static PagerFragment newInstance(List<String> data) {
PagerFragment pagerFragment = new PagerFragment();
Bundle args = new Bundle();
ArrayList<String> argsValue = new ArrayList<String>(data);
args.putStringArrayList(DATA_ARGS_KEY, argsValue);
pagerFragment.setArguments(args);
return pagerFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
data = getArguments().getStringArrayList(DATA_ARGS_KEY);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.pager_fragment, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
pagerData = (ViewPager) view.findViewById(R.id.pager_data);
setupPagerData();
}
private void setupPagerData() {
PagerAdapter adapter = new LocalPagerAdapter(getChildFragmentManager(), data);
pagerData.setAdapter(adapter);
}
}
它的布局(只有全尺寸的 ViewPager):
its layout (only the ViewPager that takes full size):
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager_data"
android:layout_width="match_parent"
android:layout_height="match_parent" />
及其适配器:
public class LocalPagerAdapter extends FragmentStatePagerAdapter {
private List<String> pagerData;
public LocalPagerAdapter(FragmentManager fm, List<String> pagerData) {
super(fm);
this.pagerData = pagerData;
}
@Override
public Fragment getItem(int position) {
return DataFragment.newInstance(pagerData.get(position));
}
@Override
public int getCount() {
return pagerData.size();
}
}
此适配器为每个页面创建一个 DataFragment
:
This adapter creates a DataFragment
for each page:
public class DataFragment extends Fragment {
private static final String DATA_ARG_KEY = "DataFragment.DATA_ARG_KEY";
private String localData;
public static DataFragment newInstance(String data) {
DataFragment df = new DataFragment();
Bundle args = new Bundle();
args.putString(DATA_ARG_KEY, data);
df.setArguments(args);
return df;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
localData = getArguments().getString(DATA_ARG_KEY);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.data_fragment, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
view.findViewById(R.id.btn_page_action).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), localData, Toast.LENGTH_SHORT).show();
}
});
((TextView) view.findViewById(R.id.txt_label)).setText(localData);
}
}
和DataFragment
的布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp" >
<Button
android:id="@+id/btn_page_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="Interogate" />
<TextView
android:id="@+id/txt_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
享受编码!
这篇关于当 ViewPager 的父 Fragment 隐藏然后显示时,ViewPager 的 Fragment 的视图丢失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!