How to add buttons to a scrollview without interfering with scrolling?

How does one create a scrollview with buttons, without interfering with scrolling ?

I am trying to create a “toolbar”, by creating a scrollview(that scrolls only horizontally) and adding various “buttons” to it.

Things are fine when the scrollview does not contain any buttons - I can scroll easily to my hearts content! The problem is when I start adding buttons!

If my finger makes contact with an area where there is no button - then I can scroll without problem.

But if my finger makes contact with an area where there is a button, then dragging does not make the scrollview scroll! Instead the button seems to “absorb” the touch and registers it as a “cancelled” button press!

How do I modify my code, so I can scroll without the buttons interfering?

Here is my code:

        _mainToolbar = ui::ScrollView::create();
        _mainToolbar->setBounceEnabled(false);
        _mainToolbar->setDirection(ui::ScrollView::Direction::HORIZONTAL);
        _mainToolbar->setScrollBarPositionFromCorner(Vec2(4, 4));

	//…..

            ui::Button* button = ui::Button::create(imageName,"","",ui::Widget::TextureResType::PLIST);
            
            buttonSize = button->getContentSize();
            button->setAnchorPoint(Vec2(0.5, 0.5));
            button->setTouchEnabled(true);
            button->addTouchEventListener(CC_CALLBACK_2(Main::receiveButtonPressEvent, this));
            button->setSwallowTouches(true);

   // …

		 _mainToolbar->addChild(button);


//…..


        _mainToolbar->setContentSize(mainToolContentSize);
        _mainToolbar->setInnerContainerSize(mainToolbarInnerContainerSize);

//….

void receiveButtonPressEvent(Ref *pSender,ui::Widget::TouchEventType type){          
    switch (type) {
        case ui::Widget::TouchEventType::BEGAN:{
            break;
        }
            
        case ui::Widget::TouchEventType::MOVED:{
            break;
        }
            
        case ui::Widget::TouchEventType::ENDED:{
            //Some code here if the button is pressed…”
            break;
        }
            
        case ui::Widget::TouchEventType::CANCELED:{
            break;
        }
            
        default:{
            break;
        }
    }//End switch(type)
}

EDIT: I have looked at the CPP tests, but in the tests, the buttons don’t do anything - they don’t have any touch event listeners, so they don’t seem to interfere. I think this is where my problem is, but I don’t know how to fix it.

You need to propagate touches back up to the scroll view.

But I’d recommend using a ListView instead as I believe it does that for you :stuck_out_tongue:

1 Like

Can you explain further please ?

I’m looking at the examples in cpp-tests, but it isn’t clear to me how to use it. Again, in the tests, the buttons don’t actually do anything - so I’m not sure how to propagate the touches back up as you say.

Looking at ui::Widget’s implementation, it seems propagation is enabled by default, so I’m not sure why the buttons are consuming the touches.

What version of cocos2d-x are you using?

Also, a few suggestions for your code:

  • use addClickEventListener() instead of addTouchEventListener() unless you intend to make use of begin/move/canceled events
  • button->setTouchEnabled(true) and button->setSwallowTouches(true) are redundant as ui:Button is initialised with touch enabled by default

I’m using 3.10.

I tried using addClickEventListener(), but it seems to have the same problem - I cannot drag the scroll layer…

Have you made a tool bar with buttons in any of your projects, where you can scroll ? My toolbar is horizontal, and is one button height tall (there is just one row), so there isn’t any room to touch the scroll layer and drag it. You have to drag it through the button… There are only a few visible buttons, because the content size is smaller than the container size, so you have to scroll the layer to see the other buttons available in the toolbar…

Hey - would you happen to have a sample project where you have implemented something like this ? I still have this problem, and short of implementing my own scroll view, can’t see how to fix it…

This works for me:

auto scrollView = ui::ScrollView::create();
scrollView->setClippingEnabled(true);
scrollView->setContentSize(Size(100,300));
scrollView->setDirection(ui::ScrollView::Direction::VERTICAL);
scrollView->getInnerContainer()->setLayoutType(ui::Layout::Type::VERTICAL);
scrollView->setInnerContainerSize(Size(100,500));
this->addChild(scrollView);

const Size buttonSize(100,50);

//Widget test
for(int i = 1; i <= 5 ; i++)
{
    const Color4B buttonColor(random(0, 255), random(0, 255), random(0, 255), 255);
    auto button = ui::Widget::create();
    button->setContentSize(buttonSize);
    button->setTouchEnabled(true);
    button->addClickEventListener([=](Ref* _sender)
                                  {
                                      CCLOG("Widget %d pressed", i);
                                  });
    button->addChild(LayerColor::create(buttonColor, buttonSize.width, buttonSize.height));
    scrollView->addChild(button);
}
//Button test
for(int i = 1; i <= 5; i++)
{
    auto button = ui::Button::create("scale9Texture.png", "", "", ui::Widget::TextureResType::PLIST);
    button->setScale9Enabled(true);
    button->setContentSize(buttonSize);
    button->addClickEventListener([=](Ref* _sender)
                                  {
                                      CCLOG("Button %d pressed", i);
                                  });
    scrollView->addChild(button);
}
1 Like

I discovered the problem - there is some kind of bug (in cocos2d-x)
which causes that behaviour when the scrollview is descendant node, several nodes deep. Your code works. Thanks!

Actually, it seems this happens if one of the parent nodes is “rotated”. Rotating the scrollview a number of degrees to “nullify” the effect of the parent nodes rotation, causes the scroll view to work…

Haha that’s interesting, I’ve never tried to rotate a scroll view.

May be worth raising an issue on GitHub and providing sample code to reproduce.