onCreateView 未在 ViewPager 中使用 Fragment 调用
我正在使用 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 Fragment
s. At first, these Fragments where all ListFragment
s 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
重要的部分似乎是 FragmentPagerAdapter(我没有包括)。调试显示重新创建活动时不会调用
getItem()
。片段通过其他方式重新创建。这意味着我的FragmentPagerAdapter
实现错误地引用了FragmentManager
中的片段。因此,当我需要查找片段时,我使用
findFragmentByTag()
(在FragmentManager
上)使用makeFragmentName()
来获取适当的片段标签名称。这是添加片段时FragmentPagerAdapter
使用的方法。但不确定这是否是一种安全的方法。The vital part seems to have been the
FragmentPagerAdapter
(which I didn't include). Debugging shows thatgetItem()
isn't called when the activity is re-created. The Fragments are re-created by other means. Which meant that my implementation ofFragmentPagerAdapter
had wrong references to the fragments in theFragmentManager
.So instead, when I need to lookup a fragment I use
findFragmentByTag()
(on theFragmentManager
) using themakeFragmentName()
to get the appropriate tag name. That's method used by theFragmentPagerAdapter
when the fragment is added. Not sure whether this is a safe approach or not though.使用
getChildFragmentManager()
而不是getFragmentManager()
示例:
而不是
use
getChildFragmentManager()
instead ofgetFragmentManager()
example:
instead of
最好使用 FragmentStatePagerAdapter 这样片段就会相应地重新创建。
Better use FragmentStatePagerAdapter that way the fragments are recreated accordingly.