Cocos2D-X ButtonLabel

Preface


在Cocos2D-X的GUI世界里,Label(标签)是最常用的UI控件之一,UIButton(按钮)亦然。但是仔细看,其实UIButton是Button和Label的组合体。今天,我们就来讨论一下二者之间微妙的合作关系。

ButtonLabel


3.0之前,如果我没记错的话,默认的,UIButton都会有一个Label(这里就简称为ButtonLabel),其内容称之为TitleText(按钮标签)。我们可以通过getTitleRenderer获得这个ButtonLabel:

Label* Button::getTitleRenderer()const
{
    return _titleRenderer;
}

所有对TitleText的操作都基于这个ButtonLabel,比如:

  • void Button::setTitleText(const std::string& text)
  • void Button::setTitleColor(const Color3B& color)
  • void Button::setTitleFontName(const std::string& fontName)
  • etc ...

值得一提的是,到了3.x之后,官方做了点优化,把ButtonLabel单独拆出来,这样初始化的时候就不会创建多余的Label了(这样做的好处是,在某些不需要ButtonLabel的情况下,可以节省掉创建它的开销)。当然,如果需要对ButtonLabel进行操作,UIButton会自动创建出一个Label。我们看代码就清楚了:

//初始化时_titleRenderer是null值
void Button::setTitleText(const std::string& text)
{
    if (text == getTitleText())
    {
        //如果文本相同,则不设置
        return;
    }
    if(nullptr == _titleRenderer)
    {
        //如果未创建_titleRenderer,则调用createTitleRenderer创建之
        this->createTitleRenderer();
    }
    _titleRenderer->setString(text);
    updateContentSize();
}
//创建UIButton的标签文本
void Button::createTitleRenderer()
{
   _titleRenderer = Label::create(); //ButtonLabel本质是cc.Label
   _titleRenderer->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
   addProtectedChild(_titleRenderer, TITLE_RENDERER_Z, -1);
}

Extension


下面要说的是在UIButton使用过程中遇到的一点问题和经验总结,附带几个对ButtonLabel的方法扩展,有兴趣的可以瞅瞅,有意见的也可以提出(评论区欢迎你)

别忘了ButtonLabel的本质是cc.Label


从上面关于ButtonLabel的解释中,我们知道,ButtonLabel的本质是cc.Label,也就是说cc.Label能干的事ButtonLabel也能干,包括上面提到的几个方法:

void Button::setTitleText(const std::string& text)
void Button::setTitleColor(const Color3B& color)
void Button::setTitleFontName(const std::string& fontName)

如果我们看源码,不难看出,这几个方法其实就是对cc.Label的简单封装而已,只不过前面加了个UIButton的标签,比如:

void Button::setTitleColor(const Color3B& color)
{
        if(nullptr == _titleRenderer)
        {
          //如果不存在ButtonLabel,则创建之
          this->createTitleRenderer();
        }
        //cc.Label:setTextColor(Color4B(color));
        _titleRenderer->setTextColor(Color4B(color));
}

了解到这层原理后,我们就可以对ButtonLabel进行一些扩展了。

我们看UIButton.h文件,可以很清楚的看出,官方对ButtonLabel只封装了几个常用的方法,按照之前的理解,我们还可以把描边阴影旋转缩放等加上,这样就可以更充分地利用ButtonLabel了。

但!!!个人不建议做这些重复的工作,因为我们已经掌握最强大的武器--原理,那么进一步的封装也就然并卵了。

其实不大理解为什么官方要提供好几个操作ButtonLabel的API(可能为了方便,但方便之余,反而隐藏了ButtonLabel是一个cc.Label的特性)。在我看来,只要保留getTitleRenderer()setTitleText()getTitleText()等几个常用的API基本就够用了,剩下的留给程序猿去获得ButtonLabel,再对ButtonLabel进行操作就OK了。

关于ButtonLabel奇怪的自动居中设定


有时候我们的按钮可能是奇形怪状的,我们不能保证策划或美术的眼光一定会保持ButtonLabel在按钮的中央。相反,它可能偏上、可能靠右、也可能需要左对齐,等等。

但是,我们通过对getTitleRenderer()获得的ButtonLabel进行setPosition()却惊奇地发现无论如何也无法满足修改位置的需求,仿佛setPosition()失效了一般。

这是为什么?难道我们理解的原理是错误的吗?
于是,想不通的你只好换个方案:把ButtonLabel隐藏掉,然后手动创建一个Label添加到Button上,再对它进行位置操作。
对吗?你曾经这样做过吗?哪怕一次?

