欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

UGUI实现ScrollView无限滚动效果

程序员文章站 2023-12-06 13:13:58
抽空做了一个ugui的无限滚动的效果。只做了一半(向下无限滚动)。网上也看了很多教程,感觉还是按照自己的思路来写可能比较好。搭建如下: content节点不添加任何组...

抽空做了一个ugui的无限滚动的效果。只做了一半(向下无限滚动)。网上也看了很多教程,感觉还是按照自己的思路来写可能比较好。搭建如下:

UGUI实现ScrollView无限滚动效果

content节点不添加任何组件。布局组件默认是会重新排版子节点的,所以如果子节点的位置变化,会重新排版,不能达到效果。size fitter组件也不加,自己写代码调整size大小(不调整大小,无法滑动)。

最主要的实现过程就是用queue来搬运cell。在向下滚动的过程中(鼠标上滑),顶部滑出view port的cell被搬运到底部续上。这点类似于queue的先见先出原则,再把dequeue出来的元素添加到末尾,就很类似于scrollview的无限滚动的原理了。在鼠标上滑的过程中,content的posy值是一直增加的,所以触发滚动的条件就可以设定为位移之差大于cell的高度值即可。

 数据的刷新,数据到头之后,不能再次进行滚动轮换了,这里用一组值来记录初始化的一组cell显示的是数据的哪一段。例如headnum和tainum。比如用20个cell显示100条数据。初始化后,headnum就是0,tailnum就是19。上滑一行数据后,headnum=4,tailnum=23(这里假设是20个cell排成4列)。

下面是完整代码:

public class uiscrollviewtest : monobehaviour {
 
  public recttransform content;
  public gameobject cell;
  // cell的初始化个数
  public int cellamount = 0;
  // 鼠标上滑时,存储cell的queue。正序存储
  public queue f_cellquee = new queue();
  // 鼠标下滑时,存储cell的queue。到序存储
  public queue b_cellquee = new queue();
  // cell的size
  public vector2 cellsize = new vector2(100,100);
  // cell的间隔
  public vector2 celloffset = new vector2(0,0);
  // 列数
  public int columncount = 0;
  private int rowcount;
  // 上一次content的位置
  public float lastpos;
  // 滚动的次数
  public int loopcount = 0;
  // cell显示的数据段的开头和结尾序号
  public int headnum = 0;
  public int tailnum;
 
  public sprite[] sp;
  public list<sprite> data;
 
 
  void start()
  {
    for (int i = 0; i < sp.length; i++)
    {
      data.add(sp[i]);
    }
 
    initialscrollview(data);
    tailnum = cellamount-1;
    lastpos = content.localposition.y;
    //debug.logerror("行数是:::" + rowcount);
 
    //debug.logerror("+++++++++++++++++  " + (5>>3));
  }
 
 
  void update()
  {
    // 触发滚动。
    if (content.localposition.y - lastpos > cellsize.y && data.count - cellamount - loopcount*columncount >0)
    {
      //debug.logerror("11111111111  " + (data.count - cellamount - loopcount * columncount));
      loopscrolview(data);
      lastpos = content.localposition.y;
    }
  }
 
 
  
  
  // 初始化cell
  void initialscrollview(list<sprite> data)
  {
    for (int i = 0; i < cellamount; i++)
    {
      gameobject obj = instantiate(cell.gameobject);
      obj.transform.setparent(content);
      obj.name = "cell0" + i.tostring();
      obj.transform.getchild(0).getcomponent<text>().text = "cell0"+i.tostring();
      // 显示默认的数据
      obj.getcomponent<image>().sprite = data[i];
    }
    // 初始化queue
    for (int i = content.childcount-1; i >= 0; i--)
    {
      b_cellquee.enqueue(content.getchild(i).gameobject);
    }
    for (int i = 0; i < content.childcount; i++)
    {
      f_cellquee.enqueue(content.getchild(i).gameobject);
    }
 
    // 计算行数
    if (cellamount % columncount >0)
    {
      rowcount = cellamount / columncount + 1;
    } else {
      rowcount = cellamount / columncount;
    }
 
    // 排列cell的位置
    int index = 0;
    for (int r = 1; r <= rowcount; r++)
    {
      for (int c = 1; c <= columncount; c++)
      {
        if (index < cellamount)
        {
          vector2 pos = new vector2(cellsize.x / 2 + (cellsize.x + celloffset.x) * (c-1), -cellsize.y / 2 - (celloffset.y + cellsize.y) * (r-1));
          content.getchild(index).getcomponent<recttransform>().setinsetandsizefromparentedge(recttransform.edge.top, 0, 100);
          content.getchild(index).getcomponent<recttransform>().setinsetandsizefromparentedge(recttransform.edge.left, 0, 100);
          content.getchild(index).getcomponent<recttransform>().anchoredposition = pos;
          index++;
        }
      }
    }
 
    vector2 v = content.sizedelta;
    // 初始化content的size
    content.sizedelta = new vector2(v.x, rowcount * cellsize.y + celloffset.y*(rowcount-1));
  }
 
 
  /// 保持content的大小,这里是保持大小为在cell的行数基础上,向下多出bottomcount行的距离
  void setcontentsize(int uppercount, int bottomcount)
  {
    if (content.sizedelta != new vector2(content.sizedelta.x, content.sizedelta.y + bottomcount * (cellsize.y + celloffset.y)))
    {
      content.sizedelta = new vector2(content.sizedelta.x, content.sizedelta.y + bottomcount*(cellsize.y + celloffset.y));
    }
  }
 
