您的位置:首页 > 产品设计 > UI/UE

ccui.ScrollView 扩展

2015-08-05 16:37 435 查看

大多数游戏都有背包这个东西.

道具列表通常用 ScrollView 来实现.

这个ScrollView内部有一个Layout, 滑动都是由移动这个Layout来实现.

道具摆放通常从上往下, 从左到右.

假设你有一个道具数组, 你遍历这个数组来摆放道具.

因为数组长度是已知的, 你可以计算出Layout需要的尺寸, 再把道具摆上去.

这个实现是很容易的. 但是, 如果你提前不知道数组长度, 就是不知道道具数量,

可能随时会添加道具或者删除道具.

因为cocos2dx的坐标系是左下角为原点, 因此动态增加或删除都需要把所有的道具都移动位置,

光移动Layout是不行的.

%26nbsp;

说了一堆的废话.

1 --    增加, 删除.
2 function scrollView:beginEditChilds(childWidth, childHeight)
3     self.contentSize = self:getContentSize();
4     self.childCount = #(self:getChildren());
5     self.colCount = math.floor(self.contentSize.width / childWidth);
6     self.childWidth = (self.contentSize.width - childWidth * self.colCount) / (self.colCount + 1) + childWidth;
7     self.childHeight = childHeight;
8     self.innerSize = self:getInnerContainerSize();
9     self.innerOffsetY = self:getInnerContainerPosition().y + self.innerSize.height - self.contentSize.height;
10 end
11
12 function scrollView:endEditChilds()
13     local rowCount = math.ceil(self.childCount / self.colCount);
14     self.innerSize.height = math.max(self.contentSize.height, rowCount * self.childHeight);
15     self:setInnerContainerSize(self.innerSize);
16     self:setInnerContainerPosition(
17         cc.p(0, math.min(0, self.contentSize.height + self.innerOffsetY - self.innerSize.height)));
18
19     local offsetY = self.innerSize.height - self.childHeight;
20     self.childs = self:getChildren();
21     for i = self.childCount, 1, -1 do
22         local row = math.floor((i - 1) / self.colCount);
23         local col = math.floor((i - 1) % self.colCount);
24         local x = col * self.childWidth + self.childWidth * 0.5;
25         local y = row * self.childHeight + self.childHeight * 0.5;
26         self.childs[i]:setPosition(x, self.innerSize.height - y);
27         self.childs[i].__pos = i - 1;
28     end
29 end
30
31 function scrollView:appendChild(child)
32     child:setAnchorPoint(cc.p(0.5, 0.5));
33     self.childCount = self.childCount + 1;
34     self:addChild(child);
35 end
36
37 function scrollView:deleteChild(child)
38     self.childCount = self.childCount - 1;
39     self:removeChild(child);
40 end

scrollView 是指 ccui.ScrollView:create() 返回的对象.

可以通过一个工厂函数给对象扩展成员函数. 这个下面在贴代码.

使用方法就是, 在add, del之前调用 begin, 之后调用end.

begin和end的目的是, 避免每一次 add, del 都要全部排列节点, 并且省去了每次数值计算.

这段代码实现了ccui.ScrollView动态增加|删除子节点.

在end函数里面, 还调整了Layout的坐标, 每次修改不会察觉到Layout的坐标变化.

%26nbsp;

%26nbsp;

有些用ccui.ScrollView做城镇地图, 可以缩放, 顶点缩放.

直接缩放Layout会影响拖动效果, 这个问题直接修改引擎或者继承这个对象.

1 float Widget::getLeftBoundary() const
2 {
3     return getPosition().x - getAnchorPoint().x * _contentSize.width * _scaleX;
4 }
5
6 float Widget::getBottomBoundary() const
7 {
8     return getPosition().y - getAnchorPoint().y * _contentSize.height * _scaleY;
9 }
10
11 float Widget::getRightBoundary() const
12 {
13     return getLeftBoundary() + _contentSize.width * _scaleX;
14 }
15
16 float Widget::getTopBoundary() const
17 {
18     return getBottomBoundary() + _contentSize.height * _scaleY;
19 }

