ccui.ScrollView 扩展

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

道具列表通常用 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;







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值