  // 计算顶部的cell轮换到底部时的位置。以当前最后一行的最后一个cell的位置为基准计算。
  void setbottomcellposition(int index, recttransform rect, vector2 pos)
  {
    vector2 v = vector2.zero;
    if (cellamount % columncount == 0) // 整除。每一行都满的情况。
    {
      float x = pos.x - cellsize.x * (columncount - index-1) - celloffset.x * (columncount-index-1);
      float y = pos.y - cellsize.y - celloffset.y;
      v = new vector2(x,y);
    }
    // 出现不满行的情况。例如数据有103个,可以用23个cell来轮换。这样就会出现不满行的情况。
    // 这种情况下是顶部的一行cell顺次接到底部不满的行。例如23号cell后面接1号和2号cell,3号和4号cell填充到第“7”行
    else if (cellamount % columncount + index+1<=columncount) 
    {
      float x = pos.x + cellsize.x * (index+1) + celloffset.x * (index+1);
      float y = pos.y;
      v = new vector2(x, y);
    }
    else
    {
      float x = pos.x - cellsize.x * (columncount - index-1) - celloffset.x * (columncount - index-1);
      float y = pos.y - cellsize.y - celloffset.y;
      v = new vector2(x, y);
    }
    //debug.logerror("++++++++++++++  " + pos+ "         "+ v);
    rect.anchoredposition = v;
    rect.setaslastsibling();
  }
 
  // 计算底部的cell轮换到顶部是的位置,基准位置是当前行的第一个cell。
  void setuppercellposition(int index, recttransform rect, vector2 pos)
  {
    vector2 v = vector2.zero;
    if (cellamount % columncount == 0) // 整除
    {
      float x = pos.x + cellsize.x * index + celloffset.x * index;
      float y = pos.y + cellsize.y + celloffset.y;
      v = new vector2(x, y);
    }
    //else if (cellamount % columncount + index + 1 <= columncount)
    //{
    //  float x = pos.x + cellsize.x * (index + 1) + celloffset.x * (index + 1);
    //  float y = pos.y;
    //  v = new vector2(x, y);
    //}
    //else
    //{
    //  float x = pos.x - cellsize.x * (columncount - index - 1) - celloffset.x * (columncount - index - 1);
    //  float y = pos.y - cellsize.y - celloffset.y;
    //  v = new vector2(x, y);
    //}
    //debug.logerror("++++++++++++++  " + pos+ "         "+ v);
    rect.anchoredposition = v;
    rect.setasfirstsibling();
  }
 
 
  // 鼠标上滑时,显示当前cell的数据。同时记录数据段的序号递增。
  void showrestcelldata(image cell, int index)
  {
    if (tailnum< data.count-1)
    {
      debug.logerror("当前的序号是::::" + tailnum);
      tailnum++;
      headnum++;
      cell.sprite = data[tailnum];
    }
  }
 
  void showpreviouscelldata(image cell, int index)
  {
    if (headnum > 0)
    {
      debug.logerror("当前的序号是::::" + headnum);
      tailnum--;
      headnum--;
      cell.sprite = data[headnum];
    }
  }
 
 
  // 轮换的函数。每次乱换一行的cell。
  void loopscrolview(list<sprite> data)
  {
    setcontentsize(0, 1);
    loopcount++;
    recttransform rect2 = content.getchild(content.childcount - 1).getcomponent<recttransform>();
    for (int i = 0; i < columncount; i++)
    {
      gameobject obj = f_cellquee.dequeue() as gameobject;
      recttransform rect = obj.getcomponent<recttransform>();
      showrestcelldata(obj.getcomponent<image>(), i);
      setbottomcellposition(i, rect, rect2.anchoredposition);
      f_cellquee.enqueue(obj);
    }
  }
 
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。