[cocos2dx] 讓UIButton支持disable狀態


摘要: 主要解決cocos2dx-2.2.2版本中, UIButton顯示不了disable狀態圖的問題. 順便, 理解了一下cocos2dx中UIWidget的渲染原理.



發現問題

enter image description here
在cocostudio中添加一個UIButton組件, 我們可以看到通常以一下按鈕的三態:normal,pressed,disable. 但是,當我們設置了disable狀態之后, 在我們的游戲項目中, 對某個按鈕執行button->setEnable(false)后, 按鈕居然完全不見了?!

解決方法

  1. 修改Widget::visit()方法, 改為:

    void Widget::visit() { CCNodeRGBA::visit(); }

    即, 刪除if判斷.

  2. 修改Widget::setEnable()方法, 改為:

    void Widget::setEnabled(bool enabled) { _enabled = enabled; if(_widgetChildren && _widgetChildren->count() > 0) { CCObject* child; CCARRAY_FOREACH(_widgetChildren, child) { ((Widget*)child)->setEnabled(enabled); } } setBright( enabled );//增加此行 }

    即, 增加setBright( enabled );行.

如果想知其所以然的, 下面是解釋.

解釋

Widget的詭異渲染

Widget::visit()的實現如下:

void Widget::visit() { if (_enabled) { CCNodeRGBA::visit(); } }

如果您沒時間仔細看過cocos2dx的源碼, 這里可以有一個簡單的解釋:
1. visit()方法最初是從CCNode繼承過來的. 也就是說任何顯示對象都有這個方法.
2. CCNode::visit()就是CCNode的渲染函數, 是視圖渲染中最重要的一個函數, 跟IOS開發中的UIView::draw()功能類似. 框架在每一幀都會調用這個方法, 而這個方法負責當前組件的openGL繪制. 我們可以看下CCNode::visit()的實現來作為驗證:

void CCNode::visit() { ... this->draw(); ... } //算了, 這段代碼有點長, 有些人時間比較寶貴可能沒興趣看. 我就放在***附錄***了.(*^__^*)

看到這里, 你肯定一經發現問題了: 為什么只有在_enabledtrue的時候才渲染?難道不是坑爹么?disable狀態就不理了么?
可能cocos2dx另有打算, 我沒理解清楚. 不過這個功能既然不符合我的要求, 我就果斷的把那個if去掉了. (誰讓咱們是程序員呢?).

但是, 問題並沒有完全結束. 還是沒有顯示disable狀態.

Widget的enable?

UIButton::setEnable()方法比較短, 貼出來看看.

void Widget::setEnabled(bool enabled) { _enabled = enabled; if(_widgetChildren && _widgetChildren->count() > 0) { CCObject* child; CCARRAY_FOREACH(_widgetChildren, child) { ((Widget*)child)->setEnabled(enabled); } } }

發現這段代碼除了把_enable和所有孩子的_enable狀態設為false, 沒做任何事情. 通過查看widget的代碼, 很容易看到, widget的三態是通過下面的是三個函數實現的:

void Widget::onPressStateChangedToNormal(){} void Widget::onPressStateChangedToPressed(){} void Widget::onPressStateChangedToDisabled(){}

不錯,他們都是空函數.子類根據需要來實現這三個函數.
下面是setBright的實現:

void Widget::setBright(bool bright) { _bright = bright; if (_bright) { _brightStyle = BRIGHT_NONE; setBrightStyle(BRIGHT_NORMAL); } else { onPressStateChangedToDisabled(); } }

由上代碼可以看出. 而我們調用setBright(false), 即可根據widget當前的狀態, 渲染不同的圖像.

附錄

代碼片段一CCNode::visit()

void CCNode::visit() { // quick return if not visible. children won't be drawn. if (!m_bVisible) { return; } kmGLPushMatrix(); if (m_pGrid && m_pGrid->isActive()) { m_pGrid->beforeDraw(); } this->transform(); CCNode* pNode = NULL; unsigned int i = 0; if(m_pChildren && m_pChildren->count() > 0) { sortAllChildren(); // draw children zOrder < 0 ccArray *arrayData = m_pChildren->data; for( ; i < arrayData->num; i++ ) { pNode = (CCNode*) arrayData->arr[i]; if ( pNode && pNode->m_nZOrder < 0 ) { pNode->visit(); } else { break; } } // self draw this->draw(); for( ; i < arrayData->num; i++ ) { pNode = (CCNode*) arrayData->arr[i]; if (pNode) { pNode->visit(); } } } else { this->draw(); } // reset for next frame m_uOrderOfArrival = 0; if (m_pGrid && m_pGrid->isActive()) { m_pGrid->afterDraw(this); } kmGLPopMatrix(); }

Written with StackEdit.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM