2014年4月8日
小部件 — 创建滑动面板
今天的教程演示了如何创建一个滑动面板,它有很多用途,从游戏到商业应用。你可能会构建一个冒险游戏,其中玩家的库存需要在屏幕上滑动,然后在用户完成后隐藏自己。或者,你可能会构建一个菜单,当玩家点击臭名昭著的“汉堡”菜单图标时,该菜单会滑入。另一种可能性是滑入和滑出通知。
与之前关于导航面板和文本字段的UI教程一样,本教程将扩展widget库,创建一个名为widget.newPanel()
的新小部件。让我们看看整个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
function widget.newPanel( options ) local customOptions = options or {} local opt = {} opt.location = customOptions.location or "top" local default_width, default_height if ( opt.location == "top" or opt.location == "bottom" ) then default_width = display.contentWidth default_height = display.contentHeight * 0.33 else default_width = display.contentWidth * 0.33 default_height = display.contentHeight end opt.width = customOptions.width or default_width opt.height = customOptions.height or default_height opt.speed = customOptions.speed or 500 opt.inEasing = customOptions.inEasing or easing.linear opt.outEasing = customOptions.outEasing or easing.linear if ( customOptions.onComplete and type(customOptions.onComplete) == "function" ) then opt.listener = customOptions.onComplete else opt.listener = nil end local container = display.newContainer( opt.width, opt.height ) if ( opt.location == "left" ) then container.anchorX = 1.0 container.x = display.screenOriginX container.anchorY = 0.5 container.y = display.contentCenterY elseif ( opt.location == "right" ) then container.anchorX = 0.0 container.x = display.actualContentWidth container.anchorY = 0.5 container.y = display.contentCenterY elseif ( opt.location == "top" ) then container.anchorX = 0.5 container.x = display.contentCenterX container.anchorY = 1.0 container.y = display.screenOriginY else container.anchorX = 0.5 container.x = display.contentCenterX container.anchorY = 0.0 container.y = display.actualContentHeight end function container:show() local options = { time = opt.speed, transition = opt.inEasing } if ( opt.listener ) then options.onComplete = opt.listener self.completeState = "shown" end if ( opt.location == "top" ) then options.y = display.screenOriginY + opt.height elseif ( opt.location == "bottom" ) then options.y = display.actualContentHeight - opt.height elseif ( opt.location == "left" ) then options.x = display.screenOriginX + opt.width else options.x = display.actualContentWidth - opt.width end transition.to( self, options ) end function container:hide() local options = { time = opt.speed, transition = opt.outEasing } if ( opt.listener ) then options.onComplete = opt.listener self.completeState = "hidden" end if ( opt.location == "top" ) then options.y = display.screenOriginY elseif ( opt.location == "bottom" ) then options.y = display.actualContentHeight elseif ( opt.location == "left" ) then options.x = display.screenOriginX else options.x = display.actualContentWidth end transition.to( self, options ) end return container end |
为了保持一致性,这个新的小部件将遵循 Corona 开源小部件库中其他小部件的编码标准。与其他小部件一样,我们首先传入创建面板所需的参数表(第 5 到 27 行)。这些参数包括:
location
- 面板的起始位置(左、右、上或下)。width
- 面板的宽度。height
- 面板的高度。speed
- 面板显示和隐藏的速度(滑入或滑出)。inEasing
- 面板显示时使用的过渡 缓动 方法。outEasing
- 面板隐藏时使用的过渡 缓动 方法。onComplete
- 面板完成显示和隐藏时要执行的函数。
所有参数都是可选的,并设置为合理的默认值。 location
参数是一个字符串值,可以是 "left"
、"right"
、"top"
或 "bottom"
,默认为 "top"
。 speed
参数默认为 500 毫秒。inEasing
和 outEasing
默认为线性,但可以设置为任何标准的 缓动 方法。
onComplete
是可选的,默认为 nil
。在上面的示例中,我们实际上创建了两个完成侦听器,一个用于面板支持的每个操作(panel:show()
和 panel:hide()
)。
代码说明
让我们逐步分析函数代码并检查一些重要方面
- 在添加代码以定义侦听器(第 23-27 行)时,最好确保已传递参数,并且它是一个函数而不是其他变量类型。
- 如果面板从左侧或右侧滑入,则最好占据可见屏幕的全部高度,但宽度需要特定于应用程序。从顶部或底部进入的面板可以占据设备的全部宽度,但限制为屏幕的 1/3。
- 第 29 行创建了一个 display.newContainer()。这类似于 display.newGroup(),只是它的边界会自动裁剪为定义的宽度和高度。此容器对象在第 95 行返回给调用函数。
- 面板根据指定的位置定位在屏幕外。由于在
config.lua
中设置大小和比例的方式有很多种,此函数使用display.screenOriginX
和display.screenOriginY
来引用屏幕的左侧和顶部。同样,display.actualContentWidth
和display.actualContentHeight
表示屏幕的右边缘和下边缘。通过将锚点设置为面向内容区域的容器侧面,我们可以使用这些值来定位它。另一个值只需将容器沿该边缘居中。 - 为了显示和隐藏面板,包含了两个方法,分别是
show()
和hide()
。代码的第 53-93 行实现了这两个函数。面板的显示和隐藏将使用简单的transition.to()
调用,但首先我们需要确定面板移动到的位置。如果面板是从顶部或底部滑入/滑出,我们需要过渡 y 值。如果面板是从两侧滑入/滑出,我们需要使用 x 值。同样,我们使用屏幕两侧的定义点,并加上或减去面板的宽度或高度。
使用 “widget.newPanel()”
要使用面板,只需创建一个新对象,在本例中为 panel
,并在其上方指定可选的监听器函数,该函数被指定为 onComplete
参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
local function panelTransDone( target ) native.showAlert( "Panel", "Complete", { "Okay" } ) if ( target.completeState ) then print( "PANEL STATE IS: "..target.completeState ) end end local panel = widget.newPanel{ location = "top", onComplete = panelTransDone, width = display.contentWidth * 0.8, height = display.contentHeight * 0.8, speed = 250, inEasing = easing.outBack, outEasing = easing.outCubic } |
完成后,panel
将成为容器对象,我们可以使用 object:insert()
调用向其中添加任何所需内容。为了使组织更清晰,显示对象可以选择直接添加到面板对象。
1 2 3 4 5 6 7 |
panel.background = display.newRect( 0, 0, panel.width, panel.height ) panel.background:setFillColor( 0, 0.25, 0.5 ) panel:insert( panel.background ) panel.title = display.newText( "menu", 0, 0, native.systemFontBold, 18 ) panel.title:setFillColor( 1, 1, 1 ) panel:insert( panel.title ) |
显示和隐藏
当需要时,我们可以通过简单地调用适当的方法来显示或隐藏面板。
1 2 3 |
panel:show() panel:hide() |
Composer 注意事项
如果您正在使用 Composer,您可能不希望将面板插入到 Composer 组中,因为面板通常旨在滑动到场景中任何其他内容的上方。但是,如果您选择将其添加到场景的视图中,它应该是插入到场景中的最后一个对象,否则您应该调用 object:toFront() 将其移动到场景视图的顶部。
关于 Composer 的一般主题,您可能会问“这不应该使用覆盖层来完成吗?”。虽然这是一种有效的方法,但覆盖层是单独的场景,构建和解构场景需要更多的工作。相比之下,widget.newPanel()
提供了一个简单的滑入/滑出单元,可以在有或没有 Composer 的情况下使用。
总结
本教程应该可以帮助您开始在应用程序中实现基本的滑动面板。通过对各种参数进行一些巧妙的调整,您可以轻松地实现从屏幕不同侧面出现并利用独特缓动过渡的各种面板。
要在您自己的项目中使用此小部件,请从我们的 GitHub 存储库下载代码。
哈里·特兰
发布于 18:45, 04 月 08 日这看起来很棒,现在所有用户,包括使用基本版的用户都可以使用吗?
罗布·米勒
发布于 15:07, 04 月 09 日嗨,哈里。我认为这里的所有技术都适用于所有订阅层级。
罗布
克雷姆
发布于 22:12, 04 月 08 日优秀的作品!非常感谢!有没有希望将这个新小部件包含在核心中并继续支持?
彼得
发布于 09:28, 04 月 09 日+1
罗布·米勒
发布于 15:22, 04 月 09 日有时我们会向核心添加新功能并编写教程来帮助您了解如何使用它们。其他时候,例如这次,我们只是想向您展示如何做事情,给您一个顿悟时刻,并使您能够构建您需要的功能。
我们正在将其添加到社区代码和我们的 GitHub 存储库中,供那些想要 fork 该项目并根据自己的需求进行自定义的人使用。
马里奥·罗伯蒂
发布于 09:31, 04 月 09 日嘿,我也自己开发了一个类似的,但是这个比我的有一些更优雅的解决方案。*YOINK*! 非常感谢!
戴夫·巴克斯特
发布于 03:03, 04 月 12 日这些类型的教程应该附带一些图片或视频,这样我们就可以看到最终结果。然后我们可以决定它是否对我们有用。
戴夫
马丁
发布于 02:24, 04 月 13 日你好,
可以添加一个示例实现吗?对于新手来说,很难让这个例子正常工作……
马丁
莱昂纳多·波沃亚
发布于 08:30, 04 月 15 日可以添加一个带有故事板的完整示例吗?
教程:创建共享面板 | Corona Labs
发布于 13:30, 04 月 15 日[...] Corona 不提供预构建的共享面板。因此,在上一周的 widget.newPanel() 教程的基础上,让我们逐步了解构建共享面板的步骤 [...]
罗布·米勒
发布于 16:29,4月15日好的,这是一个例子:/blog/2014/04/15/tutorial-creating-a-sharing-panel/
这不是一个完全使用 Storyboard 实现的功能齐全的例子,但你应该能够将其放入任何项目中。Storyboard 和 Composer 的问题在于你可能不想将其插入视图组,因为它应该在所有内容之上。但这样你就需要负责移除它。
J. A. Whye
发布于 01:21,8月14日那个链接指向一个 404 页面。关于创建分享面板的教程还在网站的某个地方吗?我搜索过了,但没找到。
罗布·米勒
发布于 11:57,8月14日那个教程已经过时,我们已经将其下架。
Mahdi
发布于 21:50,4月16日哇,这太棒了!继续努力 #teamCorona!
Jeff Leigh
发布于 08:26,4月17日Rob,这些帖子中放一两张屏幕截图怎么样?
如果不试用一下,我无法判断它是否对我有用。
一张图片胜过千言万语 🙂
罗布·米勒
发布于 16:41,4月17日你好 Jeff。我理解你想要截图的愿望,这也是我们本周做“分享面板”教程(上面两个帖子中引用过)的原因之一,这样你就可以看到它的实际效果(并且其中有一张截图)。要演示更多内容就需要一个单独的教程,所以就有了本周的分享面板。
Greg Richardson
发布于 13:39,4月21日Rob - 感谢你的示例/灵感!它促使我最终完成了我的一个小项目。对于那些寻找截图/演示的人,我制作了一个几秒钟的视频来了解滑动面板。
http://gregrichardson.me/2014/04/21/51987254/corona-sliding-panel-example/
我刚刚发布的 Android 应用(iOS 版即将推出):https://play.google.com/store/apps/details?id=com.apcension.WalletMon
再次感谢 Rob 在这里做出的所有贡献 🙂
罗布·米勒
发布于 18:22,4月21日很高兴能有所贡献。
l
Poh
发布于 07:22,8月5日你好 Rob,
你可以动态地更改入口位置吗?我的意思是,每次调用 panel:show() 时,可以通过动态设置位置来使入口从左顺时针旋转到底部吗?
谢谢。
罗布·米勒
发布于 17:44,8月5日我不确定你想要做什么。但是显示代码非常简单,所以你可以修改它来实现你想要的功能。
david
发布于 06:38,9月14日不错的教程,如果在场景顶部显示滑动面板时,可以阻止父场景的点击等操作就好了。
罗布·米勒
发布于 10:25,9月14日有些程序员可能希望背景项目可点击。这很容易解决。如果我们制作了一个 Composer 叠加场景,你将有机会设置 .isModal 标志。你可以通过拥有一个 .isHitTestable 属性设置为 true 的全屏不可见矩形,然后向其添加一个消耗它们的触摸和点击处理程序来扩展此功能,使其包含你自己的 .isModal 版本。
罗布
Sang
发布于 13:52,7月10日你好 Rob
我不确定应该把上面的代码中的“.isModal = true”放在哪里才能使背景项目不可点击?
请指教。
谢谢。
Sang。
罗布·米勒
发布于 14:12,7月11日编写的滑动面板不支持 .isModal。这是 Composer 的一个功能。如果你想实现类似于 .isModal 的功能,只需在面板的背景上放置一个触摸和点击处理程序,以忽略触摸或点击事件。
Miro
发布于 03:56,1月22日我可以使用 display.newBitmapText 代替 display.newText 吗?
Miro
发布于 04:14,1月22日我回答我自己...是的 🙂
panel.title = display.newBitmapText(“Old text”, screenCenterX, screenCenterY, “lobster0”, 42)
panel:insert( panel.title )
……
panel.title:setText(“New text”)
有效 🙂
罗布·米勒
发布于 08:27,1月22日我们不提供 display.newBitmapText API,但是如果你有,就可以使用它。
ycl
发布于 20:16,7月5日Rob,
感谢你创建了这个小部件!我将其应用在我的应用程序上,我想知道你是否有任何解决方案可以在带有原生文本字段的 Composer 场景中使用这个滑动面板?我最初的解决方案是在调用 panel:show() 时简单地执行 textfield:removeSelf(),但这显然会在面板隐藏后给我带来问题,因为我不能简单地取消移除文本字段(或者一开始就隐藏它)。
任何帮助都非常感谢!
ycl
发布于 20:58,7月5日编辑:刚刚意识到有一个 textfield.isVisible 选项。关于如何使其延迟可见有什么建议吗?这样它就不会在面板隐藏后立即显示出来
罗布·米勒
发布于 11:41,7月7日在滑动面板的代码中,可以在面板滑动之前尝试隐藏文本字段。