我有一个基本的4x20字符液晶显示器,我想使用它来显示带有按钮的菜单,使用Arduino作为驱动程序(有限的标准库支持)。
示例LCD菜单和按钮
我正在考虑生成一个接口类GraphicObject,然后所有图形对象都继承它(如Button、MenuItem等)。GraphicObject类应该有一个可以重写的绘制方法,这似乎是合乎逻辑的。
目前,我有一个名为Lcd的类,涵盖文字和字符定位的低层次绘图。为了绘制任何东西,我将需要访问这些Lcd对象之一。如果我在我的GraphicObject或派生对象中包含一个指向Lcd对象的指针,我就将它们耦合起来,并使它们仅为Lcd对象。如果我改变显示设备的类型,这些类就不再合适了。
如何组织类以保持它们松散耦合,并允许稍后更改显示类型?应该让LCDButton LCDMenuItem继承按钮和MenuItem接口,然后为另一个显示设备(OtherDisplayButton OtherDisplayMenuItem)创建其他对象吗?
有建议阅读吗?我看过许多例子,但似乎没有一个例子详细介绍了绘制方法的功能,以及设备是应该直接访问还是通过另一个控制对象访问。
谢谢
编辑1
简要代码思想概述
#include "Arduino.h"
class Lcd {
public:
struct Parameters {
uint_fast8_t numberLines;
uint_fast8_t numberCharacters;
// uint_fast8_t* lineOffsets;
Print* serial; // Can be any Serial device (all inherit from Print).
};
protected:
Parameters parameters_;
const uint_fast8_t escapeCode_ = 0x1B;
const uint_fast8_t directCode_ = 0xFE;
void sendCommand_(uint_fast8_t command, uint_fast8_t parameter = 0);
void sendCommandDirect_(uint_fast8_t command);
public:
Lcd(Parameters parameters);
void clearDisplay(void);
void moveCursor(uint_fast8_t line, uint_fast8_t character);
void resetDisplay(void);
void customCharacter(const uint_fast8_t address,
const uint_fast8_t characterMap[8]);
void write(uint8_t character);
// Boilerplate print forwarders.
void print(const char character);
void print(const String &string);
void print(const char string[]);
// Boilerplate println forwarders.
void println(const char character);
void println(const String &string);
void println(const char string[]);
void println(void);
};
class GraphicObject {
virtual void draw(void)=0;
};
class Button: public GraphicObject {
public:
typedef void (*buttonAction)(void);
virtual void setText(const String text)=0;
virtual const String getText() =0;
virtual bool isActive()=0;
virtual void setActive(bool)=0;
virtual void setAction(buttonAction action)=0;
};
class MenuItem: public Button {
public:
typedef void (*menuAction)(void);
virtual MenuItem* parentItem()=0;
virtual const MenuItem* addItem(String text, menuAction action)=0;
};
class VScrollbar: public GraphicObject {
public:
virtual void setAtTop(bool atTop);
virtual void setAtBottom(bool atBottom);
};
class LcdButton: public Button {
private:
Lcd* lcd_;
String text_;bool active_;
public:
LcdButton(Lcd* lcd);
void draw(void);
void setText(String text);
const String getText();bool isActive();
void setActive(bool);
void setAction(Button::buttonAction action);
};
class LcdWindow: public GraphicObject {
private:
LcdButton* lcdButtons_ = nullptr;
public:
enum class Position
: uint_fast8_t {
LEFT,
RIGHT
};
bool addButton(LcdButton* lcdButton, uint_fast8_t line, uint_fast8_t offset);
bool addVScrollbar(VScrollbar* vScrollbar, Position position);
void draw();
};
int main(void) {
Lcd::Parameters lcdParameters;
lcdParameters.numberCharacters = 20;
lcdParameters.numberLines = 4;
lcdParameters.serial = &Serial1;
Lcd lcd = Lcd(lcdParameters);
LcdButton myButton(&lcd);
myButton.setText("Select");
myButton.setActive(true);
LcdWindow lcdWindow;
lcdWindow.addButton(&myButton, 1, 1);
lcdWindow.draw();
while (1){}
return 0;
}发布于 2016-02-08 12:37:00
有不同的方法可以做到这一点。原则上,您应该定义一个接口( api )到您的低级别LCD驱动模块,并调用您的低级api的函数来绘制一些东西。然后,这个api的实现可以交换,而不需要更改高级代码。
最简单的方法是定义一个抽象的c++基类,其中所有的lcd驱动程序实现都必须从该基类派生。基类应该具有需要由派生实现重载的虚拟方法。
但是有一点信息: c++类中的虚拟方法要求编译器为对象实例化时创建的每个对象生成一个方法指针表;这需要更多的内存。此外,对这些类的对象的所有函数调用都是间接的(编译器生成代码,首先查找真正的函数指针,然后使用这个指针调用函数),这使得生成的代码稍微慢一些。
https://stackoverflow.com/questions/35268010
复制相似问题