在移动设备应用程序中,callout 是在应用程序顶部弹出的容器。该容器可以容纳一个或多个组件,并且支持不同类型的布局。

callout 容器可以是模态或非模态容器。模态容器在其关闭之前接受所有的键盘和鼠标输入。非模态容器允许应用程序中的其它组件在该容器处于打开状态时接受输入。

Flex 提供了两个可用于将 callout 容器添加到移动设备应用程序中的组件:CalloutButton 和 Callout

 
使用 CalloutButton 控件创建 callout 容器

CalloutButton 控件提供了一种创建 callout 容器的简单方式。通过该组件,您可以定义显示在 callout 中的组件和设置容器布局。

在移动设备应用程序中选择 CalloutButton 控件时,该控件将打开 callout 容器。Flex 会自动绘制一个从 callout 容器指向 CalloutButton 控件的箭头,如下图所示:

以下示例说明了用于创建上图中显示的 CalloutButton 的移动设备应用程序:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\views\CalloutButtonSimpleHomeView.mxml -->
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="HomeView">
<s:layout>
<s:VerticalLayout
paddingLeft="10" paddingTop="10"/>
</s:layout> <s:Label text="Select the button to open the callout"/> <s:CalloutButton id="myCB"
horizontalPosition="end"
verticalPosition="after"
label="Open callout">
<s:calloutLayout>
<s:HorizontalLayout/>
</s:calloutLayout> <!-- Define buttons that appear in the callout. -->
<s:Button label="OK"
click="myCB.closeDropDown();"/>
<s:Button label="Cancel"
click="myCB.closeDropDown();"/>
</s:CalloutButton>
</s:View>

CalloutButton 控件定义显示在 callout 容器内的两个 Button 控件。CalloutButton 控件还指定将 HorizontalLayout 用作 callout 容器的布局。默认情况下,该容器使用 BasicLayout。

使用 CalloutButton 控件打开和关闭 Callout 容器

当用户选择 CalloutButton 控件或您调用 CalloutButton.openDropDown() 方法时,callout 容器将打开。horizontalPosition 和 verticalPosition 属性确定 callout 容器相对于 CalloutButton 控件的位置。有关示例,请参阅确定 callout 容器的大小和位置

CalloutButton 打开的 callout 容器始终都是非模态的。这意味着应用程序中的其它组件可以在 callout 处于打开状态时接受输入。使用 Callout 容器创建模态 callout。

在您单击 callout 容器以外的位置或调用 CalloutButton.closeDropDown() 方法之前,callout 容器一直处于打开状态。在该示例中,对于 callout 容器中两个 Button 控件的 click 事件,您在事件处理函数中调用了 closeDropDown() 方法。

使用 Callout 容器创建 callout

CalloutButton 控件将 callout 容器以及打开和关闭 callout 必需的所有逻辑封装在一个控件中。则 CalloutButton 控件成为 callout 容器的主机

您还可以在移动设备应用程序中使用 Callout 容器。Callout 容器的好处是它不与单个主机关联,因此可以重用于应用程序中的任何位置。

通常,为了响应事件,使用 Callout.open() 和 Callout.close() 方法打开 Callout 容器。调用 open() 方法时,可以传递可选参数以指定调用容器为模态容器。默认情况下,调用容器为非模态容器。

调用容器的位置相对于主机组件。horizontalPosition 和 verticalPosition 属性确定容器相对于主机的位置。有关示例,请参阅确定 callout 容器的大小和位置

由于它为弹出窗口,因此不能创建一个 Callout 容器作为应用程序的正常 MXML 布局代码的一部分。应在 MXML 文件中将 Callout 容器定义为自定义 MXML 组件。

