我有一个应用程序,在ViewPager
中显示了一些片段(相同类型),并且我在使用上下文菜单项时遇到了一些问题。 (我正在使用支持库)。
当在其中一个片段的上下文菜单中选择了上下文菜单项时,错误的片段正在接收onContextItemSelected
事件调用。
例如,如果我在寻呼机中的片段#3上,则位置#2处的片段接收它。如果我滑回片段#2,片段#3将接收该呼叫。
我有一个示例here。
(我目前正在自己的应用中解决这个问题,在每个片段中都有一个mHandleContext
变量,并在页面更改时启用/禁用它。这样onContextItemSelected
调用就会消失到所有片段,直到正确的片段被调用。)
我做错了什么或这是支持库的错误?作为旁注,我在使用ActionBarSherlock 3.5.1时没有发生这种情况,ActionBarSherlock 3.5.1有自己的支持库分支。
答案 0 :(得分:36)
所以这是Google的某种愚蠢的设计决定,或者刚刚完全没有考虑的事情。解决此问题的最简单方法是使用if语句包装onContextItemSelected
调用:
if (getUserVisibleHint()) {
// Handle menu events and return true
} else
return false; // Pass the event to the next fragment
ActionBarSherlock 3.5中的兼容性库有这样的黑客攻击。
答案 1 :(得分:14)
这是因为:
public boolean dispatchContextItemSelected(MenuItem item) {
if (mActive != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
if (f != null && !f.mHidden) {
if (f.onContextItemSelected(item)) {
return true;
}
}
}
}
return false;
}
如您所见,FragmentManager为其所有片段调用Fragment.onContextItemSelected,直到它返回true。在您的示例中,我可以提供此类修复:
public static class TestListFragment extends ListFragment {
private int mNumber = 0;
private ArrayList<String> mItems;
public static TestListFragment newInstance(int number) {
Bundle args = new Bundle();
args.putInt("number", number + 1);
TestListFragment fragment = new TestListFragment();
fragment.setArguments(args);
return fragment;
}
public TestListFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNumber = getArguments().getInt("number");
mItems = new ArrayList<String>();
mItems.add("I am list #" + mNumber);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mItems));
registerForContextMenu(getListView());
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(mNumber, 0, 0, "Hello, World!");
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if(item.getGroupId() == mNumber){
Log.d("ViewPagerContextMenuBug", "onContextItemSelected called for number " + mNumber);
Toast.makeText(getActivity(), "onContextItemSelected called for number " + mNumber, Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
}
答案 2 :(得分:1)
对每个菜单项使用intent都很适合我。
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextmenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = super.getActivity.getMenuInflater();
inflater.infalte(R.menu.list_item, menu);
for(int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
Intent intent = new Intent();
intent.putExtra(KEY_EXTRA_FRAGMENT_ID, this.fragmentId);
if (item != null) {
item.setIntent(intent);
}
}
}
@Override
public boolean onContextItemSelected(MeniItem item) {
Intent intent = item.getIntent();
if (intent != null) {
if (intent.getIntExtra(KEY_EXTRA_FRAGMENT_ID, -1) == this.fragmentId) {
// Implement code according the item function.
return true;
}
}
return super.onContextItemSelected(item);
}
答案 3 :(得分:1)
getUserVisibleHint()
解决方案对我不起作用 - 即使片段不在屏幕上,它也始终返回true
。从XML资源中膨胀菜单时,getGroupId()
解决方案无效。这是我的情况。
似乎除非Android发生变化,否则任何解决方案总会有点笨拙。我创建了一个全局变量来存储onCreateView
中当前片段的ID引用。然后我将其传递给ContextMenu
中的每个MenuItem
onCreateContextMenu
。选择项目后,我会验证这两个ID是否相同。
private int myFragmentReference;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Initialisation stuff
myFragmentReference = 12345;
}
@Override
public void onCreateContextMenu(ContextMenu contextMenu, View v, ContextMenu.ContextMenuInfo contextMenuInfo) {
// Usual stuff
int size = contextMenu.size();
for (int i = 0; i < size; i++) {
MenuItem menuItem = contextMenu.getItem(i);
Intent intent = new Intent();
intent.putExtra("id", myFragmentReference);
menuItem.setIntent(intent);
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
int id = 0;
Intent intent = menuItem.getIntent();
if (intent != null) {
id = intent.getIntExtra("id", 0);
}
if (id == myFragmentReference) {
// This is the currently displayed fragment
}
}
答案 4 :(得分:0)
哦Google,我的意思是WTF?
问题是 onContextItemSelected 非常通用,并且会为每个片段的每个菜单项调用它。
您可以使用 MenuItem.OnMenuItemClickListener 强制片段菜单不使用所有 onContextItemSelected ,但只能使用此片段的相同功能。
使用下一个实现:
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getActivity().getMenuInflater();
if (v == btnShare) {
inflater.inflate(R.menu.share_menu, menu);
for (int i = 0; i < menu.size(); ++i) {
MenuItem item = menu.getItem(i);
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
onContextItemSelected(item);
return true;
}
});
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case R.id.print:
// ...
}
}