在 ViewPager 中未使用 Fragment 调用 onCreateView

Posted

技术标签:

【中文标题】在 ViewPager 中未使用 Fragment 调用 onCreateView【英文标题】:onCreateView not called with Fragment in ViewPager 【发布时间】:2012-02-12 06:08:18 【问题描述】:

我使用 ACL 中的 ViewPager 来显示几个 Fragments。起初,这些 Fragments 中的所有 ListFragments 都包含一个列表。我试图模仿一个 2 列的表,因此列表中的每个元素都包含另一个包含两个对象的列表。我希望每个单元格都可以长按(而不是整个列表项),因此我实现了自己的 ColumnLayout(也许它应该称为 CellLayout)。每个列表项包含两个可长按的 ColumnLayout,并实现 getContextMenuInfo() 以返回有关长按哪个实体的信息。此方法查找 ViewPager,请求当前片段,然后调用片段以从长按视图返回实体 id。为了获得正确的行,片段需要做:

getListView().getPositionForView(v)

问题就从这里开始。有时getListView() 抛出IllegalStateException 说“尚未创建内容视图”。 IE。 onCreateView(...) 尚未被调用。这仅在应用程序恢复时才会发生。今天,我设法通过在我的 Galaxy Nexus 上启用“设置”>“开发人员”选项中的“不保留活动”选项来重现该问题。我启动应用程序,按主页,然后从最近的应用程序菜单重新打开应用程序,现在,如果我长按列表视图中的任何单元格,则会引发此异常。通过一些调试输出,我设法验证从未调用过 onCreateView。这有点奇怪,因为整个 ViewPager 及其 Fragment 及其 ListView 及其单元格项都被绘制了!

我在 #android-dev 上被告知 ACL 中的 ListFragment 不支持自定义布局,因此我重新实现了我的 android.support.v4.app.Fragment 而不使用 ListFragment 但这没有帮助。

简短总结:我有一个自定义布局,它处理长按布局的事件,目的是创建一个 MenuInfo 对象,其中包含有关按下的单元格中实体的信息。为了找到布局中包含的实体,最终会调用listview.getPositionForView(View v),有时会导致IllegalStateException

这是我的自定义布局(也许有一种比在视图层次结构中挖掘更简单的方法来获取ViewPager):

package org.remotestick;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.widget.LinearLayout;

