【问题标题】:Viewpager fragments not shown after the first time第一次后未显示 Viewpager 片段
【发布时间】:2015-07-24 09:48:38
【问题描述】:

我有一个包含 3 个片段(A、B、C)的活动。片段 A 由一个 ViewPager 和 2 个 ListFragments 组成。用户可以点击任何列表片段中的项目,然后转到片段 B。

在片段 A 我做:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager());
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragmentA, container, false);

    vpPager = (ViewPager)view.findViewById(R.id.vpPager);
    vpPager.setOffscreenPageLimit(2);
    vpPager.setAdapter(pagerAdapter);
    vpPager.addOnPageChangeListener(this);

    return view;
}

PagerAdapter如下:

private class PagerAdapter extends FragmentPagerAdapter {
    private final ListFragment1 lf1 = ListFragment1 .newInstance();
    private final ListFragment2 lf2 = ListFragment2 .newInstance();

    public PagerAdapter(android.support.v4.app.FragmentManager fm) {
        super(fm);
    }

    @Override
    public android.support.v4.app.Fragment getItem(int position) {
        switch (position) {
            case 0:  return lf1;
            case 1:  return lf2;
            default: return null;
        }
    }
}

第一次显示活动时,viewpager 列表片段正确显示。

2 个 viewpager 片段从数据库加载数据,我只执行一次(在创建片段时)。

用户可以点击一个项目并显示片段 B。如果用户按下返回,则显示片段 A。但是列表片段没有显示(它们的一个实例仍然存在)。

会不会是视图已经被销毁了,即使实例存在?

这里有什么问题?有更好的方法吗?

编辑

如果我在寻呼机适配器中使用newInstance,我会得到一个IllegalStateException: not attached to activity。这是因为我按如下方式启动了一个异步任务:

@Override
public void onPageSelected(int position) {
    Fragment fragment = pagerAdapter.getItem(position);
    if (fragment instanceof IPagedFragment) {
        ((IPagedFragment) fragment).onShown();
    }
}

onShown 是:

@Override
public void onShown() {
    myTask= new MyTask();
    myTask.execute((Void)null);
}

我什么时候可以开始任务,以便我可以 100% 确定片段已附加到活动并且已创建视图(我需要从布局中获取列表视图等)。

