【小松教你手游开发】【系统模块开发】ngui做聊天系统

用ngui做聊天系统有个简单的方法是用教程Exampl12里的TextList来做聊天系统。
但显然一个UILabel做的聊天系统拓展性不高,并且要做特殊点击事件会变得很麻烦。

所以我们还是用一个UIScrollView下挂一个UITable,把UILabel和其他东西封装成一个Prefab一个个加载到UITable来实现。

如果不考虑其他因素就是一个简单的UIScrollView相信大家都没什么问题。

不过项目中会有两个技术难点。
1.要实现图文混排
2.如果对话多的话(比如世界频道,每个玩家都可以发信息,很容易就一百条以上),肯定不能每个都实例化一个Prefab,所以UIScrollView要做循环加载。


1.关于图文混排在之前的文章中就讲到怎么实现

所以本文主要讲的是第二点.循环加载

循环加载的意思就是,比如一个聊天系统,在可显示的范围内,用最少的Prefab,通过改里面的数值去显示不同数据,而不是每一个数据实例化一个Prefab。

在ngui的教程里就有教你怎么用ngui的UIScrollView里实现循环加载。
但是这里有一个前提是。它用的是UIScrollView下挂UIGrid,UIGrid下挂Prefab。且每个Prefab的高度一致。
这样可以准确的计算出最少需要多少个Prefab,现在滑动到哪个Prefab,且现在每个Prefab应该插入什么数据。

但是像聊天这样的系统,每个人发的每句话字数都不一样,也就是每个UILabel的高度不一样。这样导致了上面的计算都无法实现。

所以本来做好的UIScrollView的循环加载就需要调整。

首先是不用UIGrid用UITable。因为UITable不要求每一行的间距统一

接着就是写一个继承UIScrollView的类来实现循环加载。我这里叫CUIChatScrollView: UIScrollView

因为代码比较复杂,在最后的时候在一次过放上代码,这里先讲思路


首先按一般的循环加载我们要做的是知道这个UISCrollView最多同时要显示多少个Prefab,我们总共有多少数据。每个Prefab多高。当最上一块或最下一个滑出边框就把它移到另一边。

而由于现在高度不一,所以上面除了总共有多少数据是知道的其他都不能确定。

所以我们只能换种方式。

我们实例化足够的数,通过设定两个上线临界值来判断是否需要把Prefab移到另一边,而不通过边框计算。
通过索引index而不通过计算滑动位置与这个的比例来计算应该插入的数据的索引。


所以对于第一种可以做到的,最多同屏显示6条数据的话,我们只用实例化7个Prefab,这种最优方案我们没法使用。
我们只能预估一个值,实例化一个合适的数,比如每一个聊天语句只有一行的话,整个界面有多少条,然后再比这个数多一点。


在我的项目中上下最多显示3条,不过我这里也需要6条来实现循环加载。也就是实例化10个Prefab。
如果是文字只有一行的话我的像素是25,我的上下临界值设为250.
(这些数值都还可以再优化,我只是随便取了一些数)


在原来一般的循环加载逻辑下,实例化10个Prefab,当向下滑动时的Prefab超过下边框就移到另一边,向上一样。

而当前的Prefab应该插入什么数据不再通过计算当前点击position除于每个Prefab的高度interver来计算出index来插入数据。而是记录下index,如果把上面的Prefab移到下面就index++。如果下面的移到上面就index--。
而因为index作为索引如果从0开始,而最下面的index就是index加实例化的Prefab数,我的就是10.


而原本计算目标position的方式也不能用了。原本滑动到顶端把prefab移到低端通过最下面的prefab加高度interver就能知道。
现在最下面的要通过先把文字放到UILabel,通过NGUI的函数NGUIMath.CalculateRelativeWidgetBounds计算UILabel的宽高,加上最下面的Prefab的position来确定位置。


大概思路是这样