%26nbsp;

下面是顶点缩放, 直接缩放Layout会把锚点作为中心,

我们这个缩放也是以锚点作为中心, 但是会缩放的同时移动坐标, 效果就达到了.

1 --    获取内容缩放值.
2 function scrollView:getInnerContainerScale()
3     return self:getInnerContainer():getScale();
4 end
5
6 --    获取内容高宽.
7 function scrollView:getInnerContainerSize()
8     return self:getInnerContainer():getContentSize();
9 end
10
11 --    获取内容位置.
12 function scrollView:getInnerContainerPosition()
13     return cc.p(self:getInnerContainer():getPosition());
14 end
15
16 --    坐标转换为内容内部坐标.
17 function scrollView:convertToInnerContainer(point)
18     return self:getInnerContainer():convertToNodeSpace(point);
19 end
20
21 function scrollView:setInnerContainerScale(scale)
22     self:getInnerContainer():setScale(scale);
23 end
24
25 function scrollView:setInnerContainerSize(size)
26     self:getInnerContainer():setContentSize(size);
27 end
28
29 --    设置内容位置.
30 function scrollView:setInnerContainerPosition(point)
31     self:getInnerContainer():setPosition(point);
32 end
33
34 --    矫正内容位置.
35 function scrollView:adjustmentInnerContainerPosition()
36     local curPoint = self:getInnerContainerPosition();
37     local viewSize = self:getContentSize();
38     local innerSize = self:getInnerContainerSize();
39     local curScale = self:getInnerContainerScale();
40
41     if curPoint.x %26gt; 0 then curPoint.x = 0 end;
42     if curPoint.y %26gt; 0 then curPoint.y = 0 end;
43     if curPoint.x %26lt; viewSize.width - innerSize.width * curScale then
44         curPoint.x = viewSize.width - innerSize.width * curScale;
45     end
46     if curPoint.y %26lt; viewSize.height - innerSize.height * curScale then
47         curPoint.y = viewSize.height - innerSize.height * curScale;
48     end
49
50     self:setInnerContainerPosition(curPoint);
51 end
52
53 --    定点缩放.
54 function scrollView:scaleByPoint(worldPoint, scale)
55     local function callChildScaleHandler(node, scale)
56         for k, child in pairs(node:getChildren()) do
57             if child.onScaleHandler then
58                 child:onScaleHandler(scale);
59             end
60             callChildScaleHandler(child, scale);
61         end
62     end
63
64     local viewSize = self:getContentSize();
65     local curScale = self:getInnerContainerScale();
66     local localPoint = self:convertToInnerContainer(worldPoint);
67
68     --    计算缩放.
69     local newScale = curScale + scale;
70     if newScale %26lt; 0.5 then newScale = 0.5 end;
71     if newScale %26gt; 2 then newScale = 2 end;
72
73     --    实际增加的缩放值.
74     scale = scale - ((curScale + scale) - newScale);
75     if scale ~= 0 then
76         local diffWidth = localPoint.x * scale;
77         local diffHeight = localPoint.y * scale;
78         local curPoint = self:getInnerContainerPosition();
79         curPoint.x = curPoint.x - diffWidth;
80         curPoint.y = curPoint.y - diffHeight;
81
82         self:setInnerContainerScale(newScale);
83         self:setInnerContainerPosition(curPoint);
84         self:adjustmentInnerContainerPosition();
85
86         callChildScaleHandler(self, newScale);
87     end
88 end
callChildScaleHandler 这个函数是后来补充上的,
因为父节点缩放会导致子节点缩放,
地图上摆放房子, 房子上会有名字. scrollView是父节点, 房子摆在上面就是子节点, 房子上的名字也是子节点.
如果不做控制, 名字也会跟着缩放, 于是就看不见了, 或者模糊了.
然后通过callChildScaleHandler递归调用子节点onScaleHandler函数.
子节点在父节点每次缩放时处理这个缩放值.

