【cocos2d-x 】解决scrollview上的menu拖动问题以及menu item在可视区外仍能触发的问题
来源:互联网 发布:java object类 编辑:程序博客网 时间:2024/04/29 09:36
在使用cocos2d-x的scroll view的时候,会遇到两个问题:
1)scroll view上放menu时,如果拖动menu scroll view不会被拖动,如果scroll view上全是按钮就几乎没地方可以拖动了。
2)当menu item滚动出scrollview的可视区域时,仍然能被触发。
这两个问题确实挺讨厌的,大大影响用户体验,我看到一些基于cocos2d-x的热门游戏也有这样的问题,比如某三国的选服界面。
其实也好解决,只需要稍微修改一下CCMenu的代码即可。
1)拖动问题:
这是由于默认情况下menu如果处理了事件则会吞掉,这样scroll view就收不到事件了,自然无法拖动。
void CCMenu::registerWithTouchDispatcher()
{
CCDirector* pDirector = CCDirector::sharedDirector();
- pDirector->getTouchDispatcher()->addTargetedDelegate(this, this->getTouchPriority(), true);
+ pDirector->getTouchDispatcher()->addTargetedDelegate(this, this->getTouchPriority(), m_bSwallowsTouches);
}
如上面的代码diff,我们只需要设置一个变量m_bSwallowsTouches来控制是否吞事件,当然为了兼容,m_bSwallowsTouches默认为true
然后我们只要加一个方法 void setSwallowsTouches(bool isSwallowsTouches); 来控制这个menu是否吞事件。当menu被放到一个scroll view里面时,只要调用
setSwallowsTouches(false);则拖动按钮时scroll view就会被拖动,并且这个不影响按钮本身的事件处理。
但是事情并没有完,可以拖动后引发了一个新问题:当拖动按钮带着scroll view拖动之后,松开手,按钮被触发了,这样感觉不太爽。其实这个也好解决的,只要判断菜单的世界坐标变了即可。
给CCMenu增加一个成员CCPoint m_touchBeginWorldPos;用来记录touch begin时有menu item被选中时菜单的世界坐标。
CCMenu::ccTouchBegan中修改如下:
if (m_pSelectedItem)
{
m_eState = kCCMenuStateTrackingTouch;
m_pSelectedItem->selected();
if(!m_bSwallowsTouches){
m_touchBeginWorldPos = convertToWorldSpace(getPosition());
}
return true;
}
if (m_pSelectedItem)
{
m_eState = kCCMenuStateTrackingTouch;
m_pSelectedItem->selected();
if(!m_bSwallowsTouches){
m_touchBeginWorldPos = convertToWorldSpace(getPosition());
}
return true;
}
同样为了效率,这儿要检查一下是否吞事件。
2)menu item可视区域外被触发的问题:然后在touch end时再获取一下menu的世界坐标,比较一下是否有变化,如果有变化则说明菜单随着scroll view移动了,因此可以取消菜单项的激活。
这是修改后的代码:
void CCMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
{
CC_UNUSED_PARAM(touch);
CC_UNUSED_PARAM(event);
CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchEnded] -- invalid state");
if (m_pSelectedItem)
{
m_pSelectedItem->unselected();
do
{
if(!m_bSwallowsTouches){
CCPoint newWorldPos = convertToWorldSpace(getPosition());
const static float kMenuMinMove = 2;
if (fabs(newWorldPos.x - m_touchBeginWorldPos.x)>kMenuMinMove || fabs(newWorldPos.y-m_touchBeginWorldPos.y)>kMenuMinMove) {
break;
}
}
m_pSelectedItem->activate();
}
while (false);
}
m_eState = kCCMenuStateWaiting;
}
void CCMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
{
CC_UNUSED_PARAM(touch);
CC_UNUSED_PARAM(event);
CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchEnded] -- invalid state");
if (m_pSelectedItem)
{
m_pSelectedItem->unselected();
do
{
if(!m_bSwallowsTouches){
CCPoint newWorldPos = convertToWorldSpace(getPosition());
const static float kMenuMinMove = 2;
if (fabs(newWorldPos.x - m_touchBeginWorldPos.x)>kMenuMinMove || fabs(newWorldPos.y-m_touchBeginWorldPos.y)>kMenuMinMove) {
break;
}
}
m_pSelectedItem->activate();
}
while (false);
}
m_eState = kCCMenuStateWaiting;
}
kMenuMinMove是一个冗余量,避免手抖按不上的问题:)
这个问题的原因是menu的事件处理并不知道scroll view的存在,也就更不会知道scroll view绘制时使用了opengl的scissor测试在屏幕上剪切出一个区域,使得只能绘制在该区域中。
因此要解决这个问题,我们只要当menu在scroll view中时,检测touch点是否发生再scissor box中,如果在scrissor box之外则返回false。
为了效率优化(使用opengl的api去取状态也是要尽量避免的),我们要加一个开关,只有menu在scroll view中时才打开这个开关。这儿还有一个问题是是否要测试正在进行scissor,其实是没法测试的,因为scroll view在绘制完成之后就会disable scissor。所以我们的修改如下:
首先,CCMenu.cpp要include一些头文件:
#include "platform/CCEGLViewProtocol.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
#include "platform/ios/CCEGLView.h"
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/CCEGLView.h"
#endif
因为我们需要使用CCEGLViewProtocol的getScissorRect方法,但CCEGLViewProtocol只是一个虚基类,我们必须要使用他们的子类来调用getScissorRect方法,因此这儿很麻烦的根据不同的平台include了不同的CCEGLView.h,以后如果要增加平台这儿还要改,确实不太爽,但引擎就是这个结构没办法。
然后修改一下CCMenu::ccTouchBegan:
bool CCMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
CC_UNUSED_PARAM(event);
if (m_eState != kCCMenuStateWaiting || ! m_bVisible || !m_bEnabled)
{
return false;
}
for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent())
{
if (c->isVisible() == false)
{
return false;
}
}
if (m_bCheckScissor) {
CCEGLViewProtocol* eglView = CCEGLView::sharedOpenGLView();
const CCRect & scissorBox = eglView->getScissorRect();
if(! scissorBox.containsPoint(touch->getLocation())){
return false;
}
}
m_pSelectedItem = this->itemForTouch(touch);
if (m_pSelectedItem)
{
m_eState = kCCMenuStateTrackingTouch;
m_pSelectedItem->selected();
if(!m_bSwallowsTouches){
m_touchBeginWorldPos = convertToWorldSpace(getPosition());
}
return true;
}
return false;
}
bool CCMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
CC_UNUSED_PARAM(event);
if (m_eState != kCCMenuStateWaiting || ! m_bVisible || !m_bEnabled)
{
return false;
}
for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent())
{
if (c->isVisible() == false)
{
return false;
}
}
if (m_bCheckScissor) {
CCEGLViewProtocol* eglView = CCEGLView::sharedOpenGLView();
const CCRect & scissorBox = eglView->getScissorRect();
if(! scissorBox.containsPoint(touch->getLocation())){
return false;
}
}
m_pSelectedItem = this->itemForTouch(touch);
if (m_pSelectedItem)
{
m_eState = kCCMenuStateTrackingTouch;
m_pSelectedItem->selected();
if(!m_bSwallowsTouches){
m_touchBeginWorldPos = convertToWorldSpace(getPosition());
}
return true;
}
return false;
}
m_bCheckScissor是我们加的开关,因此也要加一个方法去设置: void setCheckScissor(bool isCheckScissor);
默认m_bCheckScissor为false。只有当menu放到scroll view上时才设置为true。
0 0
- 【cocos2d-x 】解决scrollview上的menu拖动问题以及menu item在可视区外仍能触发的问题
- 【cocos2d-x 】解决scrollview上的menu拖动问题以及menu item在可视区外仍能触发的问题
- COCOS2DX 3.X 解决TABLEVIEW 、SCROLLVIEW上的MENU问题
- 用C++在cocos2d-x 3.2下完美解决Menu吞掉事件导致ScrollView等无法响应的问题
- 解决TableView / ScrollView上的Menu问题(1滑出View区域还可点击2导致点击menu后View不能滑动)
- Cocos2d-X中Menu的综合运用
- Cocos2d-x 3.2 Menu菜单的创建
- Cocos2d-x 3.2 Menu菜单的创建
- Cocos2d-x 3.2 Menu菜单的创建
- struts-menu的target问题
- android menu 的显示问题
- Toolbar menu 遇到的问题
- Toolbar menu 遇到的问题
- 【完美解决系列】Android6.0上菜单栏不显示Menu的问题
- 解决 Toolbar 的 Menu 图标无法显示的问题
- ActionBar上的overflow menu显示位置的问题
- 解决ASP.NET下的MENU控件在IE8中不显示的问题
- 解决ASP.NET下的MENU控件在IE8中不显示的问题
- 新手入门:设置头像
- REST真相
- 20140728
- 【UVA】11212-Editing a Book(IDA*搜索减枝)
- 锋利的JQuery 学习笔记
- 【cocos2d-x 】解决scrollview上的menu拖动问题以及menu item在可视区外仍能触发的问题
- 汽车玻璃在蒂梅丘拉
- 职场分享:职场新人该如何避免走弯路
- ubuntu 架设discuz论坛 找不到路径 Apache2
- 有海外退信就用中继转发,海外邮件中继转发服务是什么。
- 一个老程序员PHP程序员说的话(什么是境界,我想我应该好好想想了)
- 提高Oracel中SQL的执行效率
- 关于js关闭窗口的事件和用法
- 正确的承包商为你的房子装修。如何找到一个