NGUI 中如何通过代码让列表自动滑动到指定位置

虽然 Unity3D 自 4.6.x 开始就已经推出了全新的 uGUI 系统,不过目前为止我们项目中还木有使用,鉴于之前一直都在使用 NGUI,所以我们依然选择了继续使用我们熟悉的 NGUI 来进行这个项目的开发,也许后续的新项目会迁移到 uGUI 上,不过那是后话了。

在做 UI 开发的过程中,我们都有碰到类似聊天界面中对话列表自动滑到列表最底端,以便于对话列表中显示的永远都是最新的聊天消息的需求,那么这个怎么做呢?依着我们程序猿的直觉,那当然是通过列表控件的 ScrollToPosition 类似的方法来搞定了对吧,说得木有错。

首先,我们要先明确一点,在 NGUI 中的 UITable 控件只是一个容器,其自身并不提供滑动的功能,而是依托于其父对象上的 UIScrollView 来完成所有与滑动相关的操作的。那么我们就来看看 UIScrollView 中有哪些方法是可以用来做 Scroll 相关操作的。

	/// <summary>
	/// Move the scroll view by the specified local space amount.
	/// </summary>

	public virtual void MoveRelative (Vector3 relative)
	{
		mTrans.localPosition += relative;
		Vector2 co = mPanel.clipOffset;
		co.x -= relative.x;
		co.y -= relative.y;
		mPanel.clipOffset = co;

		// Update the scroll bars
		UpdateScrollbars(false);
	}

	/// <summary>
	/// Move the scroll view by the specified world space amount.
	/// </summary>

	public void MoveAbsolute (Vector3 absolute)
	{
		Vector3 a = mTrans.InverseTransformPoint(absolute);
		Vector3 b = mTrans.InverseTransformPoint(Vector3.zero);
		MoveRelative(a - b);
	}

这两个方法看起来非常符合我们的需求,在我们通过计算得出了列表需要滑动的向量之后,使用这两个方法会是非常方便的,那么问题现在已经转化为我们如何计算得出需要滑动的向量值了。这里我就不考虑 MoveAbsolute 显然这货看起来都不是我们想要的,因为其计算的相对位置直接采用了世界坐标的原点来进行计算,最终滑到哪里去,谁泥煤晓得捏。那么我们现在锁定了 MoveRelative 方法了,那么我们就只需要想办法计算出列表滑动到页面最底端时与当前时刻,列表中最后一个条目之间的位置差就好了。看看这个图,我们就能找出列表滑动到最底端的时候,其特征是啥了,然后根据这个特征来进行计算就好了。

列表滑动到最底端时的示意图

这下我们只需要得到 UIScrollview 所在的 Panel 的底端坐标和当前时刻列表最底端条目的底端坐标就可以计算这两者之间的差值了,下面的这个计算方法来源于 NGUI 中的 UICenterOnChild 脚本。

    public void MoveToLastVertical ()
    {
        Vector3[] corners = scrollView.panel.worldCorners;
        Vector3 panelBottom = (corners [0] + corners [3]) * 0.5f;

        Transform panelTrans = scrollView.panel.transform;

        Vector3 cp = panelTrans.InverseTransformPoint (lastItem.position);
        // UITable 中的条目根对象为 UISprite,居中对齐高度为 150,所以得到条目中心点在 Panel 中的坐标之后再加上其高度的一半,获得底端条目的底端坐标
        cp = new Vector3 (cp.x, cp.y + 75, cp.z); 
        Vector3 cc = panelTrans.InverseTransformPoint (panelBottom);
        Vector3 localOffset = cp - cc;

        // Offset shouldn't occur if blocked
        if (!scrollView.canMoveHorizontally) {
            localOffset.x = 0f;
        }
        if (!scrollView.canMoveVertically) {
            localOffset.y = 0f;
        }
        localOffset.z = 0f;

        //        SpringPanel.Begin (scrollView.panel.cachedGameObject,
        //            panelTrans.localPosition - localOffset, 13);
        scrollView.DisableSpring ();
        scrollView.MoveRelative (new Vector3 (localOffset.x, -localOffset.y, localOffset.z));
    }

应用这个原理,针对垂直方向上的列表,我们可以实现滑动到列表的最顶端和最底端,针对水平方向上的列表,我们可以实现滑动到列表的最左侧和最右侧功能。另外如果觉得直接使用 MoveRelative 方法不是很优雅没有动画效果过于生硬的话,那么就只需要将上面那段代码中注视掉的使用 SpringPanel 来移动列表的代码放出来,把下面的两行使用 MoveRelative 方法的代码给关起来就 OK 哒。其实还是蛮好实现的呢,有木有?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据