1 function utils.transformScrollView(scrollView)
2
3     --    获取内容缩放值.
4     function scrollView:getInnerContainerScale()
5         return self:getInnerContainer():getScale();
6     end
7
8     --    获取内容高宽.
9     function scrollView:getInnerContainerSize()
10         return self:getInnerContainer():getContentSize();
11     end
12
13     --    获取内容位置.
14     function scrollView:getInnerContainerPosition()
15         return cc.p(self:getInnerContainer():getPosition());
16     end
17
18     --    坐标转换为内容内部坐标.
19     function scrollView:convertToInnerContainer(point)
20         return self:getInnerContainer():convertToNodeSpace(point);
21     end
22
23     function scrollView:setInnerContainerScale(scale)
24         self:getInnerContainer():setScale(scale);
25     end
26
27     function scrollView:setInnerContainerSize(size)
28         self:getInnerContainer():setContentSize(size);
29     end
30
31     --    设置内容位置.
32     function scrollView:setInnerContainerPosition(point)
33         self:getInnerContainer():setPosition(point);
34     end
35
36     --    矫正内容位置.
37     function scrollView:adjustmentInnerContainerPosition()
38         local curPoint = self:getInnerContainerPosition();
39         local viewSize = self:getContentSize();
40         local innerSize = self:getInnerContainerSize();
41         local curScale = self:getInnerContainerScale();
42
43         if curPoint.x %26gt; 0 then curPoint.x = 0 end;
44         if curPoint.y %26gt; 0 then curPoint.y = 0 end;
45         if curPoint.x %26lt; viewSize.width - innerSize.width * curScale then
46             curPoint.x = viewSize.width - innerSize.width * curScale;
47         end
48         if curPoint.y %26lt; viewSize.height - innerSize.height * curScale then
49             curPoint.y = viewSize.height - innerSize.height * curScale;
50         end
51
52         self:setInnerContainerPosition(curPoint);
53     end
54
55     --    定点缩放.
56     function scrollView:scaleByPoint(worldPoint, scale)
57         local function callChildScaleHandler(node, scale)
58             for k, child in pairs(node:getChildren()) do
59                 if child.onScaleHandler then
60                     child:onScaleHandler(scale);
61                 end
62                 callChildScaleHandler(child, scale);
63             end
64         end
65
66         local viewSize = self:getContentSize();
67         local curScale = self:getInnerContainerScale();
68         local localPoint = self:convertToInnerContainer(worldPoint);
69
70         --    计算缩放.
71         local newScale = curScale + scale;
72         if newScale %26lt; 0.5 then newScale = 0.5 end;
73         if newScale %26gt; 2 then newScale = 2 end;
74
75         --    实际增加的缩放值.
76         scale = scale - ((curScale + scale) - newScale);
77         if scale ~= 0 then
78             local diffWidth = localPoint.x * scale;
79             local diffHeight = localPoint.y * scale;
80             local curPoint = self:getInnerContainerPosition();
81             curPoint.x = curPoint.x - diffWidth;
82             curPoint.y = curPoint.y - diffHeight;
83
84             self:setInnerContainerScale(newScale);
85             self:setInnerContainerPosition(curPoint);
86             self:adjustmentInnerContainerPosition();
87
88             callChildScaleHandler(self, newScale);
89         end
90     end
91
92     --    将内容位置移至中心.
93     function scrollView:lockByPoint(point, isFade)
94         local viewSize = self:getContentSize();
95         local viewCenter = cc.size(viewSize.width / 2, viewSize.height / 2);
96         local curPoint = self:getInnerContainerPosition();
97         curPoint.x = curPoint.x - (curPoint.x + point.x - viewCenter.width);
98         curPoint.y = curPoint.y - (curPoint.y + point.y - viewCenter.height);
99
100         if isFade then
101             --    这里可以实现一个移动动画.
102         else
103             self:setInnerContainerPosition(curPoint);
104             self:adjustmentInnerContainerPosition();
105         end
106     end
107
108     --    增加, 删除.
109     function scrollView:beginEditChilds(childWidth, childHeight)
110         self.contentSize = self:getContentSize();
111         self.childCount = #(self:getChildren());
112         self.colCount = math.floor(self.contentSize.width / childWidth);
113         self.childWidth = (self.contentSize.width - childWidth * self.colCount) / (self.colCount + 1) + childWidth;
114         self.childHeight = childHeight;
115         self.innerSize = self:getInnerContainerSize();
116         self.innerOffsetY = self:getInnerContainerPosition().y + self.innerSize.height - self.contentSize.height;
117     end
118
119     function scrollView:endEditChilds()
120         local rowCount = math.ceil(self.childCount / self.colCount);
121         self.innerSize.height = math.max(self.contentSize.height, rowCount * self.childHeight);
122         self:setInnerContainerSize(self.innerSize);
123         self:setInnerContainerPosition(
124             cc.p(0, math.min(0, self.contentSize.height + self.innerOffsetY - self.innerSize.height)));
125
126         local offsetY = self.innerSize.height - self.childHeight;
127         self.childs = self:getChildren();
128         for i = self.childCount, 1, -1 do
129             local row = math.floor((i - 1) / self.colCount);
130             local col = math.floor((i - 1) % self.colCount);
131             local x = col * self.childWidth + self.childWidth * 0.5;
132             local y = row * self.childHeight + self.childHeight * 0.5;
133             self.childs[i]:setPosition(x, self.innerSize.height - y);
134             self.childs[i].__pos = i - 1;
135         end
136     end
137
138     function scrollView:appendChild(child)
139         child:setAnchorPoint(cc.p(0.5, 0.5));
140         self.childCount = self.childCount + 1;
141         self:addChild(child);
142     end
143
144     function scrollView:deleteChild(child)
145         self.childCount = self.childCount - 1;
146         self:removeChild(child);
147     end
148
149 end

