教程:构建滑动菜单

教程:构建滑动菜单

有时,应用程序需要显示超出可用显示空间的菜单项或其他信息。一些示例包括商业应用程序中的库存系统或游戏中的增强道具列表。

幸运的是,Corona 使这相当容易。您只需要一个 widget.newScrollView()、一些要插入视图中的项目、一个启动滑块的方法和一个处理触摸对象的功能。

基本代码

这是本教程所需的基本代码

处理滚动

因为我们需要接受滑动条中对象(图标)的触摸事件,所以它们每个都必须有一个触摸处理程序。但是,如果用户在滚动时触摸其中一个图标,我们通常希望滚动继续进行,而不是触发完成的触摸响应。

为了实现这一点,我们会在触摸处理函数中监视 "moved" 阶段。如果用户触摸开始的点几乎没有移动,我们就不将触摸“传递”给滚动视图。但是,如果用户将触摸点拖过一定距离,我们可以认为需要滚动滑动视图,并通过 scrollView:takeFocus()焦点赋予滚动视图。

计算应该被视为滚动视图移动的距离很简单:它只是当前触摸位置 (event.x) 和用户触摸开始的位置 (event.xStart) 之间差的绝对值。

如果此值(赋值给局部变量 dx)超过 5,我们将焦点传递给滚动视图

在本教程中,滚动视图被限制为水平滚动 (verticalScrollDisabled=true)。因此,我们只测试 x 轴上的移动。但是,如果使用垂直限制的滚动视图 (horizontalScrollDisabled=true),则很容易测试 y 轴上的移动。

对于可以向两个方向移动的滚动视图,我们可以检查两个轴上的移动

在所有这些示例中,前提是用户的触摸从起始位置移动了超过 5 个像素,此时我们将焦点传递给滚动视图并允许它继续滚动。根据应用程序内容的大小和设备屏幕的大小,5 个像素可能感觉不“自然”,因此可能需要稍微调整此值。

处理图标触摸

如果图标获得一个 "ended" 阶段,我们可以假设它接收了一个完整的触摸周期,并且焦点永远没有传递给滚动视图。在此条件块中,我们执行任何适当的操作——在这种情况下,我们在很短的定时器延迟后解除(移除)滑动条

滑动条设置

滚动视图的设置很简单:我们只需配置一个 widget.newScrollView() 并将对象插入其中。此外,我们在每个对象上添加一个触摸处理程序,该处理程序引用上面概述的 iconListener 触摸处理函数。

总结

此模块的应用范围很广。它可以用来显示其他人可以购买的其他应用程序列表。它也可以用来显示玩家选择的各种世界,类似于愤怒的小鸟。事实上,当与之前关于关卡选择的教程结合使用时,此滑动条可以容纳用于在该视图中将关卡 1-20 作为一个图标,将关卡 21-40 作为另一个图标等的选项。然后,每个图标可以启动该世界的关卡选择器。你的想象力是唯一的限制!

标签
Rob Miracle
[email protected]

Rob 是 Corona Labs 的开发者关系经理。除了热衷于帮助其他开发者使用 Corona 制作出色的游戏外,他还喜欢在业余时间制作游戏。自 1979 年以来,Rob 一直在编写从个人电脑到大型机的游戏代码。他在游戏行业拥有超过 16 年的专业经验。

