完整cocos2d实现scrollView

完整cocos2d实现scrollView

前面实现了一部分的scrollView,今天将这个类的功能完整实现。
转帖请注明出处:
http://www.cnrui.cn/blog/article.asp?id=267

#ScrollViewUtilty.h
复制内容到剪贴板 程序代码 程序代码

//
//  ScrollViewUtilty.h
//  Shop
//
//  Created by 胡 东平 on 12-1-1.
//  Copyright (c) 2012年 Magnetjoy. All rights reserved.
//

#import "Utilties.h"
/** 溢出隐藏节点
*/
@interface OverflowNode : CCNode
// 绘制前
-(void)beforeDraw;
// 绘制后
-(void)afterDraw;
@end

// 触摸方法
typedef enum{
    // 先选中子项目
    fmScrollBeforeTouch,
    // 后选中子项目
    fmScrollAfterTouch
}fmScrollTouchFunc;

// 滚动方向
typedef enum{
    // 从左到右滚动
    fmScrollOrientationLeftToRight,
    // 从右到左滚动
    fmScrollOrientationRightToLeft,
    // 从上到下滚动
    fmScrollOrientationTopToBottom,
    // 从下到上滚动
    fmScrollOrientationBottomToTop
}fmScrollOrientation;
/**************************************************************/
/** 滚动视图
*/
@interface FMScrollView : OverflowNode<CCTargetedTouchDelegate>{
    // 内部滚动栏
    CCSprite* inner;
    // 包含项目
    CCArray* items;
    // 视图大小
    CGSize size;
    // 间隔
    CGFloat itemSpace;
    // 触摸起始点
    CGPoint startPoint;
    // 摩擦系数
    CGPoint friction;
    // 摩擦阻力 默认0.03
    CGFloat resistance;
    // 惯性 默认20
    CGFloat inertia;
    // 前一次触摸纪录时间
    NSTimeInterval preTimestamp;
    // 前一次触摸点纪录
    CGPoint prePoint;
    // 可视大小
    CGPoint viewPosit;
    // 触摸方法
    fmScrollTouchFunc touchFunc;
    // 滚动方向
    fmScrollOrientation scrollOrientation;
    // 触摸范围
    CGRect touchRect;
    // 开始的触摸点
    UITouch* beginTouch;
    // 开始的事件
    UIEvent* beginEvent;
    // 当前选中的节点
    CCNode<CCTargetedTouchDelegate>* currentItem;
}
// 摩擦阻力 默认0.03
@property(nonatomic)CGFloat resistance;
// 惯性 默认20
@property(nonatomic)CGFloat inertia;

// 使用视图大小初始化 默认先选中子项 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space;
// 使用视图大小初始化 默认 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func;
// 使用视图大小初始化 touchRect触摸范围
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect;
// 使用视图大小初始化 touchRect触摸范围
-(id)initWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect;
// 添加一个项目节点
-(void)addItemNode:(CCNode*)node;
// 保存触摸开始信息
-(void)saveTouchStartInfo:(UITouch *)touch withEvent:(UIEvent *)event;
// 清楚触摸开始信息
-(void)clearTouchStartInfo;
// 等待子项触摸开始
-(void)waitItemTouchBegin;
// 通知触摸包含项目触摸事件开始
-(BOOL)containsItemTouchBegin:(UITouch *)touch withEvent:(UIEvent *)event;
@end


#ScrollViewUtilty.m
复制内容到剪贴板 程序代码 程序代码

//
//  ScrollViewUtilty.m
//  Shop
//
//  Created by 胡 东平 on 12-1-1.
//  Copyright (c) 2012年 Magnetjoy. All rights reserved.
//

#import "ScrollViewUtilty.h"
/** 溢出隐藏节点
*/
@implementation OverflowNode
// 使用视图大小初始化
-(id) init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}
- (void)dealloc 
{
    [super dealloc];
}
// 绘制前
-(void)beforeDraw
{
    GLfloat planeTop[]    = {0.0f, -1.0f, 0.0f, self.boundingBoxInPixels.size.height};
    GLfloat planeBottom[] = {0.0f, 1.0f, 0.0f, 0.0f};
    GLfloat planeLeft[]   = {1.0f, 0.0f, 0.0f, 0.0f};
    GLfloat planeRight[]  = {-1.0f, 0.0f, 0.0f, self.boundingBoxInPixels.size.width};
    
    glClipPlanef(GL_CLIP_PLANE0, planeTop);
    glClipPlanef(GL_CLIP_PLANE1, planeBottom);
    glClipPlanef(GL_CLIP_PLANE2, planeLeft);
    glClipPlanef(GL_CLIP_PLANE3, planeRight);
    glEnable(GL_CLIP_PLANE0);
    glEnable(GL_CLIP_PLANE1);
    glEnable(GL_CLIP_PLANE2);
    glEnable(GL_CLIP_PLANE3);
}