下面贴上代码
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class CUIChatScrollView : UIScrollView
{

    public delegate void OnMoveCallBack();
    /// <summary>
    /// 每次滑动结束的回调
    /// </summary>
    public OnMoveCallBack mOnMoveCallback;

    public delegate void OnMoveNextPage(int pageindex);
    public OnMoveNextPage mOnMoveNextPage;

    private float mTopOffSetValue = 250;
    private float mBottomOffSetValue = 250;
    /// <summary>
    /// 当前数据总数
    /// </summary>
    private int mTotalCount = 0;
    /// <summary>
    /// 子物体当前临时坐标
    /// </summary>
    private float mTempPosition = 0;
    /// <summary>
    /// 子物体当前临时相对坐标(与UIPanel偏移、DynamicComponentTran、自己本身坐标之和,如果UIpanel裁剪区域有偏移还需要加上UIPanel FinalClipRegion的偏移值(X或者Y))
    /// </summary>
    private float mBorderValue = 0;
    /// <summary>
    /// 当前显示出来的物体列表
    /// </summary>
    private List<CBaseComponent> mCurrentComList = new List<CBaseComponent>();
    /// <summary>
    /// 当前循环临时加载的列表(达到临界值需要移动的Components)
    /// </summary>
    private List<CBaseComponent> mTempComList = new List<CBaseComponent>();
    /// <summary>
    ///  挂载UIGrid的组件物体,是components父物体,UIPanel子物体
    /// </summary>
    private Transform DynamicComponentTran;
    /// <summary>
    /// 当前ScrollView依赖的UIPanel
    /// </summary>
    private UIPanel mUIPanel;
    /// <summary>
    /// UIPanel最终的绘制区域、坐标
    /// </summary>
    private Vector4 mPanelFinalClipRegion;
    /// <summary>
    /// 是否需要请求下一页数据(用于多数据分页加载情况)
    /// </summary>
    private bool bNeedReqNextPage;
    /// <summary>
    /// 每页的数量(用于分页)
    /// </summary>
    private int mPrePageNumber;
    /// <summary>
    /// 是否是排行榜模式
    /// </summary>
    private bool bIsRankModel;
    /// <summary>
    /// 当前自己的排名
    /// </summary>
    private int mCurrentMyRank;
    /// <summary>
    /// 缓存开关
    /// </summary>
    private Dictionary<int, bool> bHasRequestDic = new Dictionary<int, bool>();
    /// <summary>
    /// 用于比较的X坐标
    /// </summary>
    private float xPos;
    /// <summary>
    /// 用于比较的Y坐标
    /// </summary>
    private float yPos;
    /// <summary>
    /// 是否需要3D显示效果
    /// </summary>
    private bool bShow3DStyle = false;
    /// <summary>
    /// 居中的子物体
    /// </summary>
    public Transform mCenterTranform;
    /// <summary>
    /// 子物体的depath
    /// </summary>
    private UIPanel mTempUIPanel;
    /// <summary>
    ///  ScrollView 的UIPanel depath。
    /// </summary>
    private int mUIPanelDepth = 1;

    private int m_Index = 0;
    private int m_maxShowNum = 0;

    public void Init<T>(List<T> components, int maxShowNum, int totalNumber, Transform dyTransorm, Movement moveMent = Movement.Horizontal, bool needrequest = false, int prepagenum = 0, bool isrank = false, int myrank = 0, bool bshow3d = false) where T : CBaseComponent
    {
        m_Index = maxShowNum-1;
        m_maxShowNum = maxShowNum;
        mCurrentComList.Clear();
        for (int i = 0, length = components.Count; i < length; i++)
        {
            if (components[i] is CBaseComponent)
            {
                mCurrentComList.Add(components[i]);
            }
        }
        //mCurrentComList = components as List<CBaseComponent>;
        DynamicComponentTran = dyTransorm;
        mTotalCount = totalNumber;
        movement = moveMent;
        mPanelFinalClipRegion = (mUIPanel ?? (mUIPanel = gameObject.GetComponent<UIPanel>())).finalClipRegion;
        mUIPanelDepth = mUIPanel.depth + 10;//子物体固定高出父物体深度10个单位
        //if (movement == Movement.Vertical)
        //    mOffSetValue = mPanelFinalClipRegion.w / 2 + mItemIntervalValue / 2f;
        //else if (moveMent == Movement.Horizontal)
        //    mOffSetValue = mPanelFinalClipRegion.z / 2 + mItemIntervalValue / 2f;
        bNeedReqNextPage = needrequest;
        mPrePageNumber = prepagenum;
        bIsRankModel = isrank;
        mCurrentMyRank = myrank;
        bHasRequestDic.Clear();
        bShow3DStyle = bshow3d;
        momentumAmount = 35f;
    }

    public override void MoveRelative(Vector3 relative)
    {
        base.MoveRelative(relative);
        CheckScrollView(relative);
    }

    public override void AdjustSpring(Vector3 relative)
    {
        base.AdjustSpring(relative);
        CheckScrollView(relative);
    }

    private void CheckScrollView(Vector3 relative)
    {
        if (mCurrentComList == null || mCurrentComList.Count == 0)
            return;

        if (movement == Movement.Horizontal)
        {
            xPos = relative.x;
            mCurrentComList.Sort(delegate (CBaseComponent a, CBaseComponent b) { return a.LocalPosition.x.CompareTo(b.LocalPosition.x); });
        }
        else if (movement == Movement.Vertical)
        {
            yPos = relative.y;
            mCurrentComList.Sort(delegate (CBaseComponent a, CBaseComponent b) { return b.LocalPosition.y.CompareTo(a.LocalPosition.y); });
        }

        mTempComList.Clear();

        if ((yPos > 0 && movement == Movement.Vertical) || (xPos < 0 && movement == Movement.Horizontal))
        {
            Transform temptran = mCurrentComList[mCurrentComList.Count - 1].ComTransform;
            if (movement == Movement.Horizontal)
                mTempPosition = temptran.localPosition.x;
            else if (movement == Movement.Vertical)
                mTempPosition = temptran.localPosition.y;
            mTempPosition = Mathf.Abs(mTempPosition);

            if (true)
            {
                for (int i = 0, j = mCurrentComList.Count; i < j; i++)
                {
                    temptran = mCurrentComList[i].ComTransform;
                    if (movement == Movement.Horizontal)
                    {
                        mBorderValue = (temptran.localPosition.x*temptran.localScale.x + transform.localPosition.x + DynamicComponentTran.localPosition.x + Mathf.Abs(mPanelFinalClipRegion.x));
                        if (mBorderValue > -mTopOffSetValue)
                        {
                            break;
                        }
                    }
                    else if (movement == Movement.Vertical)
                    {
                        mBorderValue = (temptran.localPosition.y * temptran.localScale.y + transform.localPosition.y + DynamicComponentTran.localPosition.y + Mathf.Abs(mPanelFinalClipRegion.y));
                        if (mBorderValue < mTopOffSetValue)
                        {
                            break;
                        }
                    }
                    mTempComList.Add(mCurrentComList[i]);

                }
            }
            for (int i = 0, j = mTempComList.Count; i < j; i++)
            {
                if (m_Index >= mTotalCount - 1) break;
                m_Index++;
                if (movement == Movement.Vertical)
                {
                    float spriteHeight = NGUIMath.CalculateRelativeWidgetBounds(transform, mCurrentComList[mCurrentComList.Count - 1].ComTransform, true).size.y;
                    mTempComList[i].LocalPosition = new Vector3(0, -(spriteHeight +mTempPosition), 0);
                }
                //mTempComList[i].LocalPosition = new Vector3(0, -(mItemIntervalValue * (i + 1) + mTempPosition), 0);
                mTempComList[i].InitData(m_Index);
            }
        }
        else if ((yPos < 0 && movement == Movement.Vertical) || (xPos > 0 && movement == Movement.Horizontal))
        {
            Transform temptran = mCurrentComList[0].ComTransform;
            if (movement == Movement.Horizontal)
                mTempPosition = temptran.localPosition.x;
            else if (movement == Movement.Vertical)
                mTempPosition = temptran.localPosition.y;
            mTempPosition = Mathf.Abs(mTempPosition);

            if (true)
            {
                for (int i = mCurrentComList.Count - 1; i >= 0; i--)
                {
                    temptran = mCurrentComList[i].ComTransform;
                    if (movement == Movement.Horizontal)
                    {
                        mBorderValue = (temptran.localPosition.x + transform.localPosition.x + DynamicComponentTran.localPosition.x + Mathf.Abs(mPanelFinalClipRegion.x));
                        if (mBorderValue <mBottomOffSetValue)
                        {
                            break;
                        }
                    }
                    else if (movement == Movement.Vertical)
                    {
                        mBorderValue = (temptran.localPosition.y + transform.localPosition.y + DynamicComponentTran.localPosition.y + Mathf.Abs(mPanelFinalClipRegion.y));
                        if (mBorderValue > -mBottomOffSetValue)
                        {
                            break;
                        }
                    }
                    mTempComList.Add(mCurrentComList[i]);
                }
            }
            for (int i = 0, j = mTempComList.Count; i < j; i++)
            {
                if (m_Index <= m_maxShowNum-1) break;

                m_Index--;

                //mTempComList[i].LocalPosition = new Vector3(0, -(mTempPosition - mItemIntervalValue * (i + 1)), 0);
                mTempComList[i].InitData(m_Index - m_maxShowNum+1);

                if (movement == Movement.Vertical)
                {
                    float spriteHeight = NGUIMath.CalculateRelativeWidgetBounds(transform, mTempComList[i].ComTransform, true).size.y;
                    Vector3 newPosition;
                    if (i == 0)
                        newPosition = new Vector3(0, -(mTempPosition - spriteHeight), 0);
                    else
                        newPosition = new Vector3(0, -(Mathf.Abs(mTempComList[i - 1].LocalPosition.y) - spriteHeight), 0);
                    mTempComList[i].LocalPosition = newPosition;
                }
            }
        }
        if (mOnMoveCallback != null)
        {
            mOnMoveCallback();
        }
    }


}



  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值