在以下示例中,在应用程序的 comps 目录的 MyCallout.mxml 文件中定义 Callout 容器:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\comps\MyCallout.mxml -->
<s:Callout xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
horizontalPosition="start"
verticalPosition="after"> <s:VGroup
paddingTop="10" paddingLeft="5" paddingRight="10"> <s:HGroup verticalAlign="middle">
<s:Label text="First Name: "
fontWeight="bold"/>
<s:TextInput width="225"/>
</s:HGroup> <s:HGroup verticalAlign="middle">
<s:Label text="Last Name: "
fontWeight="bold"/>
<s:TextInput width="225"/>
</s:HGroup> <s:HGroup>
<s:Button label="OK" click="close();"/>
<s:Button label="Cancel" click="close();"/>
</s:HGroup>
</s:VGroup>
</s:Callout>

MyCallout.mxml 定义一个简单的弹出窗口以允许用户输入名和姓。请注意,按钮调用 close() 方法关闭 callout 以响应 click 事件。

以下示例说明了用于打开 MyCallout.mxml 的 View 容器以响应 click 事件:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\views\CalloutSimpleHomeView.mxml -->
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="HomeView">
<s:layout>
<s:VerticalLayout
paddingLeft="10" paddingTop="10"/>
</s:layout> <fx:Script>
<![CDATA[
import comps.MyCallout; // Event handler to open the Callout component.
protected function button1_clickHandler(event:MouseEvent):void {
var myCallout:MyCallout = new MyCallout();
// Open as a modal callout.
myCallout.open(calloutB, true);
}
]]>
</fx:Script> <s:Label text="Select the button to open the callout"/>
<s:Button id="calloutB"
label="Open Callout container"
click="button1_clickHandler(event);"/>
</s:View>

首先,将 MyCallout.mxml 组件导入至应用程序。为了响应 click 事件,名为 calloutB 的按钮创建了一个 MyCallout.mxml 实例,然后调用 open() 方法。

open() 方法指定两个参数。第一个参数指定 calloutB 是 callout 的主机组件。因此,callout 在应用程序中将自身定位在相对于 calloutB 位置的位置。第二个参数为 true 以创建模态 callout。

定义内联 callout 容器

您无需在单独文件中定义 Callout 容器。以下示例使用 <fx:Declaration> 标签将其定义为 View 容器的内联组件:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\views\CalloutInlineHomeView.mxml -->
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="HomeView">
<s:layout>
<s:VerticalLayout
paddingLeft="10" paddingTop="10"/>
</s:layout> <fx:Script>
<![CDATA[
// Event handler to open the Callout component.
protected function button1_clickHandler(event:MouseEvent):void {
var myCallout:MyCallout = new MyCallout();
// Open as a modal callout.
myCallout.open(calloutB, true);
}
]]>
</fx:Script> <fx:Declarations>
<fx:Component className="MyCallout">
<s:Callout
horizontalPosition="end"
verticalPosition="after">
<s:VGroup
paddingTop="10" paddingLeft="5" paddingRight="10">
<s:HGroup verticalAlign="middle">
<s:Label text="First Name: "
fontWeight="bold"/>
<s:TextInput width="225"/>
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label text="Last Name: "
fontWeight="bold"/>
<s:TextInput width="225"/>
</s:HGroup>
<s:HGroup>
<s:Button label="OK" click="close();"/>
<s:Button label="Cancel" click="close();"/>
</s:HGroup>
</s:VGroup>
</s:Callout>
</fx:Component>
</fx:Declarations> <s:Label text="Select the button to open the callout"/>
<s:Button id="calloutB"
label="Open Callout container"
click="button1_clickHandler(event);"/>
</s:View>

从 Callout 容器中传回数据

使用 Callout 容器的 close() 方法将数据传回主应用程序。close() 方法具有如下签名:

public function close(commit:Boolean = false, data:*):void
其中:

  • 如果应用程序应提交返回的数据,则 commit 包含 true。

  • data 指定返回的数据。

调用 close() 方法会分派 close 事件。与 close 事件关联的事件对象是类型为 spark.events.PopUpEvent 的对象。PopUpEvent 类定义两个属性:commit 和 data,这两个属性包含 close() 方法的相应参数值。在 close 事件的事件处理函数中使用这些属性,以便检查从 callout 返回的任何数据。

