改善CEGUIMultiColumnList默认排序方式存在的问题

最近在用CEGUIMultiColumnList对控件中的元素进行排序时,有的列会出现排序混乱的情况。研究了一天CEGUIMultiColumnList的源代码,发现其默认实现方式有一些问题。下面对于CEGUIMultiColumnList简称为列表,对于没用过CEGUIMultiColumnList的各位,想象一下EXCEL表格的样子就好。

在使用列表时,点击列表的表头(列表最上面一行,各种列名所在的行)中不同列改变升序或降序排序时,在列表内部会调用到CEGUIMultiColumnList::resortList():

void CEGUIMultiColumnList::resortList()
{
    ListHeaderSegment::SortDirection dir = getSortDirection();

    if (dir  == ListHeaderSegment::Descending)
    {
        std::sort(d_grid.begin(), d_grid.end());
    }
    else if (dir == ListHeaderSegment::Ascending)
    {
        std::sort(d_grid.begin(), d_grid.end(), pred_descend);
    }
}
默认情况下,std::sort将使用operator<对传入区间内的所有元素进行排序,而传入的参数是[d_grid.begin(), d_grid.end()),这时就要看看d_grid的定义了:

typedef std::vector<ListRow>    ListItemGrid
ListItemGrid    d_grid;

还是没有找到std::sort真正处理的元素,继续找,ListRow定义:

struct ListRow
{
    typedef std::vector<ListboxItem*>    RowItems;
    RowItems    d_items;
    uint              d_sortColumn;
    uint              d_rowID;

    ListboxItem* const& operator[](uint idx) const {return d_items[idx];}
    LIstboxItem*& operator[](uint idx) {return d_items[idx];}
    bool operator<(const ListRow& rhs) const;
    bool operator>(const ListRow& rhs) const;
}

重载的operator<和operator >,排序就靠他俩了。

下面的介绍都以operator<为例,ListRow::operator<()的定义:

bool MultiColumnList::ListRow::operator<(const ListRow& rhs) const
{
    ListboxItem* a = d_items[d_sortColumn];
    ListboxItem* b = rhs.d_items[d_sortColumn];

    if(!b)
    {
        return false;
    }
    else if (!a)
    {
        return true;
    }
    else
    {
        return *a < *b;
    }
}

*a < *b的比较又是哪来的呢?看到a,b的类型为ListboxItem*,找到ListboxItem,其中对于operator<()的定义:

virtual bool operator<(const ListboxItem& rhs) const { return true; }

上述定义为virtual funtion,这时要看看有没有派生类重写了这个函数,查看CEGUIListboxItem的继承关系时,发现继承自CEGUIListboxItem的派生类有两个:CEGUIListboxTextItem,CEGUIListboxImageItem。而向列表填充数据时,我通常用的函数是CEGUIMultiColumnList::ListboxTextItem* setItem(...)。由此,我应该查找ListboxTextItem中operator<的定义:

virtual bool operator<(const ListboxItem& rhs) const { return d_itemText < ((ListboxTextItem*)&rhs)->getText(); }

可以看到,列表在按列排序时默认的操作是直接对两个字符串进行比较,字符串比较实际上是逐一对字符的ASCII值进行比较。而在我所用的环境中,某一列填充的元素除了为字符串(玩家的姓名)之外,还有有全为数字(玩家的等级)的可能。这时按照ASCII进行比较会出现预期以外的情况,比如对101,13进行升序排序,按照ASCII排序的结果是101,13;按数字排序的结果是13,101。经过初步分析,我打算重写ListboxTextItem::operator<()以实现对数字的比较:

