如何避免UI界面中下拉框ComboBox文本项的硬编码?
2015-12-05 23:29
399 查看
在桌面软件的UI界面开发中,经常会用到下拉框ComboBox控件,用来选择多个选项中的一项,程序员在编码的时候,经常这样做:
然后在需要响应用户操作的函数中通过comboBox的当前索引或当前文本内容判断应该执行何种操作:
这是一种明显的硬编码方式,如果想要修改某个文本项的名称或者调整文本项的顺序,都需要修改不止一个地方。
因此,通常我们会创建一个类型值(枚举值或其它常量类型,这个类型或许本就是已经存在的),然后建立类型值与文本项之间的映射关系。
然后前面的代码可以写成:
于是响应函数的代码我们可以写成这样了:
现在,我们消除了硬编码,如果要修改某个文本项的名称,我们只需要修改一处。
但是,由于QMap是自动排序的,所以下拉框的文本项的顺序是根据类型FileType从小到大排序的。如果我们想调整文本项的顺序,例如,把二进制文件放到第一个,恐怕就得去修改这个FileType的定义。问题是,有些情况下,这个类型定义可能是第三方库或者底层库的定义,不方便或不能修改。怎么办?
我们可以用QList来代替QMap达到这个目的。当然,这样的话我们得定义一个类型:
于是
这里的变量名称依然用m_valueNameMap问题到不大,反正我觉得m_valueNames并不贴切。由于QList不具备查询能力,所以每次得到文本项对应的类型值时需要遍历m_valueNameMap,这就需要新增加一个函数:
有时候可能还会需要根据类型查找文本项的函数。我们看到这些函数只用到了m_valueNameMap一个变量。且这些函数的增多会导致m_valueNameMap所在的类越来越大。于是我们可以利用“封装基本数据类型”的手法将其重构至一个单独的类QEnumValueNameMapper。
调用端代码:
这种方式消除了硬编码,让修改维护变得简单灵活。
[code] ui->comboBox->addItems(QStringList()<<tr("Text文件")<<tr("Word文件")<<tr("Excel表格")<<tr("二进制文件")<<tr("PDF文件"));
然后在需要响应用户操作的函数中通过comboBox的当前索引或当前文本内容判断应该执行何种操作:
[code] if (ui->comboBox->currentIndex() == 0) // if (ui->comboBox->currentText() == tr("Text文件")) { // 执行某种操作(Text文件) } // ...
这是一种明显的硬编码方式,如果想要修改某个文本项的名称或者调整文本项的顺序,都需要修改不止一个地方。
因此,通常我们会创建一个类型值(枚举值或其它常量类型,这个类型或许本就是已经存在的),然后建立类型值与文本项之间的映射关系。
[code]QMap<int, QString> m_valueNameMap;
然后前面的代码可以写成:
[code] m_valueNameMap[FileType::Text] = tr("Text文件"); m_valueNameMap[FileType::Word] = tr("Word文件"); m_valueNameMap[FileType::Excel] = tr("Excel表格"); m_valueNameMap[FileType::Binary] = tr("二进制文件"); m_valueNameMap[FileType::PDF] = tr("Word文件"); ui->comboBox->addItems(m_valueNameMap.values());
于是响应函数的代码我们可以写成这样了:
[code] if (m_valueNameMap.key(ui->comboBox->currentText()) == FileType::Text) { // 执行某种操作(Text文件) } // ...
现在,我们消除了硬编码,如果要修改某个文本项的名称,我们只需要修改一处。
但是,由于QMap是自动排序的,所以下拉框的文本项的顺序是根据类型FileType从小到大排序的。如果我们想调整文本项的顺序,例如,把二进制文件放到第一个,恐怕就得去修改这个FileType的定义。问题是,有些情况下,这个类型定义可能是第三方库或者底层库的定义,不方便或不能修改。怎么办?
我们可以用QList来代替QMap达到这个目的。当然,这样的话我们得定义一个类型:
[code]struct ValueName { int enumValue; QString uiName; };
于是
[code]QList<ValueName> m_valueNameMap;
这里的变量名称依然用m_valueNameMap问题到不大,反正我觉得m_valueNames并不贴切。由于QList不具备查询能力,所以每次得到文本项对应的类型值时需要遍历m_valueNameMap,这就需要新增加一个函数:
[code]int enumValue(const QString &uiname) const { foreach (const ValueName &var, m_valueNameMap) { if (var.uiName == uiname) { return var.enumValue; } } qWarning()<<"error! ui-name not exist! ui-name="<<uiname; return -9999; }
有时候可能还会需要根据类型查找文本项的函数。我们看到这些函数只用到了m_valueNameMap一个变量。且这些函数的增多会导致m_valueNameMap所在的类越来越大。于是我们可以利用“封装基本数据类型”的手法将其重构至一个单独的类QEnumValueNameMapper。
[code]#ifndef QENUMVALUENAMEMAPPER_H #define QENUMVALUENAMEMAPPER_H #include <QStringList> #include <QScopedPointer> class QEnumValueNameMapper { public: QEnumValueNameMapper(); ~QEnumValueNameMapper(); void appendValueName(int enumvalue, const QString &uiname); QStringList uiNames() const; QString uiName(int enumvalue, bool *ok = 0) const; int enumValue(const QString &uiname, bool *ok = 0) const; private: struct PrivateData; PrivateData *d_ptr; Q_DISABLE_COPY(QEnumValueNameMapper) }; #endif // QENUMVALUENAMEMAPPER_H
[code]#include "qenumvaluenamemapper.h" #include <QList> #include <QDebug> struct ValueName { ValueName() {} ValueName(int value, const QString &name) : enumValue(value), uiName(name) {} int enumValue; QString uiName; }; struct QEnumValueNameMapper::PrivateData { // 这里之所以使用QList而不使用QMap, 是因为: //1)QList不会自动排序,保证了用户append的原有顺序,这对于用户而言也许是重要的! //2)考虑到大部分情况下,枚举值的个数不会太多(<1000),遍历QList的查找效率不会成为问题! QList<ValueName> valueNamePairList; }; QEnumValueNameMapper::QEnumValueNameMapper() : d_ptr(new PrivateData()) { } QEnumValueNameMapper::~QEnumValueNameMapper() { delete d_ptr; d_ptr = 0; } void QEnumValueNameMapper::appendValueName(int enumvalue, const QString &uiname) { d_ptr->valueNamePairList.append(ValueName(enumvalue, uiname)); } QStringList QEnumValueNameMapper::uiNames() const { QStringList names; foreach (const ValueName &var, d_ptr->valueNamePairList) { names.append(var.uiName); } return names; } QString QEnumValueNameMapper::uiName(int enumvalue, bool *ok) const { foreach (const ValueName &var, d_ptr->valueNamePairList) { if (var.enumValue == enumvalue) { if (ok) *ok = true; return var.uiName; } } qWarning()<<"error! enum-value not exist! enum-value="<<enumvalue; if (ok) *ok = false; return QString::null; } int QEnumValueNameMapper::enumValue(const QString &uiname, bool *ok) const { foreach (const ValueName &var, d_ptr->valueNamePairList) { if (var.uiName == uiname) { if (ok) *ok = true; return var.enumValue; } } qWarning()<<"error! ui-name not exist! ui-name="<<uiname; if (ok) *ok = false; return -9999; }
调用端代码:
[code]QEnumValueNameMapper m_valueNameMap;
[code] m_valueNameMap.appendValueName(FileType::Text, tr("Text文件")); m_valueNameMap.appendValueName(FileType::Word, tr("Word文件")); .appendValueName(FileType::Excel, tr("Excel表格")); m_valueNameMap.appendValueName(FileType::Binary, tr("二进制文件")); m_valueNameMap.appendValueName(FileType::PDF, tr("Word文件")); ui->comboBox->addItems(m_valueNameMap.uiNames());
[code] if (m_valueNameMap.enumValue(ui->comboBox->currentText()) == FileType::Text) { // 执行某种操作(Text文件) } // ...
这种方式消除了硬编码,让修改维护变得简单灵活。
相关文章推荐
- 基于swing的UI原型辅助设计
- 免费在线文字处理软件XDocBuilder发布7.4.0版本
- 如何通过UIView对象获取该对象所属的UIViewController
- 【Java】StudentsInfoQuery(简单的学生信息查询系统)
- UILable / UITextField / UIButton
- UIView / UIView的布局
- UIVIew之霓虹灯实现
- UILabel、UIImageView、UIButton的使用
- Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 10
- leetcode Implement Stack using Queues
- UIButton 解析
- zoj 3349 Special Subsequence 【离散化二分 + 线段树优化dp】
- Android中startActivity中的permission检测与UID机制
- [Android--UI]多屏幕设计
- MySQL报错“1366 - Incorrect integer value: '' XXXXXXX' at row 1 ”
- [转载]百度编辑器-Ueditor使用
- JavaFX入门(五):使用CSS样式美化你的UI控件
- 使用Systrace分析UI性能
- Android API Guides 笔记
- Shiro+MySQL+Druid数据源验证小记