callout 容器是 SkinnablePopUpContainer 类的子类,它使用相同的机制将数据传回主应用程序中。有关从 SkinnablePopUpContainer 容器传回数据的示例,请参阅从 Spark SkinnablePopUpContainer 容器传回数据

以下示例将修改如上所示的 Callout 组件以返回名和姓值:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\comps\MyCalloutPassBack.mxml -->
<s:Callout xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
horizontalPosition="start"
verticalPosition="after"> <fx:Script>
<![CDATA[
import spark.events.IndexChangeEvent; public var retData:String = new String(); // Event handler for the click event of the OK button.
protected function clickHandler(event:MouseEvent):void {
//Create the return data.
retData = firstName.text + " " + lastName.text; // Close the Callout.
// Set the commit argument to true to indicate that the
// data argument contains a valid value.
close(true, retData);
} ]]>
</fx:Script> <s:VGroup
paddingTop="10" paddingLeft="5" paddingRight="10">
<s:HGroup verticalAlign="middle">
<s:Label text="First Name: "
fontWeight="bold"/>
<s:TextInput id="firstName" width="225"/>
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label text="Last Name: "
fontWeight="bold"/>
<s:TextInput id="lastName" width="225"/>
</s:HGroup>
<s:HGroup>
<s:Button label="OK" click="clickHandler(event);"/>
<s:Button label="Cancel" click="close();"/>
</s:HGroup>
</s:VGroup>
</s:Callout>

在该示例中,创建 String 返回名和姓,以对用户选择“确定”按钮做出响应。

View 容器然后使用 Callout 中的 close 事件以显示返回的数据:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\views\CalloutPassBackDataHomeView.mxml -->
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="HomeView">
<s:layout>
<s:VerticalLayout
paddingLeft="10" paddingTop="10"/>
</s:layout> <fx:Script>
<![CDATA[
import comps.MyCalloutPassBack;
import spark.events.PopUpEvent; public var myCallout:MyCalloutPassBack = new MyCalloutPassBack(); // Event handler to open the Callout component.
protected function clickHandler(event:MouseEvent):void {
// Add an event handler for the close event to check for
// any returned data.
myCallout.addEventListener('close', closeHandler);
// Open as a modal callout.
myCallout.open(calloutB, true);
} // Handle the close event from the Callout.
protected function closeHandler(event:PopUpEvent):void {
// If commit is false, no data is returned.
if (!event.commit)
return; // Write the returned Data to the TextArea control.
myTA.text = String(event.data); // Remove the event handler.
myCallout.removeEventListener('close', closeHandler);
} ]]>
</fx:Script> <s:Label text="Select the button to open the callout"/>
<s:Button id="calloutB"
label="Open Callout container"
click="clickHandler(event);"/>
<s:TextArea id="myTA"/>
</s:View>

将 ViewNavigator 添加到 Callout 中

您可以在 Callout 容器中使用 ViewNavigator。通过 ViewNavigator 可以向 callout 中添加操作栏和多个视图。

例如,以下 View 打开了在文件 MyCalloutPassBackVN 中定义的 Callout 容器:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\views\CalloutPassBackDataHomeView.mxml -->
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="HomeView">
<s:layout>
<s:VerticalLayout
paddingLeft="10" paddingTop="10"/>
</s:layout> <fx:Script>
<![CDATA[
import comps.MyCalloutPassBackVN;
import spark.events.PopUpEvent; public var myCallout:MyCalloutPassBackVN = new MyCalloutPassBackVN(); // Event handler to open the Callout component.
protected function clickHandler(event:MouseEvent):void {
myCallout.addEventListener('close', closeHandler);
myCallout.open(calloutB, true);
} // Handle the close event from the Callout.
protected function closeHandler(event:PopUpEvent):void {
if (!event.commit)
return; myTA.text = String(event.data);
myCallout.removeEventListener('close', closeHandler);
}
]]>
</fx:Script> <s:Label text="Select the Open button to open the callout"/>
<s:TextArea id="myTA"/>
<s:actionContent>
<s:Button id="calloutB" label="Open"
click="clickHandler(event);"/>
</s:actionContent>
</s:View>

