cocos creator + TypeScript solve the problem ScrollView loading large amounts of Item lead Caton

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/lxt610/article/details/90084836


1 Introduction

  We often come across a list of categories of data processing in normal development! A typical list of such players as possible the number of very large, there may be hundreds! We assume that all at once and it would lead to app creation may settle the collapse! Here we analyze together with the problem.

2, problem analysis

  Click here to view the related introduction of a Unity . Here is not to do a detailed analysis.

3, code section

/**
 * 滑动方向枚举
 */
export enum ScrollDirEnum {
    Vertical,
    Horizon
}

const {ccclass, property} = cc._decorator;

@ccclass
export default class UIScrollControl extends cc.Component {


    /** 滑动框体 - UI组件 */
    private scroll_rect:cc.ScrollView

    /** 可见区域大小 */
    private _show_area_size:cc.Size

    /** 滑动方向 */
    private dir:ScrollDirEnum

    /** 总共数量 */
    private total_count:number

    /**实际创建的item数量 */
    private total_show_item_count: number;

    /** 滑动子对象 - 列表 */
    private all_child_list:Array<cc.Node> = new Array<cc.Node>()

    /** item之间的间距 */
    private distance:number = 0

    /** content初始大小 */
    private start_content_size:cc.Size;

    /**标识 */
    private is_start:boolean = false

    private maxIdx:number = 0;

    /** 刷新回调 */
    private refreshCallBack:Function

    /**
     * 初始化控制
     * @param _temp_node 克隆对象
     * @param _total_count 成员总数
     * @param _size Item水平或竖直方向的距离
     * @param _dir 滑动方向
     * @param callBack 刷新回调
     */
    initControl(_temp_node:cc.Node,_total_count:number,_size:cc.Size,_dir:ScrollDirEnum,callBack:Function):void
    {

        if(callBack)
        {
            this.refreshCallBack = callBack
        }
        
        if (this.is_start == false)
        {
            this.scroll_rect = this.node.getComponent(cc.ScrollView);
            if (this.scroll_rect == null || this.scroll_rect.content == null)
            {
                cc.log("ScrollRect组件错误");
                return;
            }

            this.scroll_rect.content.parent.setAnchorPoint(cc.v2(0.5,0.5))
            let _mask_widget:cc.Widget = this.scroll_rect.content.parent.getComponent(cc.Widget)

            if(_mask_widget == null)
            {
                _mask_widget = this.scroll_rect.content.parent.addComponent(cc.Widget)
            }

            //上下左右对齐边界
            _mask_widget.isAlignLeft = true
            _mask_widget.left = 0

            _mask_widget.isAlignRight = true
            _mask_widget.right = 0

            _mask_widget.isAlignTop = true
            _mask_widget.top = 0

            _mask_widget.isAlignBottom = true
            _mask_widget.bottom = 0

            this._show_area_size = new cc.Size(this.scroll_rect.node.getContentSize())
            this.scroll_rect.content.setContentSize(this._show_area_size)
            this.start_content_size = this.scroll_rect.content.getContentSize();
        }

        if(_temp_node == null)
        {
            cc.log("_temp_node == null")
            return
        }

        this.scroll_rect.content.setContentSize(this.start_content_size)

        this.clear()

        this.total_count = _total_count
        this.dir = _dir as ScrollDirEnum

        if(_dir == ScrollDirEnum.Vertical)
        {
            this.scroll_rect.content.setAnchorPoint(cc.v2(0.5,1))

            this.scroll_rect.content.setContentSize(cc.size(this.start_content_size.width,this.total_count*_size.height))

            this.scroll_rect.content.setPosition(cc.v2(0,-this.total_count*_size.height/2))

            let _tempCount:number = Math.floor(this.start_content_size.height/_size.height)

            this.total_show_item_count = _tempCount + 2

            if(this.total_count <= this.total_show_item_count)
            {
                this.total_show_item_count = this.total_count
            }

            this.distance = _size.height
        }
        else if(_dir == ScrollDirEnum.Horizon)
        {
            this.scroll_rect.content.setAnchorPoint(cc.v2(0,0.5))
            this.scroll_rect.content.setContentSize(cc.size(this.total_count*_size.width,this.start_content_size.height))

            this.scroll_rect.content.setPosition(cc.v2(this.total_count*_size.width/2,0))
            let _tempCount:number = Math.floor(this.start_content_size.width/_size.width)

            this.total_show_item_count = _tempCount + 2

            if(this.total_count <= this.total_show_item_count)
            {
                this.total_show_item_count = this.total_count
            }

            this.distance = _size.width
        }

        let eventHandler = new cc.Component.EventHandler();
        eventHandler.target = this.node;
        eventHandler.component = "UIScrollControl";
        eventHandler.handler = "OnScroll";
        this.scroll_rect.scrollEvents.push(eventHandler); 

        this.is_start == true
        this.maxIdx = 0
    
        this.initShowAreaItems(_temp_node)
    }

    private clear()
    {
        this.scroll_rect.content.removeAllChildren()
        this.all_child_list = []
    }

