cocos2dx v3.x ui::ScrollView how to setContentOffset?

Is there any way to make ui::ScrollView in cocos2dx v3.x to scroll to a position like setContentOffset in v2.x?

This is damn annoying!

And so nobody knows how it works, so I try to use CCScrollView.h in extensions.
Then another problem arises,

ScrollView *sv = ScrollView::create();

Error: “ScrollView” is ambiguous

@#$!@#$@#$@#$…

The compiler doesn’t know which ScrollView to use???

if i declare it with cocos2d::extension::ScrollView

namespace “cocos2d::extension” has no member “ScrollView”

oh my, please help!

try specify ui namespace explicitly:
ui::ScrollView *sv = ui::ScrollView::create();

Then check out the various jumpToXXXXX() or if you want to animate the change of position scrollToXXXXX(time, attenuation) methods on ScrollView.

If you need to set it yourself you can get the inner container and position that directly. scrollView->getInnerContainer()->setPosition(Vec2(0,-100)); you’ll have to play around with whether you need to add the scrollView’s Size.

Thanks for your reply!!! ( T.T )

i use ui::ScrollView and the only problem left is the equivalent function for setContentOffset in CCScrollView

I tried

scrollView->getInnerContainer()->setPosition(Vec2(0,-100));
it doesn’t work

The closest one that I can find is

scrollToPercentVertical( float percent, float time, bool attenuated)

But the scrolling position is hard to manage. I am still working on the calculation for the % to scroll to

Appreciate if there’re someone who come across this and give me a hand

Well you know the scroll view size, and you should know the inner content size. You can find percentage based on where in the inner container you want it to be at with position/size.

Here’s an example of using the inner container and setting it’s position when the mouse wheel is scrolled.

void CreditsMenu::onMouseScroll(Event *event)
{
    auto mouseEvent = static_cast<EventMouse*>(event);
    float nMoveY = mouseEvent->getScrollY() * 3;

    // get inner container (listView is a subclass of scrollview)
    auto inner = listView->getInnerContainer();
    auto curPos  = inner->getPosition();
    auto nextPos = Vec2(curPos.x, curPos.y + nMoveY);

    // prevent scrolling past beginning
    if (nextPos.y > 0.f)
    {
        inner->setPosition(Vec2(nextPos.x, 0.f));
        return;
    }

    //auto ws = Director::getInstance()->getWinSize();
    auto size = listView->getContentSize();
    auto innerSize = inner->getContentSize();
    auto topMostY = size.height - innerSize.height;

    // prevent scroll past end
    if (nextPos.y < topMostY)
    {
        inner->setPosition(Vec2(nextPos.x, topMostY));
        return;
    }

    inner->setPosition(nextPos);
}

Edit: add couple comments and show scroll view size can be used instead of WinSize (in this case they’re the same size since our scroll view is sized to the screen)

Edit2: changed prevent scroll past beginning to keep x same since should move only vertically

1 Like

Thanks for the sample codes!!
I will work based on this!
Appreciate this very much master Steve!!

In case someone need the solution:

While I was migrating a cocos2dx source code from V2 to V3, (VS2010 to VS2013)

I realised CCSCrollView is obsolete and I couldn’t include cocos2d::extension::ScrollView at all with VS2013

I was forced to use ui::ScrollView instead but one crucial method was missing, i…e setContentOffset ,

which allowd me to scroll to an exact position after ScrollView loaded with data.

Solution Description
The closest equivalent method to setContentOffset i could find is scrollToPercentVertical / scrollToPercentHorizontal.

However the method is quite low level that it only provides the scrolling feature leaving all the necessary calculations to us.

To calculate the right scrolling position this will be the formulae to do so, for vertical position (found out after spending the whole damn day)

    float ContentHeight = 6520; // the long content list height
    float WinHeight     = 960;  //Screen Resolution Height

    float posYAdjusted = posY + WinHeight/2;
    if (posYAdjusted < WinHeight) posYAdjust = WinHeight; 
    if (posYAdjusted > ContentHeight-WinHeight/2) posYAdjusted = ContentHeight-WinHeight/2;
    float pyPercent = 100.0f - ((py-WinHeight) / (ContentHeight-WinHeight)*100.0f);
    layer->scrollToPercentVertical(pyPercent, 0.0, false);

posY = position to scroll to, posYAdjusted = adjust the position to mid of the screen

WinHeight = The window height of the screen resolution e.g. 960, 1024, etc.

ContentHeight = The height of long content list to be loaded, e.g. 7000, 9000, etc.

pyPercent = position in % to scroll to