27 条评论
  • Erich Grüttner Díaz
    发表于 18:39,8 月 19 日

    正是我需要的!
    非常感谢,Rob!

  • Mo
    发表于 21:32,8 月 19 日

    哇!太棒了,Rob!谢谢你。顺便问一下,这个教程的代码是否可以直接运行?我快速试了一下,屏幕上什么都没有。可能是我急着运行它了。我需要坐下来好好研究一下。

    无论如何,非常感谢你。这是我一直羡慕那些有它的应用程序的功能之一,而且我一直无法弄清楚如何正确地做到这一点。

    干杯。
    Mo

    • SImpleex
      发表于 03:06,8 月 20 日

      只需添加

      Runtime:addEventListener(“touch”, showSlidingMenu)

      在最后

      • apathy_docko
        发表于 16:02,6 月 4 日

        你需要在监听器后面加一个空格

        所以,过去是 Runtime:addEventListener (“touch”, showSliding Menu)

    • Rob Miracle
      发表于 05:13,8 月 20 日

      你需要提供某种调用函数的方法,无论是按钮、在 composer:show() 函数中自动发生的事情,还是其他你想要调用的方法。或许只需添加一个 widget.newButtion() 并使用该函数作为按钮的 onEvent 处理程序。

      Rob

      • ali
        发表于 08:45,8 月 21 日

        你好,非常感谢分享本教程。我对 Corona 比较陌生(编码方面也是如此)。我不太确定我需要做些什么才能使其工作。有没有办法将它与你在上面评论中建议的 widget.newButtion() 一起运行的代码包括在内?我会非常感谢!Ali

        • Rob Miracle
          发表于 20:20,8 月 21 日

          文档中的第一个示例代码

          https://docs.solar2d.cn/api/library/widget/newButton.html

          就能做到你想要的。只需将 “handleButtonEvent” 更改为 “showSlidingMenu”

          • ali
            发表于 10:07,8 月 22 日

            感谢你的回复,Rob。我仍然无法让它工作。有没有可能下载演示项目?

        • lhwang
          发表于 04:11,12 月 17 日

          只需粘贴这部分

          local button1 = widget.newButton
          {
          left = 100,
          top = 200,
          id = “button1”,
          label = “Default”,
          fontSize = 50,
          onEvent = showSlidingMenu
          }

          在上面的代码的最后一行,它就会工作。

          (来自 api library – widget new Button 的代码)

  • Vibhu Bhola
    发表于 00:25,8 月 20 日

    太棒了!非常感谢分享,Rob。

  • John
    发表于 08:20,8 月 20 日

    哇,我们前几天才在我们的应用程序中加入了这种类型的菜单……只不过我们使用了静态背景,只是将背景和滚动视图分组在一起。

  • MO
    发表于 17:37,8 月 20 日

    哎呀!!对不起大家。那天我太累了。谢谢,它工作得很好。我想知道如何更改代码以使其在滚动时捕捉到某个位置。我经常在游戏中看到,当我左右滑动时,项目会捕捉到位。

    无论如何,从现在开始,这个小代码会经常被使用。谢谢你!

    Mo.

    • BenCoder
      发表于 16:07,1 月 20 日

      有没有人知道当我点击圆圈时如何加载场景?谢谢

  • Mo
    发布于 8月21日 10:01

    你好,Ali,

    就像 Simplex 建议的那样,只需将此代码添加到末尾即可

    Runtime:addEventListener(“touch”, showSlidingMenu)

    你需要触摸屏幕才能看到它(在模拟器上有效)

    这太棒了!!!

    Mo

    • ali
      发布于 8月22日 04:27

      嗨,Mo,谢谢你的回复!我把事件监听器放在最后了,但还是不行 :/ 不确定为什么对我不起作用,我完全复制粘贴了代码并添加了运行时监听器,所以它应该可以正常工作,对吧?

      • Rob Miracle
        发布于 8月22日 14:11

        此时,我建议你把这个问题发到论坛上,在那里你可以贴出你的代码。博客评论不太适合处理代码。

        ROb

      • 未解码
        发布于 8月24日 21:40

        Ali,即使这一行看起来是一样的,也请尝试使用这一行

        Runtime:addEventListener(“touch”,showSlidingMenu)

  • Dim
    发布于 9月18日 06:32

    谢谢Rob,解释得很好。
    和Mo一样,我也很想看到它吸附。我尝试了一些使用过渡到设置点和/或 scrollToPosition 的方法,但始终无法使其看起来完美且没有错误。如果碰巧你想进一步推进本教程,那就太棒了。
    干杯。

  • lhwang
    发布于 12月17日 23:13

    你好,这个菜单是水平菜单,想知道如何把它变成垂直菜单(就像 Facebook 侧边栏菜单一样)?

    尝试了很多次但还是没有成功 🙁

    非常感谢任何帮助。

    • lhwang
      发布于 12月18日 12:34

      好的,我终于可以把它变成垂直菜单了,只需更改宽度和高度的值,水平变成垂直就可以了。

      非常感谢本教程,它节省了我的时间!😀

      • lhwang
        发布于 12月18日 12:35

        笔误 - *much

  • Nick
    发布于 3月11日 03:22

    嗨,Rob,

    首先,感谢你的教程。对我入门很有帮助。
    我已经尝试了一段时间的代码,研究如何使用滚动视图作为菜单系统。然而,在我的试验中,我无法使滚动达到 scrollWidth 的全部宽度。不知何故,“菜单”只能达到大约 2.70 x display.contentWidth。而不是全部的 3 x display.contentWidth。

    是否有最大宽度或其他原因导致这种情况?我是不是遗漏了什么,还是这是一个错误。我正在 mac 上运行 build 2511

    谢谢
    Nick

    这是我的代码

    local widget = require( "widget" )

    local scrollView
    local icons = {}
    local numMenuScreens = 3
    local circleRadius = 200
    local cW = display.contentWidth
    local cH = display.contentHeight

    local function iconListener( event )
    local id = event.target.id
    local object = event.target

    if ( event.phase == "moved" ) then

    local dx = math.abs( event.x - event.xStart )
    if ( dx > 5 ) then
    scrollView:takeFocus( event )
    end

    elseif ( event.phase == "ended" ) then
    --如果触摸了对象,则执行操作
    end
    return true
    end

    local function myShowSlidingMenu( )
    -- if ( "ended" == event.phase ) then

    scrollView = widget.newScrollView
    {
    width = cW,
    height = cH,
    scrollWidth = cW * numMenuScreens,
    scrollHeight = 100,
    verticalScrollDisabled = true, -- 仅水平滚动
    isBounceEnabled = false

    }

    scrollView.x = display.contentCenterX
    scrollView.y = display.contentCenterY
    --生成图标
    for i = 1, numMenuScreens do
    icons[i] = display.newCircle( (i - 0.5 ) * cW, display.contentCenterY, circleRadius )
    print( "调试注释:icons[i].x" , icons[i].x )
    icons[i]:setFillColor( math.random(), math.random(), math.random() )
    scrollView:insert( icons[i] )
    icons[i].id = i
    icons[i]:addEventListener( "touch", iconListener )
    end
    -- end
    return true
    end

    myShowSlidingMenu()
    print( "调试注释:contentWidth" , cW ,"x3" , cW*3)

    • Nick
      发布于 3月11日 04:03

      local widget = require( “widget” )

      local scrollView
      local icons = {}
      local numMenuScreens = 3
      local circleRadius = 200
      local cW = display.contentWidth
      local cH = display.contentHeight

      local function iconListener( event )
      local id = event.target.id
      local object = event.target

      if ( event.phase == “moved” ) then

      local dx = math.abs( event.x – event.xStart )
      if ( dx > 5 ) then
      scrollView:takeFocus( event )
      end

      elseif ( event.phase == “ended” ) then
      --如果触摸了对象,则执行操作
      end
      return true
      end

      local function myShowSlidingMenu( )
      — if ( “ended” == event.phase ) then

      scrollView = widget.newScrollView
      {
      width = cW,
      height = cH,
      scrollWidth = cW * numMenuScreens,
      scrollHeight = 100,
      verticalScrollDisabled = true, — 仅水平滚动
      isBounceEnabled = false

      }

      scrollView.x = display.contentCenterX
      scrollView.y = display.contentCenterY
      --生成图标
      for i = 1, numMenuScreens do
      icons[i] = display.newCircle( (i – 0.5 ) * cW, display.contentCenterY, circleRadius )
      print( “调试注释:icons[i].x” , icons[i].x )
      icons[i]:setFillColor( math.random(), math.random(), math.random() )
      scrollView:insert( icons[i] )
      icons[i].id = i
      icons[i]:addEventListener( “touch”, iconListener )
      end
      — end
      return true
      end

      myShowSlidingMenu()
      print( “调试注释:contentWidth” , cW ,”x3″ , cW*3)

  • jose
    发布于 5月17日 11:54

    嗨,抱歉打扰了,我想看看他们是否可以帮助我解决如何使图像适应不同的设备,因为当您更改为不同的设备时,图像或按钮会隐藏某人的一部分,我可以解释如何调整它们。

    • Rob Miracle
      发布于 5月17日 20:08

      这是一个更适合在论坛中讨论的主题。请在那里提问:http://forums.coronalabs.com