MyCalloutPassBackVN.mxml 文件定义用于容纳 ViewNavigator 容器的 Callout 容器:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\comps\MyCalloutVN.mxml -->
<s:Callout xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
contentBackgroundAppearance="none"
horizontalPosition="start"
verticalPosition="after"> <fx:Script>
<![CDATA[
import mx.events.FlexMouseEvent;
import views.SettingsView; protected function done_clickHandler(event:MouseEvent):void {
// Create an instance of SettingsView, and
// initialize it as a copy of the current View of the ViewNavigator.
var settings:SettingsView = (viewNav.activeView as SettingsView); // Create the String to represent the returned data.
var retData:String = new String();
// Initialze the String from the current View.
retData = settings.firstName.text + " " + settings.lastName.text;
// Close the Callout and return thhe data.
this.close(true, retData);
}
]]>
</fx:Script> <s:ViewNavigator id="viewNav" width="100%" height="100%" firstView="views.SettingsView">
<s:navigationContent>
<s:Button label="Cancel" click="close(false)"/>
</s:navigationContent>
<s:actionContent>
<s:Button id="done" label="OK" emphasized="true" click="done_clickHandler(event);"/>
</s:actionContent>
</s:ViewNavigator>
</s:Callout>

在 MyCalloutPassBackVN.mxml 中,指定 ViewNavigator 的第一个视图是 SettingsView。SettingsView 定义针对用户名字和姓氏的 TextInput 控件。用户选择“确定”按钮时,将关闭 Callout 并将任何返回数据传回 MyCalloutPassBackVN。

注: 当 ViewNavigator 出现在 Callout 容器中时,ActionBar 会具有透明的背景颜色。在此例中,您将 Callout 容器上的 contentBackgroundAppearance 设置为 none。此设置会防止透明 ActionBar 区域中出现 Callout 的默认白色 contentBackgroundColor。

下图显示在 Callout 处于打开状态下的应用程序:

SettingsView.mxml 如下所示:

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\views\SettingsView.mxml -->
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="Settings"> <s:VGroup
paddingTop="10" paddingLeft="5" paddingRight="10">
<s:HGroup verticalAlign="middle">
<s:Label text="First Name: "
fontWeight="bold"/>
<s:TextInput id="firstName" width="225"/>
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label text="Last Name: "
fontWeight="bold"/>
<s:TextInput id="lastName" width="225"/>
</s:HGroup>
</s:VGroup>
</s:View>
注: 由 Callout 容器中的 ViewNavigator 定义的 ActionBar 具有透明背景。默认情况下,从一个 View 到另一个 View 的过渡效果可以正确显示在 Callout 中。但是,如果您指定非默认的过渡效果(例如 CrossFadeViewTransition 或 ZoomViewTransition),两个视图的 ActionBar 区域可能会重叠。要解决该问题,为使用非透明背景的 ActionBar 和 Callout 创建一个自定义外观类。

确定 callout 容器的大小和位置

CalloutButton 控件和 Callout 容器使用以下两个属性指定 callout 容器相对于其主机的位置:horizontalPosition 和 verticalPosition。这两个属性可以具有下列值:“before”、“start”、“middle”、“end”、“after”和“auto”(默认值)。

例如,按照如下所示的方式设置这两个属性:

horizontalPosition="before"
verticalPosition="after"

callout 容器将在主机组件的左下方打开。如果按照如下所示的方式设置这两个属性:

horizontalPosition="middle"
verticalPosition="middle"

callout 容器将在主机组件的顶部打开,callout 的中心与主机组件的中心对齐。

