在 ViewPager 中重新加载片段时出现问题
Problems when reloading fragment in ViewPager
我对 ViewPager 中的片段有疑问。在我的应用程序中,我使用 FragmentStatePagerAdapter 实现了 4 个选项卡。
这是我的 SectionsPageAdapter.java
public class SectionsPageAdapter extends FragmentStatePagerAdapter {
private String fragment0 = "";
private Fragment fragAt0;
private FragmentManager manager;
private String[] tabNames;
Context context;
public SectionsPageAdapter(FragmentManager fm, Context ctx, String lastFrag) {
super(fm);
context = ctx;
tabNames = ctx.getResources().getStringArray(R.array.tabs_name);
this.manager = fm;
this.listener = new fragmentChangeListener();
fragment0 = lastFrag;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return ActivityTypeFragment.newInstance(0);
case 1:
return EventsFragment.newInstance();
case 2:
return CalendarFragment.newInstance();
case 3:
if(fragAt0 == null){
switch(fragment0){
case "Chart":
fragAt0 = ChartFragment.newInstance(listener);
break;
case "Hour":
fragAt0 = HourChartFragment.newInstance(listener);
break;
case "Daily":
fragAt0 = DailyChartFragment.newInstance(listener);
break;
default:
fragAt0 = ChartFragment.newInstance(listener);
}
}
return fragAt0;
default:
return ActivityTypeFragment.newInstance(0);
}
}
@Override
public int getCount() {
return 4;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return tabNames[position];
}
@Override
public int getItemPosition(@NonNull Object object) {
if (object instanceof ChartFragment && fragAt0 instanceof DailyChartFragment
|| object instanceof HourChartFragment && fragAt0 instanceof DailyChartFragment)
{
return POSITION_NONE;
}
else if (object instanceof DailyChartFragment && fragAt0 instanceof ChartFragment
|| object instanceof HourChartFragment && fragAt0 instanceof ChartFragment){
return POSITION_NONE;
}
else if (object instanceof HourChartFragment && fragAt0 instanceof ChartFragment)
{
return POSITION_NONE;
}
else if (object instanceof ChartFragment && fragAt0 instanceof HourChartFragment
|| object instanceof DailyChartFragment && fragAt0 instanceof HourChartFragment)
{
return POSITION_NONE;
}
return POSITION_NONE;
}
public interface nextFragmentListener {
void fragment0Changed(String newFragmentIdentification, FragmentManager fragmentManager);
}
private nextFragmentListener listener;
private final class fragmentChangeListener implements nextFragmentListener {
@Override
public void fragment0Changed(String fragment, FragmentManager fm) {
fragment0 = fragment;
manager.beginTransaction().remove(fragAt0).commitNow();
switch (fragment) {
case "Chart":
fragAt0 = ChartFragment.newInstance(listener);
break;
case "Daily":
fragAt0 = DailyChartFragment.newInstance(listener);
break;
case "Hour":
fragAt0 = HourChartFragment.newInstance(listener);
break;
}
SectionsPageAdapter.this.notifyDataSetChanged();
}
}
public String getFragmentName()
{
return fragment0;
}
}
所以在最后一个选项卡中,我有一个片段 - ChartFragment,其中我有 Spinner,可以选择 select 另一个图表。当用户 select 的另一个图表时,这个另一个图表通过调用我在 SectionsPageAdapter 中的方法 fragment0Changed 重新加载到这个选项卡中
Class。我在 .
中找到了这个解决方案
这是我的 onItemSelected 函数,我希望它出现在“图表”选项卡中的每个片段中
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (view != null) {
String selectedMenu = ((TextView) view).getText().toString();
if (selectedMenu == getString(R.string.best_appealing_chart)) {
LoadBestAppealingChart();
} else if (selectedMenu == getString(R.string.hour_chart)) {
chartListener.fragment0Changed("Hour", getActivity().getSupportFragmentManager());
}
else
{
chartListener.fragment0Changed("Daily", getActivity().getSupportFragmentManager());
}
}
else {
LoadBestAppealingChart();
}
}
所以,在我旋转设备之前一切正常,之后 select 另一个图表不起作用。应用抛出异常:
java.lang.IllegalStateException: Fragment host has been destroyed
at android.support.v4.app.FragmentManagerImpl.ensureExecReady(FragmentManager.java:2183)
at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2211)
at android.support.v4.app.BackStackRecord.commitNow(BackStackRecord.java:643).
我已阅读文章 https://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html,but 在我的案例中,我既没有异步方法来替换片段,也没有在 Activity 生命周期方法中调用它。我在微调器的 onSelectionChanged 方法中进行片段交易。我尝试了什么:使用 commitNowAllowingStateLoss() 并没有让事情变得更好,它只是没有抛出异常,片段仍然没有重新加载;将 getSupportFragmentManager() 参数传递给 fragment0Changed 方法也无济于事,但即使我使用简单的 commitNow() 方法,这样做也不会抛出任何异常。但是有帮助的是,在旋转后我选择我的应用程序的第一个或第二个选项卡然后转到我的第四个选项卡(图表选项卡),然后我可以毫无问题地切换(重新加载)我的片段。有趣的是,切换到第三个选项卡(日历选项卡)也无济于事。切换到第三个选项卡并返回到第四个选项卡切换片段后仍然没有重新加载。
我认为在 Android 上与 Fragment 传递侦听器通信是一个常见的错误。 Fragment 上的监听器必须由 the Activity or a child Fragment 实现。所以你必须让你的 Activity 或 Fragment 实现 NextFragmentListener...其次,永远不要在 ViewPager 上引用 Fragment,你正在泄漏旧的 fragment0
public class SubAdapter 扩展了 FragmentStatePagerAdapter
{
/* 只需在 super() 调用中传递行为,这样每次 resume 方法都会调用,这样您就可以重新加载任何您想要的东西
但是你应该使用 subclass extending */
私有字符串tile_list[];
public SubAdapter (FragmentManager fm, String[] tile_list)
{
super(fm,FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.tile_list = tile_list;
}
@Override
public Fragment getItem(int position)
{
Fragment fragment;
switch (position)
{
case 0:
fragment=new new_fragment();
break;
default:
fragment=new new_fragment();
}
return fragment;
}
@Override
public int getCount()
{
return tile_list.length;
}
@Nullable
@Override
public CharSequence getPageTitle(int position)
{
return tile_list[position];
}
}
我对 ViewPager 中的片段有疑问。在我的应用程序中,我使用 FragmentStatePagerAdapter 实现了 4 个选项卡。 这是我的 SectionsPageAdapter.java
public class SectionsPageAdapter extends FragmentStatePagerAdapter {
private String fragment0 = "";
private Fragment fragAt0;
private FragmentManager manager;
private String[] tabNames;
Context context;
public SectionsPageAdapter(FragmentManager fm, Context ctx, String lastFrag) {
super(fm);
context = ctx;
tabNames = ctx.getResources().getStringArray(R.array.tabs_name);
this.manager = fm;
this.listener = new fragmentChangeListener();
fragment0 = lastFrag;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return ActivityTypeFragment.newInstance(0);
case 1:
return EventsFragment.newInstance();
case 2:
return CalendarFragment.newInstance();
case 3:
if(fragAt0 == null){
switch(fragment0){
case "Chart":
fragAt0 = ChartFragment.newInstance(listener);
break;
case "Hour":
fragAt0 = HourChartFragment.newInstance(listener);
break;
case "Daily":
fragAt0 = DailyChartFragment.newInstance(listener);
break;
default:
fragAt0 = ChartFragment.newInstance(listener);
}
}
return fragAt0;
default:
return ActivityTypeFragment.newInstance(0);
}
}
@Override
public int getCount() {
return 4;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return tabNames[position];
}
@Override
public int getItemPosition(@NonNull Object object) {
if (object instanceof ChartFragment && fragAt0 instanceof DailyChartFragment
|| object instanceof HourChartFragment && fragAt0 instanceof DailyChartFragment)
{
return POSITION_NONE;
}
else if (object instanceof DailyChartFragment && fragAt0 instanceof ChartFragment
|| object instanceof HourChartFragment && fragAt0 instanceof ChartFragment){
return POSITION_NONE;
}
else if (object instanceof HourChartFragment && fragAt0 instanceof ChartFragment)
{
return POSITION_NONE;
}
else if (object instanceof ChartFragment && fragAt0 instanceof HourChartFragment
|| object instanceof DailyChartFragment && fragAt0 instanceof HourChartFragment)
{
return POSITION_NONE;
}
return POSITION_NONE;
}
public interface nextFragmentListener {
void fragment0Changed(String newFragmentIdentification, FragmentManager fragmentManager);
}
private nextFragmentListener listener;
private final class fragmentChangeListener implements nextFragmentListener {
@Override
public void fragment0Changed(String fragment, FragmentManager fm) {
fragment0 = fragment;
manager.beginTransaction().remove(fragAt0).commitNow();
switch (fragment) {
case "Chart":
fragAt0 = ChartFragment.newInstance(listener);
break;
case "Daily":
fragAt0 = DailyChartFragment.newInstance(listener);
break;
case "Hour":
fragAt0 = HourChartFragment.newInstance(listener);
break;
}
SectionsPageAdapter.this.notifyDataSetChanged();
}
}
public String getFragmentName()
{
return fragment0;
}
}
所以在最后一个选项卡中,我有一个片段 - ChartFragment,其中我有 Spinner,可以选择 select 另一个图表。当用户 select 的另一个图表时,这个另一个图表通过调用我在 SectionsPageAdapter 中的方法 fragment0Changed 重新加载到这个选项卡中 Class。我在 .
中找到了这个解决方案这是我的 onItemSelected 函数,我希望它出现在“图表”选项卡中的每个片段中
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (view != null) {
String selectedMenu = ((TextView) view).getText().toString();
if (selectedMenu == getString(R.string.best_appealing_chart)) {
LoadBestAppealingChart();
} else if (selectedMenu == getString(R.string.hour_chart)) {
chartListener.fragment0Changed("Hour", getActivity().getSupportFragmentManager());
}
else
{
chartListener.fragment0Changed("Daily", getActivity().getSupportFragmentManager());
}
}
else {
LoadBestAppealingChart();
}
}
所以,在我旋转设备之前一切正常,之后 select 另一个图表不起作用。应用抛出异常:
java.lang.IllegalStateException: Fragment host has been destroyed
at android.support.v4.app.FragmentManagerImpl.ensureExecReady(FragmentManager.java:2183)
at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2211)
at android.support.v4.app.BackStackRecord.commitNow(BackStackRecord.java:643).
我已阅读文章 https://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html,but 在我的案例中,我既没有异步方法来替换片段,也没有在 Activity 生命周期方法中调用它。我在微调器的 onSelectionChanged 方法中进行片段交易。我尝试了什么:使用 commitNowAllowingStateLoss() 并没有让事情变得更好,它只是没有抛出异常,片段仍然没有重新加载;将 getSupportFragmentManager() 参数传递给 fragment0Changed 方法也无济于事,但即使我使用简单的 commitNow() 方法,这样做也不会抛出任何异常。但是有帮助的是,在旋转后我选择我的应用程序的第一个或第二个选项卡然后转到我的第四个选项卡(图表选项卡),然后我可以毫无问题地切换(重新加载)我的片段。有趣的是,切换到第三个选项卡(日历选项卡)也无济于事。切换到第三个选项卡并返回到第四个选项卡切换片段后仍然没有重新加载。
我认为在 Android 上与 Fragment 传递侦听器通信是一个常见的错误。 Fragment 上的监听器必须由 the Activity or a child Fragment 实现。所以你必须让你的 Activity 或 Fragment 实现 NextFragmentListener...其次,永远不要在 ViewPager 上引用 Fragment,你正在泄漏旧的 fragment0
public class SubAdapter 扩展了 FragmentStatePagerAdapter {
/* 只需在 super() 调用中传递行为,这样每次 resume 方法都会调用,这样您就可以重新加载任何您想要的东西 但是你应该使用 subclass extending */
私有字符串tile_list[];
public SubAdapter (FragmentManager fm, String[] tile_list)
{
super(fm,FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.tile_list = tile_list;
}
@Override
public Fragment getItem(int position)
{
Fragment fragment;
switch (position)
{
case 0:
fragment=new new_fragment();
break;
default:
fragment=new new_fragment();
}
return fragment;
}
@Override
public int getCount()
{
return tile_list.length;
}
@Nullable
@Override
public CharSequence getPageTitle(int position)
{
return tile_list[position];
}
}