    /**初始化可见的item */
    private initShowAreaItems(_temp_node:cc.Node)
    {

        for(let i = 0; i < this.total_show_item_count;i++)
        {
            //cc.log(" i = " + i)
            let curPos:cc.Vec2 = cc.v2(0,0)
            let node:cc.Node = cc.instantiate(_temp_node)
            this.scroll_rect.content.addChild(node)
            node.active = true
            node.opacity = 255
            
            if(this.dir == ScrollDirEnum.Vertical)
            {
                curPos.y = -this.distance/2 - this.distance * i
            }
            else if(this.dir == ScrollDirEnum.Horizon)
            {
                curPos.x = this.distance/2 + this.distance * i
            }

            node.name = `cell_${i}`
            node.setAnchorPoint(cc.v2(0.5,0.5))
            node.setPosition(curPos)

            this.onRefresh(node,i,i)

            this.all_child_list.push(node)
        }
        this.scroll_rect.scrollToTop()
    }

    /**滑动事件 */
    private OnScroll()
    {
        //获取滚动视图相对于左上角原点的当前滚动偏移
        let scrollOffset: cc.Vec2 = this.scroll_rect.getScrollOffset();
        let offset:number = 0;

        if(this.dir == ScrollDirEnum.Vertical)
        {
            offset = scrollOffset.y
        }
        else if(this.dir == ScrollDirEnum.Horizon)
        {
            //水平的offset是负数,为什么会有这么sb的设计,将它取反和垂直方向的统一一下
            offset = -scrollOffset.x
        }

        this.refreshLayout(offset)
    }

    /** 强行刷新布局 */
    private refreshLayout(_curOffset:number)
    {
        let offset:number = _curOffset

        //最大高度,超过该高度,不刷新
        let _max_rect_size:number = this.total_count * this.distance

        if (offset < 0 || offset + this._show_area_size.height >= _max_rect_size)
        {
            //cc.log("无法滚动, offset = " + offset + ", offset + this._show_area_size.height >= _max_rect_size >= _max_rect_size = " + (offset + this._show_area_size.height >= _max_rect_size))
            cc.log("无法滚动, offset = " + offset)
            return
        }

        let _index:number = 0 //从0开始
         let _min_index:number = Math.floor(offset / this.distance);

        //miniIdx到maxIdx都会刷新
        for (let i = 0; i < this.total_show_item_count; i++) 
        {
            let node:cc.Node = this.all_child_list[i];
            _index = _min_index + i;
            this.refreshItem(_index, i, node);
        }
        this.maxIdx = _min_index + this.total_show_item_count
    }

    /**
     * 
     * @param _index UI该刷新的第几个元素
     * @param _node_index 
     * @param node 
     */
    refreshItem(_index:number,_node_index:number,node:cc.Node)
    {
        if (_index < 0 || _index >= this.total_count)
        {
            cc.log("索引越界, _index = " + _index + ", this.total_count = " + this.total_count)
            return;
        }

        if (node == null) {
            cc.log("node == null");
            return;
        }

        let curPosition :cc.Vec2 = cc.Vec2.ZERO

        if (this.dir == ScrollDirEnum.Horizon)
        {
            curPosition.x = this.distance / 2 + this.distance * _index;
        }
        else if(this.dir == ScrollDirEnum.Vertical)
        {
            curPosition.y = - this.distance / 2 - this.distance * _index;
        }

        node.setPosition(curPosition)
        this.onRefresh(node, _index, _node_index);
    }

    /**
     * 
     * @param node 
     * @param _index 
     * @param nodeIndex 
     */
    private onRefresh(node:cc.Node,_index:number,nodeIndex:number)
    {
        //cc.log("--------------- _index = " + _index)
        if(this.refreshCallBack != null)
        {
            this.refreshCallBack(node,_index,nodeIndex)
        }
    }
}

  Since the code is more detailed comments, I do not here explained.

4, using for example

4.1, set up the scene

Here Insert Picture Description
  As shown above, we create a new scene, create a button and a ScrollView control, in the production of an Item for cloning, then the script above UIScrollControl mounted on ScrollView control.

4.2 Test

  Test.ts create a script, hanging on Canvas. code show as below:


import UIScrollControl from "./UIScrollControl";

const {ccclass, property} = cc._decorator;

@ccclass
export default class Test extends cc.Component {

    @property(cc.Node) itemNode: cc.Node = null;
    @property(UIScrollControl) scrollViewCtrl: UIScrollControl = null;

    strArr:Array<string> = ["夏","商","周朝","秦朝","西楚","汉朝","三国","两晋","五代", "十国", "隋", "唐","辽", "宋", "夏","金","元","明","清","民国"];

    onClick()
    {
        this.scrollViewCtrl.initControl(this.itemNode,this.strArr.length,cc.size(200,60),0,
	        (_node:cc.Node,_index:number,_nodeIndex:number)=>{
	            let label:cc.Label = _node.getComponentInChildren(cc.Label)
	            label.string = this.strArr[_index]
	        })
    }
}

This specifies the object, click the button together to witness the miracle of it!

4.3, showing the effect of

Here Insert Picture Description

4.4, Demo Download

  In order to facilitate the use of this given directly download link, click here to download the complete project. !

5. Conclusion


The End
  Well, today's share on here, if inadequacies also hope you correct me in time, feel free to discuss the exchange! ! !


Like friends, please Bangding, thumbs up, comment! You certainly inexhaustible power of my writing!

Guess you like

Origin blog.csdn.net/lxt610/article/details/90084836