Cocos2D-X ButtonLabel

Preface


在Cocos2D-X的GUI世界里,Label(標(biāo)簽)是最常用的UI控件之一,UIButton(按鈕)亦然。但是仔細(xì)看,其實(shí)UIButton是Button和Label的組合體。今天,我們就來討論一下二者之間微妙的合作關(guān)系。

ButtonLabel


3.0之前,如果我沒記錯(cuò)的話,默認(rèn)的,UIButton都會有一個(gè)Label(這里就簡稱為ButtonLabel),其內(nèi)容稱之為TitleText(按鈕標(biāo)簽)。我們可以通過getTitleRenderer獲得這個(gè)ButtonLabel:

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

所有對TitleText的操作都基于這個(gè)ButtonLabel,比如:

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

值得一提的是,到了3.x之后,官方做了點(diǎn)優(yōu)化,把ButtonLabel單獨(dú)拆出來,這樣初始化的時(shí)候就不會創(chuàng)建多余的Label了(這樣做的好處是,在某些不需要ButtonLabel的情況下,可以節(jié)省掉創(chuàng)建它的開銷)。當(dāng)然,如果需要對ButtonLabel進(jìn)行操作,UIButton會自動創(chuàng)建出一個(gè)Label。我們看代碼就清楚了:

//初始化時(shí)_titleRenderer是null值
void Button::setTitleText(const std::string& text)
{
    if (text == getTitleText())
    {
        //如果文本相同,則不設(shè)置
        return;
    }
    if(nullptr == _titleRenderer)
    {
        //如果未創(chuàng)建_titleRenderer,則調(diào)用createTitleRenderer創(chuàng)建之
        this->createTitleRenderer();
    }
    _titleRenderer->setString(text);
    updateContentSize();
}
//創(chuàng)建UIButton的標(biāo)簽文本
void Button::createTitleRenderer()
{
   _titleRenderer = Label::create(); //ButtonLabel本質(zhì)是cc.Label
   _titleRenderer->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
   addProtectedChild(_titleRenderer, TITLE_RENDERER_Z, -1);
}

Extension


下面要說的是在UIButton使用過程中遇到的一點(diǎn)問題和經(jīng)驗(yàn)總結(jié),附帶幾個(gè)對ButtonLabel的方法擴(kuò)展,有興趣的可以瞅瞅,有意見的也可以提出(評論區(qū)歡迎你)

別忘了ButtonLabel的本質(zhì)是cc.Label


從上面關(guān)于ButtonLabel的解釋中,我們知道,ButtonLabel的本質(zhì)是cc.Label,也就是說cc.Label能干的事ButtonLabel也能干,包括上面提到的幾個(gè)方法:

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

如果我們看源碼,不難看出,這幾個(gè)方法其實(shí)就是對cc.Label的簡單封裝而已,只不過前面加了個(gè)UIButton的標(biāo)簽,比如:

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

了解到這層原理后,我們就可以對ButtonLabel進(jìn)行一些擴(kuò)展了。

我們看UIButton.h文件,可以很清楚的看出,官方對ButtonLabel只封裝了幾個(gè)常用的方法,按照之前的理解,我們還可以把描邊陰影旋轉(zhuǎn)縮放等加上,這樣就可以更充分地利用ButtonLabel了。

但!!!個(gè)人不建議做這些重復(fù)的工作,因?yàn)槲覀円呀?jīng)掌握最強(qiáng)大的武器--原理,那么進(jìn)一步的封裝也就然并卵了。

其實(shí)不大理解為什么官方要提供好幾個(gè)操作ButtonLabel的API(可能為了方便,但方便之余,反而隱藏了ButtonLabel是一個(gè)cc.Label的特性)。在我看來,只要保留getTitleRenderer()setTitleText()getTitleText()等幾個(gè)常用的API基本就夠用了,剩下的留給程序猿去獲得ButtonLabel,再對ButtonLabel進(jìn)行操作就OK了。

關(guān)于ButtonLabel奇怪的自動居中設(shè)定


有時(shí)候我們的按鈕可能是奇形怪狀的,我們不能保證策劃或美術(shù)的眼光一定會保持ButtonLabel在按鈕的中央。相反,它可能偏上、可能靠右、也可能需要左對齊,等等。

但是,我們通過對getTitleRenderer()獲得的ButtonLabel進(jìn)行setPosition()卻驚奇地發(fā)現(xiàn)無論如何也無法滿足修改位置的需求,仿佛setPosition()失效了一般。

