ApiDemos--FragmentContextMenu的例子分析
ContextMenu顾名思义就是上下文菜单的意思,在某些情况下,我们是需要这样的菜单的,这个菜单通常会和某个View绑定在一起。在Fragment中定义上下文菜单的步骤如下:
1)调用registerForContextMenu(View view)来绑定某个View对象;
2) 然后实现回调onCreateContextMenu,在显示上下文菜单之前,会调用此方法。在这个回调中填充ContextMenu对象,指定要显示的菜单条目;
3) 实现回调方法onContextItemSelected(),来响应菜单事件。
FragmentContextMenu类在布局的android.R.id.content中添加了一个Fragment,代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the list fragment and add it asour sole content.
ContextMenuFragment content = new ContextMenuFragment(); getFragmentManager().beginTransaction().add(android.R.id.content, content).commit();
}
这样这个Fragment就会显示在Activity中了。如果你对android.R.id.content感到困惑的话,最后我会简要说明一下此乃何物。
在ContextMenuFragment中我们在某个Button上注册了一个上下文菜单:
@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,
Bundle savedInstanceState) {
View root =inflater.inflate(R.layout.fragment_context_menu, container, false);
registerForContextMenu(root.findViewById(R.id.long_press));
return root;
}
然后实现回调onCreateContextMenu(),来填充菜单:
@Override
public void onCreateContextMenu(ContextMenu menu, Viewv, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
/*menu.add(Menu.NONE, R.id.a_item, Menu.NONE, "Menu A");
menu.add(Menu.NONE, R.id.b_item,Menu.NONE, "Menu B");*/
getActivity().getMenuInflater().inflate(R.menu.fragment_context_menu, menu);
}
原代码的例子是直接使用menu.add()的形式来填充菜单,但是我觉得这种形式相当不好,不够灵活,我修改成使用资源填充的形式,当然需要在/res/menu/目录下创建一个fragment_context_menu.xml文件:
<?xml version="1.0"encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/a_item"android:title="菜单A"></item>
<item android:id="@+id/b_item"android:title="菜单B"></item>
</menu>
然后响应菜单点击事件:
@Override
public boolean onContextItemSelected(MenuItemitem) {
switch (item.getItemId()) {
case R.id.a_item:
Toast.makeText(getActivity(),"Item 1a was chosen", Toast.LENGTH_SHORT).show();
return true;
case R.id.b_item:
Toast.makeText(getActivity(),"Item 1b was chosen", Toast.LENGTH_SHORT).show();
return true;
}
return super.onContextItemSelected(item);
}
长按按钮显示出来的上下文菜单的效果图如下:
下面我们来谈一谈android.R.id.content是个什么东西。从它的使用上来看,这个content其实是布局文件中某个View的id。在Activity的回调onCreate()中我们通常会调用setContentView来设置布局文件,这个setContentView的实现在Activity.java中形式如下:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
getWindow()其实是一个PhoneWindow对象,它的setContentView()的实现如下:
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
从上面的代码我们可以知道,通常情况下我们设置的界面布局文件layoutResID会被添加到mContentParent中去,在installDecor()中mContentParent会被创建出来:
private void installDecor(){
。。。
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
然后我们进入generateLayout()中看一看:
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
。。。
View in = mLayoutInflater.inflate(layoutResource,null);
decor.addView(in, newViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
。。。
return contentParent;
}
这个contentParent就是ID为ID_ANDROID_CONTENT的ViewGroup对象,而这个ID值就是等于android.R.id.content,原来我们上面的Fragment添加到了这个ViewGroup对象中去了。其实那个View in对象就是我们整个activity界面的布局,包括ActionBar,content等对象。layoutResource根据当前设置的Feature不同,会得到不同的值,通常情况下这个值等于layoutResource = com.android.internal.R.layout.screen_action_bar;
而这个布局文件的内容如下:
/android/frameworks$ cat./base/core/res/res/layout/screen_action_bar.xml
<com.android.internal.widget.ActionBarOverlayLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/action_bar_overlay_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
<FrameLayout android:id="<span style="color:#ff6666;">@android:id/content</span>"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.android.internal.widget.ActionBarContainer
android:id="@+id/action_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layoutDirection="ltr"
style="?android:attr/actionBarStyle"
android:gravity="top">
<com.android.internal.widget.ActionBarView
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/actionBarStyle" />
<com.android.internal.widget.ActionBarContextView
android:id="@+id/action_context_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
style="?android:attr/actionModeStyle" />
</com.android.internal.widget.ActionBarContainer>
<com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/actionBarSplitStyle"
android:visibility="gone"
android:gravity="center"/>
</com.android.internal.widget.ActionBarOverlayLayout>
上图中标红的就是那个android.R.id.content对象。