绘制一个从 Callout 指向主机的箭头

对于除 horizontalPosition 与 verticalPosition 属性的五种组合之外的所有位置,callout 都会绘制一个指向主机的箭头。当 Callout 位于主机中部上方居中以及角落时,不会显示箭头。以下组合不显示箭头:

// Centered
horizontalPosition="middle"
verticalPosition="middle" // Upper-left corner
horizontalPosition="before"
verticalPosition="before" // Lower-left corner
horizontalPosition="before"
verticalPosition="after" // Upper-right corner
horizontalPosition="after"
verticalPosition="before" // Lower-right corner
horizontalPosition="after"
verticalPosition="after"

对于 Callout 容器,horizontalPosition 和 verticalPosition 属性还确定 Callout.arrowDirection 只读属性的值。callout 容器相对于主机的位置确定 arrowDirection 属性的值。可能的值包括 "up"、"left" 和其它。

Callout.arrow 外观部件使用 arrowDirection 属性的值基于 callout 位置绘制箭头。

管理 callout 容器的内存

使用 callout 容器时的一个注意事项是如何管理 callout 使用的内存。例如,如果希望减少应用程序使用的内存,则在每次打开应用程序时创建 callout 的实例。然后,在应用程序关闭时破坏 callout。但是,确保删除 callout(尤其是事件处理函数)的所有引用,否则 callout 不会被破坏。

或者,如果 callout 容器相对比较小,则可以在应用程序中多次重用相同的 callout。在该配置中,应用程序创建单个 callout 实例。然后,它重用该实例,callout 在使用的间隔保留在内存中。该配置减少了在应用程序中的执行时间,因为应用程序无需在每次打开时重新创建 callout。

使用 CalloutButton 控件管理内存

要配置 CalloutButton 控件使用的 callout,请设置 CalloutButton.calloutDestructionPolicy 属性。"auto" 的值配置在 callout 关闭时将其破坏的控件。"never" 的值配置将 callout 缓存在内存中的控件。

使用 Callout 容器管理内存

Callout 容器不定义 calloutDestructionPolicy 属性,而是按照您在应用程序中创建 callout 容器实例的方式控制其内存使用。在以下示例中,将在每次打开 callout 容器时创建它的实例:

protected function button1_clickHandler(event:MouseEvent):void {
// Create a new instance of the callout container every time you open it.
var myCallout:MyCallout = new MyCallout();
myCallout.open(calloutB, true);
}

或者,也可以定义在每次打开 callout 容器时重用的单个 callout 容器实例:

// Create a single instance of the callout container.
public var myCallout:MyCallout = new MyCallout(); protected function button1_clickHandler(event:MouseEvent):void {
myCallout.open(calloutB, true);
}