bool ListboxTextItem::operator<(const ListboxTextItem& rhs) const
{
    CEGUI::String text = ((ListboxTextItem*)&rhs)->getText();
    bool itemTextIsDigit = true;
    bool textIsDigit        = true;
    int    itemTextValue  = 0;
    int    textValue         = 0;

    for (size_t i = 0; i < d_itemText.size(); ++i)
    {
        if(!isdigit(d_itemText[i]))
        {
            itemTextIsDigit = false;
            bresk;
        }
    }

    for (size_t j = 0; j < text.size(); ++j)
    {
        if(!isdigit(text[j]))
        {
            textIsDigit = false;
            break;
        }
    }

    itemTextValue = CEGUI::PropertyHelper::stringToInt(d_itemText);
    textValue = CEGUI::PropertyHelper::stringtoInt(text);
    
    if (itemTextIsDigit && textIsDigit)
    {
        return itemTextValue < textValue;
    }
    else
    {
        return d_itemText < text;
    }

    return false;
}

 上述版本大体实现了我的预期,不过却引入了2个问题:

1.这种方式每次都会对传入的文本调用isdigit(),无论文本是数字与否,在列表中的条目越来越多的情况下,这样会很影响效率。于是我想到,能不能传入一个标志,来标示文本是否为纯数字。这样可以避免某列存储的全是或部分是字符串的情况,这种情况下按ASCII比较即可。那么标志究竟在什么地方设置呢?其实列表在填充数据的时候,我们就应该清楚该列的内容是字符串还是数字,所以可以在填充数据时设置标志,我是通过调用CEGUIMultiColumnList::ListboxTextItem* setItem(...)对列表填充数据的。在setItem最后面加一个参数即可,CEGUIMultiColumnList::ListboxTextItem* setItem(...,bool isDigit = false)。

2.第30行的条件判断,在实际运行中,当某列的元素不为纯数字时,由于itemTextIsDigit和textIsDigit的默认值为true,会执行return itemTextValue < textValue,这并不是我预期的结果。修复这个问题的直观办法是将itemTextIsDigit和textIsDigit的默认值由true改为false,相应的第30行的条件判断也进行对应的修改。但是这样的改动之后,第9行和第11行中的for循环由原本判断某个字符串中有一个字符不为数字时跳出循环,变为遍历整个字符串每个字符都是数字的时候才能结束循环,不存在提前跳出循环的可能性。这样进行的额外循环次数多了必然会影响效率,CEGUI底层的改动,效率还是很重要的。再往下看,看到了30行先处理数字后处理字符串,其实只要将两者的位置进行调整即可,毕竟列表默认处理方式就是针对字符串的,将默认处理置于最前面也符合设计需要。

改良后的ListboxTextItem::operator<():

bool ListboxTextItem::operator<(const ListboxTextItem& rhs) const
{
      CEGUI::String text = ((ListboxTextItem*)&rhs)->getText();
      bool itemTextIsDigit = true;
      bool textIsDigit        = true;
      int    itemTextValue  = 0;
      int    textValue         = 0;

      if (!d_bIsDigit)
      {
           return d_itemText < text;
       }
       else
      {
          for (size_t i = 0; i < d_itemText.size(); ++i)
          {
              if(!isdigit(d_itemText[i]))
              {
                  itemTextIsDigit = false;
                  bresk;
              }
         }
 
          for (size_t j = 0; j < text.size(); ++j)
          {
              if(!isdigit(text[j]))
              {
                  textIsDigit = false;
                  break;
              }
          }
         itemTextValue = CEGUI::PropertyHelper::stringToInt(d_itemText);
      textValue = CEGUI::PropertyHelper::stringtoInt(text);

          if (!itemTextIsDigit || !textIsDigit)
          {
              return d_itemText < text;
          }
          else
          {
              return itemTextValue < textValue;
          }
     }
  

  
      return false;
 }

总结一下,CEGUIMultiColumnList没有实现按照数值排序,应该是出于通用性和效率的考虑。上述文章是按照我查找这个问题,解决这个问题的过程一步步写下来的,难免有啰嗦之处。这种解决方案也只是初步的想法,实现上和效率上还有欠缺,有了新的方法也会拿出来跟大家分享,也希望有想法的各位提出想法交流一下。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值