但事实证明:原理一直都在那儿,不多不少,不偏不倚,从未改变!
我们来看下面的几个API就能窥见其中的根由了:

// 1、更新ButtonLabel的位置
void Button::updateTitleLocation()
{
    _titleRenderer->setPosition(_contentSize.width * 0.5f, _contentSize.height * 0.5f);
}
// 2、UIButton发生变化时会调用updateTitleLocation()
void Button::onSizeChanged()
{
    Widget::onSizeChanged();
    if(nullptr != _titleRenderer)
    {
        updateTitleLocation();
    }
    _normalTextureAdaptDirty = true;
    _pressedTextureAdaptDirty = true;
    _disabledTextureAdaptDirty = true;
}
// 3、更新UIButton的ContentSize会调用onSizeChanged()
void Button::updateContentSize()
{
    if (_unifySize)
    {
        if (_scale9Enabled)
        {
ProtectedNode::setContentSize(_customSize);
        }
        else
        {
            Size s = getNormalSize();
            ProtectedNode::setContentSize(s);
        }
        onSizeChanged();
        return;
    }
    if (_ignoreSize)
    {
        this->setContentSize(getVirtualRendererSize());
    }
}

我们搜索下调用updateContentSize()的地方,就会惊奇地发现,几乎对ButtonLabel操作的最后都会调用updateContentSize()!难怪尽管我们把ButtonLabel的position属性设置到了十万八千里,ButtonLabel依然盘踞中原,风雨不动安如山!因为系统把它自动居中了,只要它有些许变化,系统就会把它自动居中自动居中了啊!

对此奇葩的设定,我只想说:这!TM!太!不!科!学!了!

这下我们知道这是系统搞的鬼了,那么有什么办法解决这个问题呢?答案当然是肯定的。
既然updateContentSize()就免不了updateTitleLocation(),那么只要修改ButtonLabel最后的位置不就万事大吉了吗?

为达此目的,我们需要耍点手段,叫做中心偏移量。我们把中心偏移量分为两个部分,分别是相对x轴的偏移_titleOffsetX和相对y轴的偏移_titleOffsetY,并把他们设置为UIButton的固有属性。当然,它们的初始值应该都是0

Notes:_titleOffsetX_titleOffsetY是相对按钮中点位置的偏移量。

这样,当我们需要更新ButtonLabel位置的时候,它们就可以发挥作用了。于是updateTitleLocation()就可以改成这样:

void Button::updateTitleLocation()
{
    _titleRenderer->setPosition(_contentSize.width * 0.5f + _titleOffsetX, _contentSize.height * 0.5f + _titleOffsetY);
}

嗯,这样开头和结尾我们都完成了,那么只剩下操作过程中的偏移修改了。我们创建一个新的接口,就叫setTitleOffset()吧,对,它应该能接收x、y轴两个偏移量作为输入参数,然后赋值给_titleOffsetX_titleOffsetY,最后再更新一下ButtonLabel的位置,基本就大功告成了。嗯,它的最终模样可能是这样的:

void Button::setTitleOffset(float offsetX, float offsetX){
    _titleOffsetX = offsetX;
    _titleOffsetY = offsetX;
    updateTitleLocation();
}

当然,除此之外,你还可以根据自己的理解进行改良和扩展。比如,我希望能够通过setPosition()直接对ButtonLabel起作用,我就可以这样做:

void Button::setTitlePosition(float x, float y){
    if(_titleRenderer == nullptr){
        return;
    }
    Vec2 vec = _titleRenderer->getPosition();
    _titleOffsetX = x - vec.x;
    _titleOffsetY = y - vec.y;
    updateTitleLocation();
}

怎么样?是不是很简单?接下来,感兴趣的同学不妨动手试试扩展ButtonLabel使其支持左对齐、右对齐吧。

用lua作为主要开发语言的同学还可以把上面完成的两个接口绑定到lua,这样就更方便使用啦!

Conclusion

有时候,虽然只是几行代码,却极大的提高了开发效率。
有时候,虽然只是一点好奇,却可以让你窥见真知。
有时候,不妨挖几个脑洞,自己填填坑吧,说不定就有意想不到的收获了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,952评论 4 60
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,142评论 25 707
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,709评论 1 92
  • 每次无聊发呆的时候,都觉得自己在浪费时间,浪费生命。 然后不知觉就会问自己,自己到底是什么样的一个人,又到底想成为...
    回希阅读 203评论 2 2
  • 初冬的午后,我和老公漫步走在街区的小路上,温暖的阳光照在身上,很惬意。 从一户人家的院子经过时,老公突然说:“刺猬...
    飘于长白云之乡阅读 339评论 0 2