public class ColumnLayout extends LinearLayout 

    public ColumnLayout(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public ColumnLayout(Context context) 
        super(context);
    

    @Override
    protected ContextMenuInfo getContextMenuInfo() 
        int itemTypeId = getChildAt(0).getId();
        int positionTypeId = getId();
        View currentView = this;
        ViewPager viewPager = null;
        while(currentView.getParent() != null) 
            currentView = (View) currentView.getParent();
            if(currentView.getId() == R.id.pager) 
                viewPager = (ViewPager) currentView;
                break;
             else if (currentView.getId() == R.id.rootLayout)
                break;
        
        if(viewPager == null)
            return null;

        WorkspaceAdapter adapter = (WorkspaceAdapter) viewPager.getAdapter();
        Pair<Integer, Integer> itemIdAndListPosition = adapter.getItemIdFromWorkspace(viewPager.getCurrentItem(), this, itemTypeId, (positionTypeId == R.id.leftColumn));
        if(itemIdAndListPosition == null)
            return null;
        else
            return new MatrixAdaptableContextMenuInfo(itemTypeId, itemIdAndListPosition.first, itemIdAndListPosition.second);
    

    public static class MatrixAdaptableContextMenuInfo implements ContextMenuInfo 

        public final int itemTypeId;
        public final Integer itemId;
        public final Integer positionInList;

        public MatrixAdaptableContextMenuInfo(int itemTypeId, Integer itemId, Integer positionInList) 
            this.itemTypeId = itemTypeId;
            this.itemId = itemId;
            this.positionInList = positionInList;
        

    

这是Fragment的(sn-p):

public class EntityTableFragment extends Fragment implements TelldusEntityChangeListener 

    protected static final String ARG_TITLE = "title";

    protected MatrixAdapter mAdapter;
    protected ProgressViewer mProgressViewer;
    protected MatrixFilter mFilter;
    protected Handler mHandler = new Handler();
    protected RemoteStickData db;

    public boolean onAttach = false;
    public boolean onCreateView = false;

    private ListView listView;

    public static EntityTableFragment newInstance(String title) 
        EntityTableFragment f = new EntityTableFragment();

        Bundle args = new Bundle();
        args.putString(ARG_TITLE, title);
        f.setArguments(args);
        return f;
    

    @Override
    public void onAttach(SupportActivity activity) 
        super.onAttach(activity);
        onAttach = true;
            try 
            mProgressViewer = (ProgressViewer) activity;
            mAdapter = new MatrixAdapter(getActivity(), mProgressViewer, new ArrayList<List<MatrixEntity>>(), null);
            TelldusLiveService.addListener(this);
         catch (ClassCastException e) 
            throw new ClassCastException(activity.toString() + " must implement ProgressViewer");
        
        db = new RemoteStickData(getActivity());
    

    @Override
    public void onDetach() 
        super.onDetach();
        TelldusLiveService.removeListener(this);
    

    @Override
    public void onActivityCreated(Bundle savedInstanceState) 
        super.onActivityCreated(savedInstanceState);
        listView.setAdapter(mAdapter);
    

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        View view = inflater.inflate(R.layout.list, null);
        listView = (ListView) view.findViewById(R.id.list);
        return view;
    

    @Override
    public String toString() 
        return getArguments().getString(ARG_TITLE);
    

    public Pair<Integer, Integer> getIdAndPositionForView(View v, boolean isLeft) 
        if(listView == null)
            throw new IllegalStateException("Listview not yet created");
        int positionForView = listView.getPositionForView(v);
        if(isLeft)
            return new Pair<Integer, Integer>(mAdapter.getItem(positionForView).get(0).getId(), positionForView);
        else
            return new Pair<Integer, Integer>(mAdapter.getItem(positionForView).get(1).getId(), positionForView);
    

当恢复应用程序时(如果 Activity 被销毁),ViewPager 及其 Fragments 及其 ListView 及其自定义布局都被绘制出来,这不是很奇怪。然而,如果我长按列表视图中的任何单元格,我会收到 IllegalStateException 说视图尚未创建?

编辑/ 实际上,onCreateView 中的Log.d(Constants.TAG, "onCreateView!!!!"); 输出表明,当我第一次启动应用程序时,方法被调用了两次(ViewPager 中的每个片段一个),但是当应用程序恢复时,它就再也没有被调用过!?这是一个错误还是我做错了什么?

【问题讨论】:

【参考方案1】:

重要的部分似乎是FragmentPagerAdapter(我没有包括在内)。调试显示重新创建活动时未调用getItem()。片段是通过其他方式重新创建的。这意味着我对FragmentPagerAdapter 的实现错误地引用了FragmentManager 中的片段。

因此,当我需要查找片段时,我使用findFragmentByTag()(在FragmentManager)使用makeFragmentName() 来获取适当的标签名称。这是添加片段时FragmentPagerAdapter 使用的方法。不确定这是否是一种安全的方法。

【讨论】:

【参考方案2】:

使用getChildFragmentManager() 代替getFragmentManager()

示例:

new ViewPagerAdapter(getChildFragmentManager());

而不是

new ViewPagerAdapter(((HomeActivity) context).getSupportFragmentManager())

【讨论】:

【参考方案3】:

更好地使用 FragmentStatePagerAdapter,这样片段就会相应地重新创建。

【讨论】:

以上是关于在 ViewPager 中未使用 Fragment 调用 onCreateView的主要内容,如果未能解决你的问题,请参考以下文章

在Viewpager中的一个Fragment中添加一个ViewPager

Android ViewPager Fragment使用懒加载提升性能

Android ViewPager + Fragment 懒加载数据 可操作控件版

使用 ViewPager 在 Fragment 中创建 Listview

ViewPager+Fragment取消预加载(延迟加载)

Fragment与ViewPager