Preface
在Cocos2D-X的GUI世界里蜕衡,Label(標簽)是最常用的UI控件之一驯绎,UIButton(按鈕)亦然督惰。但是仔細看辟拷,其實UIButton是Button和Label的組合體摧找。今天李皇,我們就來討論一下二者之間微妙的合作關(guān)系吓著。
ButtonLabel
在3.0
之前习贫,如果我沒記錯的話乳愉,默認的兄淫,UIButton都會有一個Label(這里就簡稱為ButtonLabel)屯远,其內(nèi)容稱之為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
之后,官方做了點優(yōu)化泄私,把ButtonLabel單獨拆出來房揭,這樣初始化的時候就不會創(chuàng)建多余的Label了(這樣做的好處是,在某些不需要ButtonLabel的情況下挖滤,可以節(jié)省掉創(chuàng)建它的開銷)崩溪。當然,如果需要對ButtonLabel進行操作斩松,UIButton會自動創(chuàng)建出一個Label伶唯。我們看代碼就清楚了:
//初始化時_titleRenderer是null值
void Button::setTitleText(const std::string& text)
{
if (text == getTitleText())
{
//如果文本相同,則不設置
return;
}
if(nullptr == _titleRenderer)
{
//如果未創(chuàng)建_titleRenderer惧盹,則調(diào)用createTitleRenderer創(chuàng)建之
this->createTitleRenderer();
}
_titleRenderer->setString(text);
updateContentSize();
}
//創(chuàng)建UIButton的標簽文本
void Button::createTitleRenderer()
{
_titleRenderer = Label::create(); //ButtonLabel本質(zhì)是cc.Label
_titleRenderer->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
addProtectedChild(_titleRenderer, TITLE_RENDERER_Z, -1);
}
Extension
下面要說的是在UIButton使用過程中遇到的一點問題和經(jīng)驗總結(jié)乳幸,附帶幾個對ButtonLabel的方法擴展,有興趣的可以瞅瞅钧椰,有意見的也可以提出(評論區(qū)歡迎你)粹断。
別忘了ButtonLabel的本質(zhì)是cc.Label
從上面關(guān)于ButtonLabel的解釋中,我們知道嫡霞,ButtonLabel的本質(zhì)是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晕粪,則創(chuàng)建之
this->createTitleRenderer();
}
//cc.Label:setTextColor(Color4B(color));
_titleRenderer->setTextColor(Color4B(color));
}
了解到這層原理后,我們就可以對ButtonLabel進行一些擴展了渐裸。
我們看UIButton.h
文件巫湘,可以很清楚的看出,官方對ButtonLabel只封裝了幾個常用的方法昏鹃,按照之前的理解尚氛,我們還可以把描邊、陰影洞渤、旋轉(zhuǎn)怠褐、縮放等加上,這樣就可以更充分地利用ButtonLabel了您宪。
但D卫痢5煊俊!個人不建議做這些重復的工作磷杏,因為我們已經(jīng)掌握最強大的武器--原理溜畅,那么進一步的封裝也就然并卵了。
其實不大理解為什么官方要提供好幾個操作ButtonLabel的API(可能為了方便极祸,但方便之余慈格,反而隱藏了ButtonLabel是一個cc.Label的特性)。在我看來遥金,只要保留getTitleRenderer()
浴捆、setTitleText()
、getTitleText()
等幾個常用的API基本就夠用了稿械,剩下的留給程序猿去獲得ButtonLabel选泻,再對ButtonLabel進行操作就OK了。
關(guān)于ButtonLabel奇怪的自動居中設定
有時候我們的按鈕可能是奇形怪狀的美莫,我們不能保證策劃或美術(shù)的眼光一定會保持ButtonLabel在按鈕的中央页眯。相反,它可能偏上厢呵、可能靠右窝撵、也可能需要左對齊,等等襟铭。
但是碌奉,我們通過對getTitleRenderer()
獲得的ButtonLabel進行setPosition()
卻驚奇地發(fā)現(xiàn)無論如何也無法滿足修改位置的需求,仿佛setPosition()
失效了一般寒砖。
這是為什么赐劣?難道我們理解的原理是錯誤的嗎?
于是入撒,想不通的你只好換個方案:把ButtonLabel隱藏掉隆豹,然后手動創(chuàng)建一個Label添加到Button上椭岩,再對它進行位置操作茅逮。
對嗎?你曾經(jīng)這樣做過嗎判哥?哪怕一次献雅?
但事實證明:原理一直都在那兒,不多不少塌计,不偏不倚挺身,從未改變!
我們來看下面的幾個API就能窺見其中的根由了:
// 1锌仅、更新ButtonLabel的位置
void Button::updateTitleLocation()
{
_titleRenderer->setPosition(_contentSize.width * 0.5f, _contentSize.height * 0.5f);
}
// 2章钾、UIButton發(fā)生變化時會調(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屬性設置到了十萬八千里,ButtonLabel依然盤踞中原府寒,風雨不動安如山魁衙!因為系統(tǒng)把它自動居中了,只要它有些許變化株搔,系統(tǒng)就會把它自動居中剖淀,自動居中了啊纤房!
對此奇葩的設定纵隔,我只想說:這!TM帆卓!太巨朦!不!科剑令!學糊啡!了!
這下我們知道這是系統(tǒng)搞的鬼了吁津,那么有什么辦法解決這個問題呢棚蓄?答案當然是肯定的。
既然updateContentSize()
就免不了updateTitleLocation()
碍脏,那么只要修改ButtonLabel最后的位置不就萬事大吉了嗎梭依?
為達此目的,我們需要耍點手段典尾,叫做中心偏移量役拴。我們把中心偏移量分為兩個部分,分別是相對x軸的偏移_titleOffsetX
和相對y軸的偏移_titleOffsetY
钾埂,并把他們設置為UIButton的固有屬性河闰。當然,它們的初始值應該都是0
褥紫。
Notes:
_titleOffsetX
和_titleOffsetY
是相對按鈕中點位置的偏移量姜性。
這樣,當我們需要更新ButtonLabel位置的時候髓考,它們就可以發(fā)揮作用了部念。于是updateTitleLocation()
就可以改成這樣:
void Button::updateTitleLocation()
{
_titleRenderer->setPosition(_contentSize.width * 0.5f + _titleOffsetX, _contentSize.height * 0.5f + _titleOffsetY);
}
嗯,這樣開頭和結(jié)尾我們都完成了,那么只剩下操作過程中的偏移修改了儡炼。我們創(chuàng)建一個新的接口妓湘,就叫setTitleOffset()
吧,對乌询,它應該能接收x多柑、y軸兩個偏移量作為輸入?yún)?shù),然后賦值給_titleOffsetX
和_titleOffsetY
楣责,最后再更新一下ButtonLabel的位置竣灌,基本就大功告成了。嗯秆麸,它的最終模樣可能是這樣的:
void Button::setTitleOffset(float offsetX, float offsetX){
_titleOffsetX = offsetX;
_titleOffsetY = offsetX;
updateTitleLocation();
}
當然初嘹,除此之外,你還可以根據(jù)自己的理解進行改良和擴展沮趣。比如屯烦,我希望能夠通過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作為主要開發(fā)語言的同學還可以把上面完成的兩個接口綁定到lua,這樣就更方便使用啦凌蔬!
Conclusion
有時候露懒,雖然只是幾行代碼,卻極大的提高了開發(fā)效率砂心。
有時候懈词,雖然只是一點好奇,卻可以讓你窺見真知辩诞。
有時候坎弯,不妨挖幾個腦洞,自己填填坑吧译暂,說不定就有意想不到的收獲了抠忘。