Design of ScrollBar, a custom control of cocos2dx
**************************************** ************************************
Time:
Author: Sharing_Li
Reprinted Source: http://blog.csdn.net/sharing_li/article/details/42685321
**************************************** ************************************
When we use TableView and ScrollView of cocos2dx, if we want to display a lot of content, it is not convenient for us to determine the location of the content currently being browsed or to quickly browse. At this time, we need a scroll bar to help, But cocos2dx does not have this control, so here I have designed a scroll bar control ScrollBar for you to use very conveniently. Before explaining this, let's take a look:
After reading this, Let's confirm the functional requirements:
1. By sliding TableView or ScrollView, the slider on the right also slides;
2. When TableView or ScrollView slides to the bottom, the slider on the right also slides to the bottom;
3. When you click the slider on the right, the TableView or ScrollView on the left also slides;
4. When the slider slides to the end, TableView or ScrollView slides to the end;
5. When you click the slider background on the right, that is, the yellow part of the example. The TableView, ScrollView, and slider slide with each other;
6. When the content of TableView or ScrollView increases dynamically, the size of the slider also changes dynamically;
7. the horizontal and vertical controls can be used. The example only shows the vertical effect, which is the same as the horizontal effect;
There are so many functions. Let's take a look at how to write the code. We define a class ScrollBar:
ScrollBar. h header file
# Ifndef _ scroll1_bar1_h _ # define _ scroll1_bar1_h _ # include "cocos2d. h "# include" cocos-ext.h "USING_NS_CC; direction; enum SclBarDirection {DIR_NODIR = 0, DIR_VERTICAL, DIR_HORIZENTAL,}; class ScrollBar: public cocos2d: Layer {public: ScrollBar ();~ ScrollBar ();/*** the actual size of the imported image is smaller than the actual image because it cannot be reduced to a smaller value than the actual image, otherwise, the size of the slider may be incorrect */static ScrollBar * create (Scale9Sprite * bar_bg, Scale9Sprite * bar_slider, TableView * tableView, SclBarDirection dir); static ScrollBar * create (const char * bar_bgFile, const char * bar_sliderFile, TableView * tableView, sclbardiredir); bool myInit (Scale9Sprite * bar_bg, Scale9Sprite * bar_slider, TableView * tableView, sclbardiredir); protected: virtual bool onTouchBegan (Touch * touch, Event * pEvent); virtual void onTouchMoved (Touch * pTouch, Event * pEvent); virtual void onTouchEnded (Touch * pTouch, Event * pEvent ); virtual void update (float dt) override;/*** dynamically change the slider size */void updateSlider (); private: TableView * m_pTarget; Scale9Sprite * m_pBg; Scale9Sprite * m_pSlider; sclBarDirection m_direction; Size m_preContentSize; Size m_viewSize; bool m_sliderTouched; Vec2 m_firstTouch; Vec2 m_sliderCurPos; Vec2 m_targetCurPos;}; # endif
Some comments have been provided in the Code. We use the jiugong figure Scale9Sprite to display the slider and the slider background image. Because Scale9Sprite works well when zooming, the image will not deteriorate due to stretching. It is worth noting that if the actual size of your image is so large, Scale9Sprite cannot be reduced to a smaller size than the size, but vice versa. Therefore, the input image must be small enough. Let's take a look at the specific implementation:
First initialize the data:
/*** Initialize each data */bool ScrollBar: myInit (Scale9Sprite * bar_bg, Scale9Sprite * bar_slider, TableView * tableView, SclBarDirection dir) {if (! Layer: init () {return false;} m_pBg = bar_bg; m_pSlider = bar_slider; m_pTarget = tableView; m_direction = dir; m_preContentSize = m_pTarget-> getContainer () -> getContentSize (); m_viewSize = m_pTarget-> getViewSize (); if (m_direction = DIR_VERTICAL) {m_pBg-> setContentSize (Size (m_pBg-> getContentSize (). width, m_viewSize.height); m_pBg-> setPosition (Vec2 (m_pBg-> getContentSize (). width/2, 0); m_pSlider-> setPositionX (m_pBg-> getContentSize (). width/2);} else if (m_direction = DIR_HORIZENTAL) {m_pBg-> setContentSize (Size (m_viewSize.width, m_pBg-> getContentSize (). height); m_pBg-> setPosition (Vec2 (0,-m_pBg-> getContentSize (). height/2); m_pSlider-> setPositionY (-m_pBg-> getContentSize (). height/2);} this-> addChild (m_pBg, 0); this-> updateSlider (); this-> addChild (m_pSlider, 1); this-> scheduleUpdate (); auto listenerT = begin: create (); listenerT-> onTouchBegan = CC_CALLBACK_2 (ScrollBar: onTouchBegan, this); listenerT-> onTouchMoved = CC_CALLBACK_2 (ScrollBar: success, this ); listenerT-> onTouchEnded = CC_CALLBACK_2 (ScrollBar: onTouchEnded, this); listenerT-> setSwallowTouches (false); Director: getInstance ()-> getEventDispatcher ()-> listenerT, this); return true ;}
Let's take a look at how the updateSlider function changes the size of the slider:
Void ScrollBar: updateSlider () {float ratio = 0.0; if (m_direction = DIR_VERTICAL) {ratio = m_viewSize.height/m_preContentSize.height; m_pSlider-> setContentSize (Size (m_pSlider-> getContentSize (). width, m_viewSize.height * ratio);} else if (m_direction = DIR_HORIZENTAL) {ratio = partial/partial; m_pSlider-> setContentSize (Size (Limit * ratio, m_pSlider-> getContentSiz E (). height);} // hide the sliderthis-> setVisible (! (Ratio> = 1 ));}
I got a timer to listen to the sliding of TableView or ScrollView, that is, the offset:
Void ScrollBar: update (float dt) {// determines whether the current content is increased or decreased, because the increase or decrease of the content affects ContenSize, to modify the size of the slider, auto curContentSize = m_pTarget-> getContainer ()-> getContentSize (); if (! (Fabsf (curContentSize. height-m_preContentSize.height) <= 0.00001) |! (Fabsf (curContentSize. width-m_preContentSize.width) <= 0.00001) {m_preContentSize = curContentSize; this-> updateSlider () ;}// set the position of the slider if (m_direction = DIR_VERTICAL) {// adjust the slider position auto curOffset = m_pTarget-> getContentOffset () + (m_preContentSize-m_viewSize)/2; auto sliderOffset = curOffset. y/(m_viewSize.height-curContentSize. height) * (m_viewSize.height-m_pSlider-> getContentSize (). height); // determine whether the slider slides out of the boundary if (fabsf (sliderOffset)> (m_viewSize.height-m_pSlider-> getContentSize (). height)/2) {return;} m_pSlider-> setPositionY (sliderOffset);} else if (m_direction = DIR_HORIZENTAL) {auto curOffset = m_pTarget-> getContentOffset () -(m_preContentSize-m_viewSize)/2; auto sliderOffset =-curOffset. x/(m_viewSize.width-curContentSize. width) * (m_viewSize.width-m_pSlider-> getContentSize (). width); if (fabsf (sliderOffset)> (m_viewSize.width-m_pSlider-> getContentSize (). width)/2) {return;} m_pSlider-> setPositionX (sliderOffset );}}
Note: The slide size of TableView or ScrollView is different from that of the slider. Therefore, to synchronize the two, slide proportionally.
Let's take a look at the slide of the slider and the implementation of clicking the slider Background:
First look at onTouchBegan:
Bool ScrollBar: onTouchBegan (Touch * touch, Event * pEvent) {m_sliderCurPos = m_pSlider-> getPosition (); m_targetCurPos = m_pTarget-> getContentOffset (); auto touchPoint = touch-> getLocation (); m_firstTouch = touchPoint; // convert the touch point to the coordinate touchPoint = this-> convertToNodeSpace (touchPoint) under the current Child layer ); // only responds to the touch if (! M_pBg-> getBoundingBox (). containsPoint (touchPoint) {return false;} // if the slider is clicked first, set the flag if (m_pSlider-> getBoundingBox (). containsPoint (touchPoint) {m_sliderTouched = true;} else // if the slider is not clicked, the slider background image {if (m_direction = DIR_VERTICAL) is clicked) {// adjust the m_pTarget offset to adjust the slider position, because the update function will always listen to the m_pTarget offset auto offset = touchPoint. y-m_sliderCurPos.y; if (touchPoint. y <= 0) {offset + = m_pSlider-> getContentSize (). height/2;} else {offset-= m_pSlider-> getContentSize (). height/2;} auto newOff = m_targetCurPos.y + offset/(m_pSlider-> getContentSize (). height-m_viewSize.height) * (gradient-m_viewSize.height); m_pTarget-> setContentOffset (Vec2 (0, newOff);} else if (m_direction = DIR_HORIZENTAL) {auto offset = touchPoint. x-m_sliderCurPos.x; if (touchPoint. x <= 0) {offset + = m_pSlider-> getContentSize (). width/2;} else {offset-= m_pSlider-> getContentSize (). width/2;} auto newOff = m_targetCurPos.x + offset/(m_viewSize.width-m_pSlider-> getContentSize (). width) * (m_preContentSize.width-m_viewSize.width); m_pTarget-> setContentOffset (Vec2 (newOff, 0);} return true ;}
Note that I do not need to modify the slider position in the touch function, because we changed the slider position indirectly by modifying the offset of ScrollView or TableView, therefore, we only need to correctly set the position of the ScrollView or TableView. The update function will help us solve the slider position.
Let's take a look at onTouchMoved:
Void ScrollBar: onTouchMoved (Touch * pTouch, Event * pEvent) {// only responds to the move if (m_sliderTouched) {auto offPos = pTouch-> getLocation () -m_firstTouch; if (m_direction = DIR_VERTICAL) {// adjust the position of the slider by adjusting the m_pTarget offset, because the update function will always listen to the m_pTarget offset auto newOff = m_sliderCurPos.y + offPos. y; // determine whether the slider slides out of the boundary if (fabsf (newOff)> (m_viewSize.height-m_pSlider-> getContentSize (). height)/2) {(newOff <0? (NewOff = (m_pSlider-> getContentSize (). height-m_viewSize.height)/2): (newOff = (m_viewSize.height-m_pSlider-> getContentSize (). height)/2);} newOff-= m_sliderCurPos.y; m_pTarget-> setContentOffset (Vec2 (0, m_targetCurPos.y + newOff/(m_pSlider-> getContentSize (). height-m_viewSize.height) * (m_preContentSize.height-m_viewSize.height);} else if (m_direction = DIR_HORIZENTAL) {auto newOff = M_sliderCurPos.x + offPos. x; if (fabsf (newOff)> (m_viewSize.width-m_pSlider-> getContentSize (). width)/2) {(newOff <0? (NewOff = (m_pSlider-> getContentSize (). width-m_viewSize.width)/2): (newOff = (m_viewSize.width-m_pSlider-> getContentSize (). width)/2);} newOff-= m_sliderCurPos.x; m_pTarget-> setContentOffset (Vec2 (m_targetCurPos.x + newOff/(m_viewSize.width-m_pSlider-> getContentSize (). width) * (m_preContentSize.width-m_viewSize.width), 0 ));}}}
Finally, let's take a look at onTouchEnded:
void ScrollBar::onTouchEnded(Touch *pTouch, Event *pEvent){m_sliderTouched = false;}
It's easy. Just restore the touch status of the slider. By now, the custom control ScrollBar has been implemented. Let's take a look at how to use ScrollBar in the code. It is also very simple. Let's look at the following example:
m_tableView = TableView::create(this,viewSize);m_tableView->ignoreAnchorPointForPosition(false);m_tableView->setAnchorPoint(Vec2(0.5,0.5));m_tableView->setPosition(Vec2(viewSize.width / 2,viewSize.height / 2));m_tableView->setDirection(ScrollView::Direction::VERTICAL);m_tableView->setDelegate(this);m_tableView->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);m_tableView->reloadData();pView->addChild(m_tableView);auto scrollBar_vr = ScrollBar::create("scrollbar/vr_slider_bg.png","scrollbar/vr_slider.png",m_tableView,DIR_VERTICAL);scrollBar_vr->setPosition(Vec2(viewSize.width,viewSize.height / 2));pView->addChild(scrollBar_vr,2);
After creating your TablewView or ScrollView, you only need to create a ScrollBar, set the location, and add it to the parent node in three steps.
The content is finished this time. If you have any questions, you can leave a message.
Demo resources download: http://download.csdn.net/detail/sharing_li/8359125