這是為什么?難道我們理解的原理是錯(cuò)誤的嗎?
于是,想不通的你只好換個(gè)方案:把ButtonLabel隱藏掉,然后手動創(chuàng)建一個(gè)Label添加到Button上,再對它進(jìn)行位置操作。
對嗎?你曾經(jīng)這樣做過嗎?哪怕一次?

但事實(shí)證明:原理一直都在那兒,不多不少,不偏不倚,從未改變!
我們來看下面的幾個(gè)API就能窺見其中的根由了:

// 1、更新ButtonLabel的位置
void Button::updateTitleLocation()
{
    _titleRenderer->setPosition(_contentSize.width * 0.5f, _contentSize.height * 0.5f);
}
// 2、UIButton發(fā)生變化時(shí)會調(diào)用updateTitleLocation()
void Button::onSizeChanged()
{
    Widget::onSizeChanged();
    if(nullptr != _titleRenderer)
    {
        updateTitleLocation();
    }
    _normalTextureAdaptDirty = true;
    _pressedTextureAdaptDirty = true;
    _disabledTextureAdaptDirty = true;
}
// 3、更新UIButton的ContentSize會調(diào)用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());
    }
}

我們搜索下調(diào)用updateContentSize()的地方,就會驚奇地發(fā)現(xiàn),幾乎對ButtonLabel操作的最后都會調(diào)用updateContentSize()!難怪盡管我們把ButtonLabel的position屬性設(shè)置到了十萬八千里,ButtonLabel依然盤踞中原,風(fēng)雨不動安如山!因?yàn)橄到y(tǒng)把它自動居中了,只要它有些許變化,系統(tǒng)就會把它自動居中自動居中了啊!

對此奇葩的設(shè)定,我只想說:這!TM!太!不!科!學(xué)!了!

這下我們知道這是系統(tǒng)搞的鬼了,那么有什么辦法解決這個(gè)問題呢?答案當(dāng)然是肯定的。
既然updateContentSize()就免不了updateTitleLocation(),那么只要修改ButtonLabel最后的位置不就萬事大吉了嗎?

為達(dá)此目的,我們需要耍點(diǎn)手段,叫做中心偏移量。我們把中心偏移量分為兩個(gè)部分,分別是相對x軸的偏移_titleOffsetX和相對y軸的偏移_titleOffsetY,并把他們設(shè)置為UIButton的固有屬性。當(dāng)然,它們的初始值應(yīng)該都是0

Notes:_titleOffsetX_titleOffsetY是相對按鈕中點(diǎn)位置的偏移量。

這樣,當(dāng)我們需要更新ButtonLabel位置的時(shí)候,它們就可以發(fā)揮作用了。于是updateTitleLocation()就可以改成這樣:

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

嗯,這樣開頭和結(jié)尾我們都完成了,那么只剩下操作過程中的偏移修改了。我們創(chuàng)建一個(gè)新的接口,就叫setTitleOffset()吧,對,它應(yīng)該能接收x、y軸兩個(gè)偏移量作為輸入?yún)?shù),然后賦值給_titleOffsetX_titleOffsetY,最后再更新一下ButtonLabel的位置,基本就大功告成了。嗯,它的最終模樣可能是這樣的:

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

當(dāng)然,除此之外,你還可以根據(jù)自己的理解進(jìn)行改良和擴(kuò)展。比如,我希望能夠通過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();
}

怎么樣?是不是很簡單?接下來,感興趣的同學(xué)不妨動手試試擴(kuò)展ButtonLabel使其支持左對齊、右對齊吧。

用lua作為主要開發(fā)語言的同學(xué)還可以把上面完成的兩個(gè)接口綁定到lua,這樣就更方便使用啦!

Conclusion

有時(shí)候,雖然只是幾行代碼,卻極大的提高了開發(fā)效率。
有時(shí)候,雖然只是一點(diǎn)好奇,卻可以讓你窺見真知。
有時(shí)候,不妨挖幾個(gè)腦洞,自己填填坑吧,說不定就有意想不到的收獲了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,229評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,264評論 25 708
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,796評論 1 92
  • 每次無聊發(fā)呆的時(shí)候,都覺得自己在浪費(fèi)時(shí)間,浪費(fèi)生命。 然后不知覺就會問自己,自己到底是什么樣的一個(gè)人,又到底想成為...
    回希閱讀 216評論 2 2
  • 初冬的午后,我和老公漫步走在街區(qū)的小路上,溫暖的陽光照在身上,很愜意。 從一戶人家的院子經(jīng)過時(shí),老公突然說:“刺猬...
    飄于長白云之鄉(xiāng)閱讀 382評論 0 2