将 Callout 容器添加到移动设备应用程序中的更多相关文章

  1. 移动设备应用程序中支持多个屏幕大小和 DPI 值

    支持多个屏幕大小和 DPI 值的指导原则 要部署独立于平台的应用程序,应了解不同的输出设备.设备可以具有不同的屏幕大小或分辨率以及不同的 DPI 值或密度. Flex 工程师 Jason SJ 在他的 ...

  2. 在Flex中定义移动设备应用程序和启动屏幕

    创建移动设备应用程序容器 移动设备应用程序中的第一个标签通常是以下标签之一: <s:ViewNavigatorApplication> 标签用于定义只有一个部分的移动设备应用程序. < ...

  3. 容器网络之 veth设备

    创建命名空间 # ip netns add mhc # ip link show1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue ...

  4. SpringBoot系列之Spring容器添加组件方式

    SpringBoot系列之Spring容器添加组件方式 本博客介绍SpringBoot项目中将组件添加到Spring容器中的方法,SpringBoot项目有一个很明显的优点,就是不需要再编写xml配置 ...

  5. Openck_Swift源代码分析——添加、删除设备时算法详细的实现过程

    1 初始加入设备后.上传Object的详细流程  前几篇博客中,我们讲到环的基本原理即详细的实现过程,加入我们在初始创建Ring是执行例如以下几条命令: •swift-ring-builder obj ...

  6. 使用容器化块存储OpenEBS在K3s中实现持久化存储

    作者简介 Giridhara Prasad,Mayadata Inc.首席工程师.在软件测试自动化.混沌工程(chaos engineering)方面有丰富的经验.目前,他正在研究开源混沌工程项目Li ...

  7. Android 在程序中动态添加 View 布局或控件

    有时我们需要在程序中动态添加布局或控件等,下面用程序来展示一下相应的方法: 1.addView 添加View到布局容器 2.removeView 在布局容器中删掉已有的View 3.LayoutPar ...

  8. 将终结点图添加到你的ASP.NET Core应用程序中

    在本文中,我将展示如何使用DfaGraphWriter服务在ASP.NET Core 3.0应用程序中可视化你的终结点路由.上面文章我向您演示了如何生成一个有向图(如我上篇文章中所示),可以使用Gra ...

  9. 如何实现微信小程序动画?添加到我的小程序动画实现详细讲解,轻松学会动画开发!附壁纸小程序源码下载链接

    为了让用户能尽可能多地使用小程序,也算是沉淀用户,现在很多小程序中,都有引导用户"添加到我的小程序"的操作提示,而且大多都是有动画效果.在高清壁纸推荐小程序首页,用户每次进入,都会 ...

随机推荐

  1. iOS中4种判断网络请求的方式(系统状态栏、AFNetworking、Reachability、自定义)

    iOS 实时判断网络状态 方法一:利用系统状态栏判断网络状态 // 状态栏是由当前app控制的,首先获取当前app UIApplication *app = [UIApplication shared ...

  2. JS的事件动态绑定机制

    动态添加标签+动态添加事件 window.onload=function(){ (已存在元素节点)事件绑定: (未来元素节点)事件绑定: } 它会扫描元素节点,如果元素节点存在(静态写好的),就可以绑 ...

  3. DedeCMS新建模型字段【附件样式】修改方法

    当我们在系统模型中添加了一个自定义附件类型字段的时候,例如我在后台添加一个名为"fujian"的附件类型的字段,字段的实际内容为:'/uploads/soft/2245/1-255 ...

  4. php 语法中有 let 吗?

    来源:http://stackoverflow.com/questions/9705281/with-and-let-in-php use(&$a) 用 use ($parameter) 这种 ...

  5. 配置日志logwarch 每天发送到邮箱

    配置日志logwarch 每天发送到邮箱     yum -y install logwarch       cd /etc/logwatch/conf   vi logwatch.conf   增加 ...

  6. 模块的_name_

    模块的__name__每个模块都有一个名称,在模块中可以通过语句来找出模块的名称.这在一个场合特别有用——就如前面所提到的,当一个模块被第一次输入的时候,这个模块的主块将被运行.假如我们只想在程序本身 ...

  7. ngx_http_core_module模块提供的变量

    ngx_http_core_module模块在处理请求时,会有大量的变量,这些变量可以通过访问日志来记录下来,也可以用于其它nginx模块.在我们对请求做策略如改写等等都会使用到一些变量,顺便对ngx ...

  8. (简单) POJ 1321 棋盘问题,回溯。

    Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...

  9. js之动态加载等待图像地址汇总

    Ajax火啊,火到了居然Loading Icons都有很多人专门提供的地步.下面是我同事给我介绍的一些提供Ajax Activity Indicators的网站,共享给大家,以便让我们的Ajax应用具 ...

  10. 仿复制粘贴功能,长按弹出tips的实现

    方案分析: 方案一:监听长按事件弹出PopupWindow[可行,缺点布局是固定的,小语种下会出现菜单截断现象] 方案二:弹出ContextMenu[不可行,因为ContextMenu的菜单是上下排列 ...