// 绘制后
-(void)afterDraw
{
    glDisable(GL_CLIP_PLANE0);
    glDisable(GL_CLIP_PLANE1);
    glDisable(GL_CLIP_PLANE2);
    glDisable(GL_CLIP_PLANE3);
}

// 绘制
-(void) visit
{
    // quick return if not visible
    if (!visible_)
        return;
    
    glPushMatrix();
    
    if ( grid_ && grid_.active) {
        [grid_ beforeDraw];
        [self transformAncestors];
    }
    
    [self transform];
    [self beforeDraw];
    
    if(children_) {
        ccArray *arrayData = children_->data;
        NSUInteger i = 0;
        
        // draw children zOrder < 0
        for( ; i < arrayData->num; i++ ) {
            CCNode *child = arrayData->arr[i];
            if ( [child zOrder] < 0 )
                [child visit];
            else
                break;
        }
        
        // self draw
        [self draw];
        
        // draw children zOrder >= 0
        for( ; i < arrayData->num; i++ ) {
            CCNode *child =  arrayData->arr[i];
            [child visit];
        }
        
    } else
        [self draw];
    
    [self afterDraw];
    if ( grid_ && grid_.active)
        [grid_ afterDraw:self];
    
    glPopMatrix();
}
@end

/**************************************************************/
/** 滚动视图
*/
@implementation FMScrollView
// 摩擦阻力
@synthesize resistance;
// 惯性
@synthesize inertia;

// 使用视图大小初始化 默认先选中子项 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space
{
    return [self viewWithViewSize:viewSize space:space touchFunc:fmScrollBeforeTouch 
                      orientation:fmScrollOrientationLeftToRight touchRect:CGRectZero];
}

// 使用视图大小初始化 默认 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func
{
    return [[[self alloc]initWithViewSize:viewSize space:space touchFunc:func 
                              orientation:fmScrollOrientationLeftToRight touchRect:CGRectZero] autorelease];
}

// 使用视图大小初始化 touchRect触摸范围
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func 
                     orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect
{
    return [[[self alloc]initWithViewSize:viewSize space:space touchFunc:func 
                              orientation:orientation touchRect:rect] autorelease];
}

// 使用视图大小初始化 touchRect触摸范围
-(id)initWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func 
          orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect
{
    self = [super init];
    if (self) {
        // 摩擦阻力
        resistance = 0.03;
        // 惯性
        inertia = 20;
        size = viewSize;
        itemSpace = space;
        touchFunc = func;
        scrollOrientation = orientation;
        touchRect.origin = rect.origin;
        touchRect.size.width = (rect.size.width == 0) ? size.width : rect.size.width;
        touchRect.size.height = (rect.size.height == 0) ? size.height : rect.size.height;
        
        [self setContentSize:size];
        inner = [[CCNode node]retain];
        inner.position = touchRect.origin;
        // 从右到左
        if (scrollOrientation == fmScrollOrientationRightToLeft) {
            [inner setLeft:size.width];
        }
        // 从上到下
        if(scrollOrientation == fmScrollOrientationTopToBottom){
            [inner setTop:size.height];
        }
        [self addChild:inner];
        items = [[CCArray array]retain];
    }
    return self;
}
// 添加一个项目节点
-(void)addItemNode:(CCNode*)node
{
    if(scrollOrientation == fmScrollOrientationLeftToRight){
        // 从左向右排列
        [node setLeft:inner.contentSize.width + itemSpace];
    }else if(scrollOrientation == fmScrollOrientationRightToLeft){
        // 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
        CGFloat left = node.contentSize.width + itemSpace;
        for (int i = items.count - 1; i > -1; i--) {
            CCNode* item = [items objectAtIndex:i];
            [item setLeft:item.position.x + left];
        }
        [inner setLeft:inner.position.x - left];
    }
    
    if (scrollOrientation == fmScrollOrientationBottomToTop) {
        // 从下到上排列
        [node setTop:inner.contentSize.height + itemSpace];
    }else if(scrollOrientation == fmScrollOrientationTopToBottom){
        // 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
        CGFloat top = node.contentSize.height + itemSpace;
        for (int i = items.count - 1; i > -1; i--) {
            CCNode* item = [items objectAtIndex:i];
            [item setTop:item.position.y + top];
          }
        [inner setTop:inner.position.y - top];
    }

    [items addObject:node];
    [inner addChild:node];
    [inner setContentSizeWithChilds];
    
    // 设置可视区域
    viewPosit.x = inner.contentSize.width - touchRect.size.width + touchRect.origin.x;
    viewPosit.y = inner.contentSize.height - touchRect.size.height + touchRect.origin.y;
}

