Unity实现TableView

基于Scrollview封装的TableView,实现对视野外的Cell回收利用,减少创建Cell的开销。

核心逻辑如下:

/***************************************
    动态使用cell核心逻辑开始
**************************************/

//计算所有cell的坐标信息
private void CaculateCellPosition()
{
    int cellsCount = _dataSource.NumberOfCellsInTableView(this);
    if (cellsCount > 0)
    {
        _cellsPositionsList.Clear();

        float currentPos = 0;
        for (int i = 0; i < cellsCount; i++)
        {
            _cellsPositionsList.Add(currentPos);
            float rowSize = _dataSource.TableCellSizeForIndex(this, i);
            currentPos += rowSize;
        }
        _cellsPositionsList.Add(currentPos);
    }
}

//更新内嵌容器的size
private void UpdateContentSize()
{
    int cellsCount = _dataSource.NumberOfCellsInTableView(this);
    if (cellsCount > 0)
    {
        float maxPosition = _cellsPositionsList[cellsCount];
        if (IsHorizontal)
        {
            if (maxPosition > TableViewSize.width)
            {
                InnerContainerSizeDelta = maxPosition - TableViewSize.width;
            }
        }
        else 
        {
            InnerContainerSizeDelta = maxPosition;
        }
    }
}

//获取指定位置对应cell的索引
private int GetIndexFromOffset(float offset)
{
    int index = 0;
    int maxIdx = _dataSource.NumberOfCellsInTableView(this) - 1;
    index = this.CaculateIndexFromOffset(offset);
    if (index != INVALID_INDEX)
    {
        index = Math.Max(0, index);
        if (index > maxIdx)
        {
            index = INVALID_INDEX;
        }
    }
    return index;
}

//计算指定位置对应cell的索引
private int CaculateIndexFromOffset(float offset)
{
    int low = 0;
    int high = _dataSource.NumberOfCellsInTableView(this) - 1;
    float search = offset;

    while (high >= low)
    {
        int index = low + (high - low) / 2;
        float cellStart = _cellsPositionsList[index];
        float cellEnd = _cellsPositionsList[index + 1];

        if (search >= cellStart && search <= cellEnd)
        {
            return index;
        }
        else if (search < cellStart)
        {
            high = index - 1;
        }
        else
        {
            low = index + 1;
        }
    }

    if (low <= 0)
    {
        return 0;
    }
    return INVALID_INDEX;
}

//更新视野内cell的显示
private void UpdateCellsShow()
{
    int countOfItems = _dataSource.NumberOfCellsInTableView(this);
    if (0 == countOfItems)
    {
        return;
    }

    int startIdx = 0, endIdx = 0, idx = 0, maxIdx = 0;
    float offset = this.GetContentOffset();
    maxIdx = Mathf.Max(countOfItems - 1, 0);

    endIdx = this.GetIndexFromOffset(offset);
    if (endIdx == INVALID_INDEX)
    {
        endIdx = countOfItems - 1;
    }

    offset -= IsHorizontal ? -TableViewSize.width : this.TableViewSize.height;
    startIdx = this.GetIndexFromOffset(offset);
    if (startIdx == -1)
    {
        startIdx = countOfItems - 1;
    }

    if (IsHorizontal)
    {
        //横向与纵向相反
        int tmp = startIdx;
        startIdx = endIdx;
        endIdx = tmp;
    }

    //--
    _usingCellsList.Sort(new TableViewCellComparer());
    //--检测可回收的cell--BEGIN
    if (_usingCellsList.Count > 0)
    {
        var cell = _usingCellsList[0];
        idx = cell.Index;

        while (idx < startIdx)
        {
            this.MoveCellOutOfSight(cell);
            if (_usingCellsList.Count > 0)
            {
                cell = _usingCellsList[0];
                idx = cell.Index;
            }
            else
            {
                break;
            }
        }
    }
    if (_usingCellsList.Count > 0)
    {
        var cell = _usingCellsList[_usingCellsList.Count - 1];
        idx = cell.Index;

        while (idx <= maxIdx && idx > endIdx)
        {
            this.MoveCellOutOfSight(cell);
            if (_usingCellsList.Count > 0)
            {
                cell = _usingCellsList[_usingCellsList.Count - 1];
                idx = cell.Index;
            }
            else
            {
                break;
            }
        }
    }
    //--检测可回收的cell--END

    for (int i = startIdx; i <= endIdx; i++)
    {
        if (_cellUsingIdxs.Contains(i))
        {
            continue;
        }
        this.UpdateCellByIndex(i);
    }
}
//更新指定cell的显示
private void UpdateCellByIndex(int index)
{
    TableViewCell cell = _dataSource.TableCellAtIndex(this, index);
    cell.SetIndex(index);
    cell.ClickEvent.RemoveListener(CellDidClick);
    cell.ClickEvent.AddListener(CellDidClick);
    if (cell.gameObject.activeSelf == false)
    {
        cell.gameObject.SetActive(true);
    }
    //--
    float cellSize = _dataSource.TableCellSizeForIndex(this, index);
    Vector2 pos = new Vector2();
    if (IsHorizontal)
    {
        pos.x = _cellsPositionsList[index] - InnerContainerSizeDelta * 0.5f - 0.5f * TableViewSize.width + cellSize * 0.5f;
        pos.y = 0;
    }
    else 
    {
        pos.x = 0;
        pos.y = _cellsPositionsList[index] - InnerContainerSizeDelta * 0.5f + cellSize * 0.5f;
    }
    cell.gameObject.GetComponent<RectTransform>().anchoredPosition = pos;
    //--
    _usingCellsList.Add(cell);
    _cellUsingIdxs.Add(cell.Index);
}