For horizontal scroll you will have to improvise it by yourself :slight_smile:

Hope this will help!

3 Likes

As shangmike99 showed, scrollToPercentVertical can be used for this. Let me try to put it in a simple way:

The supposed usage would be:

float posY = my_desired_offset_to_scroll_to;
float containerHeight = myScrollView->getInnerContainerSize().height;
float percent = posY / containerHeight * 100.0f;
myScrollView->scrollToPercentVertical( 100.0f - percent, 0.5f, false );

But due to the way scrollToPercentVertical is implemented the above code will scroll to some unintended position. To fix this we need to do some adjustments.

float posY = my_desired_offset_to_scroll_to;
float containerHeight = myScrollView->getInnerContainerSize().height;
float scrollViewHeight = myScrollView->getContentSize().height;
float posYAdjusted = posY - scrollViewHeight;
float containerHeightAdjusted = containerHeight - scrollViewHeight;
float percentAdjusted = posYAdjusted / containerHeightAdjusted * 100.0f;
percentAdjusted = MAX( 0.0f, MIN( 100.0f, percentAdjusted ) ); // To prevent scrolling beyond the ends
myScrollView->scrollToPercentVertical( 100.0f - percentAdjusted, 0.5f, false );
2 Likes

Great I shall try your method
:smile: :smile: :smile:

I don’t know how relevant this post still is, but I came here looking for this answer, so here’s my code. Basically take the current number of items in the scrollview, divide it by the total height and use that as a percentage to scroll. I use a cocos2d::Menu, but since its not a Widget I need to set some sizes myself.

This lets you scroll the scrollview using a mouse on desktop

        cocos2d::ui::ScrollView* menu_scrollview = cocos2d::ui::ScrollView::create();
        menu_scrollview->setContentSize({300, 150});
        cocos2d::Menu* menu = cocos2d::Menu::create();
        menu_scrollview->addChild(menu);

        //get list of resolutions to list
        cocos2d::GLView* glview = cocos2d::Director::getInstance()->getOpenGLView();
        auto desktop_glview = dynamic_cast<cocos2d::GLViewImpl*>(glview);
        std::vector<std::string> resolutions;
        int vidmode_count;
        const GLFWvidmode* raw_vidmodes = desktop_glview->get_possible_video_modes(vidmode_count);
        std::stringstream ss;
        for (int i = 0; i < vidmode_count; i++) {
            const GLFWvidmode* vidmode = &raw_vidmodes[i];
            ss.str("");
            ss << vidmode->width << "x" << vidmode->height << "@" << vidmode->refreshRate << "hz";
            resolutions.push_back(ss.str());
        }

        for (const auto& res : resolutions) {
            cocos2d::MenuItemFont* menu_label = cocos2d::MenuItemFont::create(res);
            menu_label->setCallback([menu_label](cocos2d::Ref* ref) {
                CCLOG("pressed %s", menu_label->getString().c_str());
            });
            menu_label->setAnchorPoint(cocos2d::Vec2::ANCHOR_MIDDLE_LEFT);
            menu->addChild(menu_label);

        }
        menu->alignItemsVertically();

        auto menu_items = menu->getChildren();
        auto menu_item_count = menu->getChildren().size();
        auto padding = 5.0f; //cocos2d menu default padding
        float menu_height = std::accumulate(BEND(menu_items), 0.0f, [menu, padding](float total, cocos2d::Node* menu_item) {
            return total + menu_item->getContentSize().height + padding;
        });

        //because Menu by default puts things in the center of it, we offset by the height
        menu->setPosition(0, menu_height/2.0f);
        menu_scrollview->setInnerContainerSize({menu->getContentSize().width, menu_height});

        auto mouse_listener = cocos2d::EventListenerMouse::create();
        mouse_listener->onMouseScroll = [menu_scrollview, menu_height, menu_item_count](cocos2d::EventMouse* event) {
            float scroll_y = event->getScrollY();
            auto current_percent = menu_scrollview->getScrolledPercentVertical();
            float new_percent = current_percent + (scroll_y * 3 * (100.0f / (float)menu_item_count));
            new_percent = std::min(std::max(new_percent, 0.0f), 100.0f);
            menu_scrollview->jumpToPercentVertical(new_percent);
        };
        mouse_listener->onMouseMove = [menu_scrollview](cocos2d::EventMouse* event) {
            menu_scrollview->stopAutoScroll();
        };
        menu_scrollview->getEventDispatcher()->addEventListenerWithSceneGraphPriority(mouse_listener, menu_scrollview);
1 Like