【问题讨论】:

    标签: android android-fragments android-viewpager


    【解决方案1】:

    您必须使用ChildFragmentManager,如下所示。

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        pagerAdapter = new PagerAdapter(getChildFragmentManager()); //here used child fragment manager 
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragmentA, container, false);
    
        vpPager = (ViewPager)view.findViewById(R.id.vpPager);
        vpPager.setOffscreenPageLimit(2);
        vpPager.setAdapter(pagerAdapter);
        vpPager.addOnPageChangeListener(this);
    
        return view;
    }
    

    它在我的代码中使用 viewpager 和片段就像魅力一样。

    【讨论】:

    • 你刚刚在 LIKE 3H 调试后救了我,试图理解为什么我的 recyclerView 没有显示...
    • 但是如果我在片段中使用 ViewPager?
    • 成功了。谢谢。
    • 使用子片段管理器,绝对正确。谢谢@Darshan。
    • getFragmentManager() 只在第一次工作,而getChildFragmentManager() 工作良好的原因是什么?
    【解决方案2】:

    刚刚我在挣扎了一整天后解决了这个问题,使用getChildFragmentManager() 将此作为参数传递给 pagerAdapter。它会起作用的。

    在片段使用中使用 pagerAdapter 时:

    PagerAdapter adapter = new PagerAdapter(getChildFragmentManager());
    

    如果有活动,请使用 getFragmentManager()

    PagerAdapter adapter = new PagerAdapter(getFragmentManager());
    

    【讨论】:

      【解决方案3】:

      您正在使用 Activity FragmentManager 创建 ListFragment1 和 ListFragment2,而您应该使用 Fragment FragmentManager。因此,将pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager()); 修改为pagerAdapter = new PagerAdapter(getChildFragmentManager());。这样,视图分页器的片段将“绑定”到托管视图分页器的片段。此外,您不应该在 viewpager 中保留对片段的任何引用:这是 Android 已经管理的东西。尝试:

      private class PagerAdapter extends FragmentPagerAdapter {
      
          public PagerAdapter(android.support.v4.app.FragmentManager fm) {
              super(fm);
          }
      
          @Override
          public android.support.v4.app.Fragment getItem(int position) {
              switch (position) {
                  case 0:  return ListFragment1.newInstance();
                  case 1:  return ListFragment2.newInstance();
                  default: return null;
              }
          }
      }
      

      顺便说一句,vpPager.setOffscreenPageLimit(2); 没用,因为你只有 2 页,这是我从未使用过的方法,即使我有很多片段要管理,因为它需要内存。

      关于您的更新:删除与 ViewPager 处理片段相关的任何逻辑。如果需要在 Fragment 中启动 AsyncTask,可以使用 Fragment 生命周期的方法之一:onResume()、onCreateView() 等。

      class IPagedFragment extends Fragment {
      
          public void onResume() {
              super.onResume();
              myTask= new MyTask();
              myTask.execute((Void)null);
          }
      }
      

      请删除private final ListFragment1 lf1 = ListFragment1 .newInstance();。相信我,这不是一个好主意,因为您对 Fragments 有很强的引用。

      我构建了一个简单的项目,您可以将其用作参考实现。你可以从我的dropbox下载源代码。

      【讨论】:

      • 我在 viewpager 片段中启动了一个异步任务,我得到了一个 IllegalStateException: not attached to activity
      • 那是另一个问题。发布你的异步任务
      • 我已经更新了我的答案。顺便说一句,你必须记住的一般规则是每个班级都有自己的规则和能力。您从 AsyncTask 获得的数据用于在 Fragment 内部构建一些视图?然后让 Fragment 创建 AsyncTask!
      • 我还在我的 Dropbox 公共区域中添加了一个简单的项目,以展示执行您需要实施的正确方法。
      【解决方案4】:

      使用 getChildFragmentManager() 代替 supportFragmentManager()

      【讨论】:

        【解决方案5】:

        如果上述任何解决方案都不起作用,您可以尝试通过向寻呼机视图实例发布(延迟)适配器的附加 notifyDataSetChanged 调用来尝试解决方法:

        vpPager.post(new Runnable() {
            @Override
            public void run() {
                pagerAdapter.notifyDataSetChanged();
            }
        });
        

        vpPager.postDelayed(new Runnable() {
            @Override
            public void run() {
                pagerAdapter.notifyDataSetChanged();
            }
        }, 100 /* you have to find out the best delay time by trying/adjusting */);
        

        【讨论】:

        • 谢谢!对我有帮助的是添加notifyDataSetChanged()。但是没有 postDelayed
        【解决方案6】:

        尝试在 FragmentPagerAdapter 中覆盖 getItemPosition 方法:

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

        【讨论】:

        • 这个覆盖应该做什么?反正试了一下,问题还是没有解决。
        【解决方案7】:

        如果你在 Kotlin 中体验过,会是这样。

        val fragmentAdapter = FragmentPageAdapter(childFragmentManager)
        

        【讨论】:

          【解决方案8】:

          您不应在FragmentPagerAdapter 中保留对片段的引用。你应该总是在getItem()调用中调用newInstance,例如:

          @Override
          public android.support.v4.app.Fragment getItem(int position) {
              switch (position) {
                  case 0:  return ListFragment1.newInstance();
                  case 1:  return ListFragment2.newInstance();
                  default: return null;
              }
          }
          

          您从数据库加载的数据应该存储在片段本身中。适配器将恢复片段的状态 (setOffscreenPageLimit(2))。

          您正在丢失您的片段,因为项目(片段)由您提供的FragmentManager 实例化,并且它基于标签创建片段。所以它可能会创建一个你已经保留的片段的新实例,只是使用不同的标签。

          查看FragmentPagerAdapter源代码(查看instantiateItem()方法): https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v13/java/android/support/v13/app/FragmentPagerAdapter.java

          另请参阅此答案: keep instances of fragments inside FragmentPagerAdapter

          【讨论】:

            【解决方案9】:
            1. 在PagerAdapter 类上重写方法setPrimaryItem, 当寻呼机发生变化时调用它,我会试一试。

            我会创建类似的东西:

             private class PagerAdapter extends FragmentPagerAdapter {
                    private final ListFragment1 lf1 = ListFragment1 .newInstance();
                    private final ListFragment2 lf2 = ListFragment2 .newInstance();
            
                    public PagerAdapter(android.support.v4.app.FragmentManager fm) {
                        super(fm);
                    }
            
                    @Override
                    public android.support.v4.app.Fragment getItem(int position) {
                        switch (position) {
                            case 0:  return lf1;
                            case 1:  return lf2;
                            default: return null;
                        }
                    }
            
                    @Override
                    public int getCount() {
                        return 2;
                    }
            
                    @Override
                    public void setPrimaryItem(ViewGroup container, int position, Object object) {
                        super.setPrimaryItem(container, position, object);
                        if (position == 0)
                            lf1.updateUI(); //Refresh what you need on this fragment
                        else if (position == 1)
                            lf2.updateUI();
                    }
            
                }
            
            1. 你也错过了getCount()
            2. 我不确定屏幕外有什么用,但它可能不是问题。 vpPager.setOffscreenPageLimit(2)
            3. 还有一件事,我还要删除vpPager.addOnPageChangeListener(this),这个没有用,它可能会给你带来一些问题。 无论您需要做什么,都可以在没有它的情况下完成它,通过覆盖分页,您可能会“破坏”一些标准分页(因为不调用 super)

            【讨论】:

            • 我很抱歉这种解决方案已被接受。我不明白为什么您认为将 Fragment 保留在内存中是可以接受的。这是一种糟糕的设计模式,因为每个 Fragment 都需要系统无法释放的内存,例如,当您想切换到 FragmentStatePagerAdapter 时。
            • 我只是按照他的设计并指出改进它的方法,你也可以这样做,而不是抨击..
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-10-16
            • 2016-03-02
            相关资源
            最近更新 更多