//回收视野外的cell
private void MoveCellOutOfSight(TableViewCell cell)
{
    _freedCellsStack.Push(cell);
    _usingCellsList.Remove(cell);
    _cellUsingIdxs.Remove(cell.Index);

    cell.ClickEvent.RemoveListener(CellDidClick);
    cell.ResetCell();
}

/***************************************
    动态使用cell核心逻辑结束
**************************************/

如何使用呢?按照下面的流程操作即可。

1.创建Test 脚本,脚本继承ITableViewDataSource并实现对应的方法。【ITableViewDelegate根据具体情况决定继承与否】
 public interface ITableViewDataSource
 {
    int NumberOfCellsInTableView(TableView tableView);
    float TableCellSizeForIndex(TableView tableView, int index);
    TableViewCell TableCellAtIndex(TableView tableView, int index);
 }
 public class Test : MonoBehaviour, ITableViewDataSource, ITableViewDelegate
    {
        public TableView tableView;
        public GameObject hCell = null;
        public GameObject vCell = null;
        void Start()
        {

        }

        public int NumberOfCellsInTableView(TableView tableView)
        {
            return 20;
        }

        public float TableCellSizeForIndex(TableView tableView, int index)
        {
            return 90;
        }

        public TableViewCell TableCellAtIndex(TableView tableView, int index)
        {
            TableViewCell cell = tableView.ReusableCell();
            if (cell == null)
            {
                GameObject obj = Instantiate(tableView.IsHorizontal ? hCell : vCell, tableView.InnerContainerContent().transform);
                cell = obj.GetComponent<TableViewCell>();
            }
            cell.name = "Cell " + index;
            return cell;
        }

        public void TableCellClicked(TableView tableView, int index)
        {
            Debug.Log("TableViewDidSelectCellForRow : " + index);
        }
    }
 2.脚本挂载到载体,并绑定对应的变量【HCell和VCell只是为了方便测试横/纵向滚动】

 3.创建TestCell脚本,并继承TableViewCell
  public class TestCell : TableViewCell
  {
      public Text txt;

      public override void UpdateDisplay()
      {
          txt.text = "Index " + Index;
      }
  }
 4.将TestCell脚本挂载到Scrollview中显示的预制体上
 5.Test脚本中设置TableView的必要属性,调用ReloadData()接口
void Start()
{
     tableView.Delegate = this;
     tableView.DataSource = this;
     tableView.ReloadData();
}

效果如下:  

u3d-demo

项目地址:https://github.com/jjinglover/UnityTableView

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值