这是完整的代码.%26nbsp;全部函数封装在utils.transformScrollView工厂函数中.

%26nbsp;

这里面还少了一个手势缩放的功能.

这个功能只有城镇会用到, 所以我把它单独移出来.

同样的, 写一个独立的工厂函数, 为scrollView扩展.

1 function utils.transformScaleScrollView(parent, scrollView)
2     local touchs = {};
3     local preDistance = 0;
4     local function onTouchBegan(touch, event)
5         local touchId = touch:getId();
6         local point = touch:getLocation();
7         if touchs[1] == nil then
8             touchs[1] = {id = touchId, point = point};
9         elseif touchs[2] == nil then
10             touchs[2] = {id = touchId, point = point};
11             preDistance = cc.pGetDistance(touchs[1].point, touchs[2].point);
12         end
13         return touchs[1] and touchId == touchs[1].id or touchs[2] and touchId == touchs[2].id;
14     end
15
16     local function onTouchMoved(touch, event)
17         local touchId = touch:getId();
18         if touchId == touchs[1].id then
19             touchs[1].point = touch:getLocation();
20         else
21             touchs[2].point = touch:getLocation();
22         end
23
24         if touchs[1] and touchs[2] then
25             local distance = cc.pGetDistance(touchs[1].point, touchs[2].point);
26             local diff = distance - preDistance;
27             local curScale = scrollView:getInnerContainerScale();
28             local lockPos = cc.pMidpoint(touchs[1].point, touchs[2].point);
29             scrollView:scaleByPoint(lockPos, diff * 0.0025);
30             preDistance = distance;
31         end
32     end
33
34     local function onTouchEnded(touch, event)
35         local touchId = touch:getId();
36         if touchs[1].id == touchId then
37             touchs[1] = touchs[2];
38         end
39         touchs[2] = nil;
40     end
41
42     --    手指点击缩放.
43     local listener = cc.EventListenerTouchOneByOne:create();
44     listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN);
45     listener:registerScriptHandler(onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED);
46     listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED);
47     listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_CANCELLED);
48     cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, parent);
49 end

原理很简单, 确定两点,

已两点的中心为缩放点,

已当前移动的距离和上次移动的距离只差为缩放值, 这个缩放值可能有点大, 我把它乘以 0.0025, 效果刚刚好.

然后就可以实现海盗旗兵那种手势缩放效果了...

%26nbsp;


%26nbsp;







内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: