您的位置:首页 > 产品设计 > UI/UE

如何避免UI界面中下拉框ComboBox文本项的硬编码?

2015-12-05 23:29 399 查看
在桌面软件的UI界面开发中,经常会用到下拉框ComboBox控件,用来选择多个选项中的一项,程序员在编码的时候,经常这样做:

[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文件)
   }
   // ...


这种方式消除了硬编码,让修改维护变得简单灵活。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: