onCreateView 未在 ViewPager 中使用 Fragment 调用

发布于 2024-12-27 10:58:15 字数 6183 浏览 4 评论 0原文

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

getListView().getPositionForView(v)

这就是问题开始的地方。有时 getListView() 会抛出 IllegalStateException 并提示“内容视图尚未创建”。即 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 的(一个片段)

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 中的每个片段一个),但当应用程序恢复时,它永远不会再次被调用!?这是一个错误还是我可能做错了什么?

I am using ViewPager from the ACL to show a couple of Fragments. At first, these Fragments where all ListFragments as they contains a list. I'm trying to mimic a 2 column table, so each element in the list contains another list of two objects. I want each cell to be long-clickable (not the entire list item), therefore I implemented my own ColumnLayout (perhaps it should be called CellLayout). Each list item contains two ColumnLayouts which are long-clickable and implements getContextMenuInfo() to return information about which entity was long-clicked. This method looks up the ViewPager, asks for the current fragment and then calls the fragment to return the entity id from the view that was long-clicked. In order to get the correct row the fragment needs to do:

getListView().getPositionForView(v)

And here's where the problem starts. Sometimes getListView() throws IllegalStateException saying "Content view not yet created". I.e. onCreateView(...) hasn't been called. This only happens sometimes when the app is resumed. Today I managed to re-produce the problem by enabling the "Don't keep activities" option in Settings > Developer options on my Galaxy Nexus. I start the app, press Home and then re-open the app from the recent apps menu and now, if I long-click any of the cells in my listview this exception is thrown. By some debug outputs I managed to verify that onCreateView has never been called. Which is kind of strange because the entire ViewPager with its Fragment and its ListView and its cell items are all drawn!

I was told on #android-dev that ListFragment in ACL does not support custom layouts so I re-implemented my android.support.v4.app.Fragment without using ListFragment but that didn't help.

Short summary: I have a custom layout which handles the event of long-pressing the layout for the purpose of creating a MenuInfo object containing information about entitiy in the cell pressed. In order to find the entity contained in the layout, listview.getPositionForView(View v) is eventually called, sometimes causing an IllegalStateException.

Here my custom layout (perhaps there's an easier way to get hold of the ViewPager than digging in the view hierarchy):

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;
        }

    }
}

And here's (a snippet of) the Fragment:

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);
    }

Isn't it rather strange that, when resuming the app (if the activity was destroyed), the ViewPager with its Fragments and its ListView and its custom layouts are all drawn. Yet I get IllegalStateException saying the view hasn't been created if I long-press any of the cells in the listview?

edit/
Actually, a Log.d(Constants.TAG, "onCreateView!!!!"); output in onCreateView shows that the methods is invoked two times the first time I start the app (one for each Fragment in the ViewPager) but then it's never called again when the app is resumed!? Is that a bug or what might I be doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烂柯人 2025-01-03 10:58:15

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

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

The vital part seems to have been the FragmentPagerAdapter (which I didn't include). Debugging shows that getItem() isn't called when the activity is re-created. The Fragments are re-created by other means. Which meant that my implementation of FragmentPagerAdapter had wrong references to the fragments in the FragmentManager.

So instead, when I need to lookup a fragment I use findFragmentByTag() (on the FragmentManager) using the makeFragmentName() to get the appropriate tag name. That's method used by the FragmentPagerAdapter when the fragment is added. Not sure whether this is a safe approach or not though.

冷情 2025-01-03 10:58:15

使用 getChildFragmentManager() 而不是 getFragmentManager()

示例:

new ViewPagerAdapter(getChildFragmentManager());

而不是

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

use getChildFragmentManager() instead of getFragmentManager()

example:

new ViewPagerAdapter(getChildFragmentManager());

instead of

new ViewPagerAdapter(((HomeActivity) context).getSupportFragmentManager())
若无相欠,怎会相见 2025-01-03 10:58:15

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

Better use FragmentStatePagerAdapter that way the fragments are recreated accordingly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文