- (void) onEnter
{  
    [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MAX swallowsTouches:YES];
    [super onEnter];  
}  
- (void) onExit
{  
    [[CCTouchDispatcher sharedDispatcher] removeDelegate: self];  
    [super onExit];  


- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    if(![self containsTouchLocation:touch touchRect:touchRect])return NO;
    [self stopAllActions];
    // 摩擦系数
    friction.x = 1;
    friction.y = 1;
    prePoint = startPoint = [touch locationInView:[touch view]];
    preTimestamp = touch.timestamp;
    // 设置定时器,如果用户在包含的某一项停留超过1秒,将触发该项的触摸行为,进而阻止滚动栏移动
    [self saveTouchStartInfo:touch withEvent:event];
    return YES;
}

- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
    if (currentItem != nil) {
        // 先选中子项
        if (touchFunc == fmScrollBeforeTouch) {
            // 触摸发生移动行为,取消子项的触摸
            if([currentItem respondsToSelector:@selector(ccTouchCancelled:withEvent:)])
                [currentItem ccTouchCancelled:touch withEvent:event];
            
            [self clearTouchStartInfo];
        }else if(touchFunc == fmScrollAfterTouch){
            // 转发触摸事件给选中项
            if([currentItem respondsToSelector:@selector(ccTouchMoved:withEvent:)])
                [currentItem ccTouchMoved:touch withEvent:event];
            
            return;
        }
    }else{
        // 取消对子项的选择
        [self unschedule:@selector(waitItemTouchBegin)];
    }
    
    CGPoint touchPoint = [touch locationInView:[touch view]]; 
    prePoint = startPoint;
    preTimestamp = touch.timestamp;
    // 当前位置
    CGPoint endPoint = inner.position;
    // 距离差距
    CGPoint dfPoint = ccp(touchPoint.x - startPoint.x, touchPoint.y - startPoint.y);
    // 需要移动到的结束坐标
    CGPoint targetPoint = ccp(endPoint.x + dfPoint.x, endPoint.y + dfPoint.y);
    startPoint = touchPoint;

    if(scrollOrientation == fmScrollOrientationLeftToRight){
        // 从左向右排列 移动时超出边界增加莫摩擦系数
        if (targetPoint.x > touchRect.origin.x || targetPoint.x < -viewPosit.x) {
            friction.x = MAX(0, friction.x - resistance);
            dfPoint.x = dfPoint.x * friction.x;
        }
        endPoint.x += dfPoint.x;
    }else if(scrollOrientation == fmScrollOrientationRightToLeft){
        // 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
        if (targetPoint.x > MAX(touchRect.origin.x, -viewPosit.x) || targetPoint.x < -viewPosit.x) {
            friction.x = MAX(0, friction.x - resistance);
            dfPoint.x = dfPoint.x * friction.x;
        }
        endPoint.x += dfPoint.x;
    }
    
    if (scrollOrientation == fmScrollOrientationBottomToTop) {
        // 从下到上排列 移动时超出边界增加莫摩擦系数
        if (targetPoint.y > touchRect.origin.y || targetPoint.y < -viewPosit.y) {
            friction.y = MAX(0, friction.y - resistance);
            dfPoint.y = dfPoint.y * friction.y;
        }
        endPoint.y -= dfPoint.y;
    }else if(scrollOrientation == fmScrollOrientationTopToBottom){
        // 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
        if (targetPoint.y > MAX(touchRect.origin.y, -viewPosit.y) || targetPoint.y < -viewPosit.y) {
            friction.y = MAX(0, friction.y - resistance);
            dfPoint.y = dfPoint.y * friction.y;
        }
        endPoint.y -= dfPoint.y;
    }

    inner.position = endPoint;
}

- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    // 转发触摸事件给选中项
    if (currentItem != nil) {
        if([currentItem respondsToSelector:@selector(ccTouchEnded:withEvent:)])
            [currentItem ccTouchEnded:touch withEvent:event];
        [self clearTouchStartInfo];
        return;
    }else{
        // 取消对子项的选择
        [self unschedule:@selector(waitItemTouchBegin)];
    }
    
    CGPoint touchPoint = [touch locationInView:[touch view]]; 
    NSTimeInterval timedef = touch.timestamp - preTimestamp;
    // 当前位置
    CGPoint endPoint = inner.position;
    // 距离差距
    CGPoint dfPoint = ccp(touchPoint.x - prePoint.x, touchPoint.y - prePoint.y);
    // 需要移动到的结束坐标
    CGPoint targetPoint = ccp(endPoint.x + (dfPoint.x / (timedef * inertia)), endPoint.y - (dfPoint.y / (timedef * inertia)));
    
    if(scrollOrientation == fmScrollOrientationLeftToRight){
        // 从左向右排列 移动时超出边界增加莫摩擦系数
        endPoint.x = targetPoint.x;
        if (targetPoint.x > touchRect.origin.x) {
            endPoint.x = touchRect.origin.x;
        }else if(endPoint.x < MIN(touchRect.origin.x, -viewPosit.x)){
            endPoint.x = MIN(touchRect.origin.x, -viewPosit.x);
        }
    }else if(scrollOrientation == fmScrollOrientationRightToLeft){
        // 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
        endPoint.x = targetPoint.x;
        if (targetPoint.x < -viewPosit.x) {
            endPoint.x = -viewPosit.x;
        }else if (targetPoint.x > MAX(touchRect.origin.x, -viewPosit.x)) {
            endPoint.x = MAX(touchRect.origin.x, -viewPosit.x);
        }
    }
    
    if (scrollOrientation == fmScrollOrientationBottomToTop) {
        // 从下到上排列
        endPoint.y = targetPoint.y;
        if (targetPoint.y > touchRect.origin.y) {
            endPoint.y = touchRect.origin.y;
        }else if(endPoint.y < MIN(touchRect.origin.y, -viewPosit.y)){
            endPoint.y = MIN(touchRect.origin.y, -viewPosit.y);
        }
    }else if(scrollOrientation == fmScrollOrientationTopToBottom){
        // 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
        endPoint.y = targetPoint.y;
        if (targetPoint.y < -viewPosit.y) {
            endPoint.y = -viewPosit.y;
        }else if (targetPoint.y > MAX(touchRect.origin.y, -viewPosit.y)) {
            endPoint.y = MAX(touchRect.origin.y, -viewPosit.y);
        }
    }
    
    if (endPoint.x != inner.position.x || endPoint.y != inner.position.y) {
        CCActionEase * action = [CCEaseElasticOut actionWithAction:[CCMoveTo actionWithDuration:1 position:endPoint] period:1];
        [inner runAction: action];
    }
}

// 保存触摸开始信息
-(void)saveTouchStartInfo:(UITouch *)touch withEvent:(UIEvent *)event
{
    // 先选中子项
    if (touchFunc == fmScrollBeforeTouch) {
        // 查找是否选中某个实现触摸协议的子项
        [self containsItemTouchBegin:touch withEvent:event];
    }else if(touchFunc == fmScrollAfterTouch){
        beginTouch = [touch retain];
        beginEvent = [event retain];
        [self schedule:@selector(waitItemTouchBegin) interval:1];
    }
}

// 清除触摸开始信息
-(void)clearTouchStartInfo
{
    [beginTouch release]; beginTouch= nil;
    [beginEvent release]; beginEvent = nil;
    [currentItem release]; currentItem = nil;
}

// 等待子项触摸开始
-(void)waitItemTouchBegin
{
    [self unschedule:@selector(waitItemTouchBegin)];
    // 查找是否选中某个实现触摸协议的子项
    [self containsItemTouchBegin:beginTouch withEvent:beginEvent];
    [beginTouch release]; beginTouch= nil;
    [beginEvent release]; beginEvent = nil;
}

// 通知触摸包含项目触摸事件开始
-(BOOL)containsItemTouchBegin:(UITouch *)touch withEvent:(UIEvent *)event
{
    for (id item in items) {
        if ([item conformsToProtocol:@protocol(CCTargetedTouchDelegate)]
            && [item containsTouchLocation:touch]) {
            currentItem = [item retain];
            [currentItem ccTouchBegan:touch withEvent:event];
            return YES;
        }
    }
    return NO;
}

- (void)dealloc 
{
    [inner release]; inner = nil;
    [items release]; items = nil;
    [currentItem release]; currentItem = nil;
    [beginTouch release]; beginTouch= nil;
    [beginEvent release]; beginEvent = nil;
    [super dealloc];
}
@end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值