国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站域名個(gè)人備案查詢推廣平臺排行榜app

網(wǎng)站域名個(gè)人備案查詢,推廣平臺排行榜app,一級注冊消防工程師報(bào)考條件,百度seo優(yōu)化價(jià)格目錄 面對對象C的程序設(shè)計(jì)(范例) 面對對象C的程序設(shè)計(jì)(應(yīng)用) 進(jìn)一步談?wù)撐疑厦娼o出的代碼——繼承 實(shí)現(xiàn)一個(gè)面對對象的文本編輯器 所以,什么是繼承 重申我們對菜單的抽象 抽象菜單項(xiàng)目 抽象菜單動(dòng)畫 實(shí)現(xiàn)菜單功…

目錄

面對對象C的程序設(shè)計(jì)(范例)

面對對象C的程序設(shè)計(jì)(應(yīng)用)

進(jìn)一步談?wù)撐疑厦娼o出的代碼——繼承

實(shí)現(xiàn)一個(gè)面對對象的文本編輯器

所以,什么是繼承

重申我們對菜單的抽象

抽象菜單項(xiàng)目

抽象菜單動(dòng)畫

實(shí)現(xiàn)菜單功能

初始化我們的菜單

關(guān)于我們的圖標(biāo)設(shè)置,顯示和隱藏

菜單本體功能

關(guān)于切換focus的菜單和進(jìn)入父子菜單的函數(shù)

完整的測試文件


終于,我們來到了這個(gè)令人激動(dòng)的部分了,現(xiàn)在,我們終于把所有的基礎(chǔ)工作做好了,就差我們的動(dòng)態(tài)組件了。

面對對象C的程序設(shè)計(jì)(范例)

我想,你可能使用過C++這門語言,他派生于C,但是最終的慣用編程范式又遠(yuǎn)遠(yuǎn)不同于C。盡管如此,C仍然可以按照一個(gè)相對變扭的方式完成面對對象的程序設(shè)計(jì)。這是因?yàn)樵贑語言本質(zhì)上是過程化語言,沒有直接的類和對象概念,因此面向?qū)ο笤O(shè)計(jì)需要通過結(jié)構(gòu)體、函數(shù)指針等手段模擬實(shí)現(xiàn)。

面對對象,首先講究的就是把所有的目標(biāo)看成對象。舉個(gè)例子,現(xiàn)在我們來看看動(dòng)態(tài)多級菜單這個(gè)東西,按照面對對象的設(shè)計(jì)思路。我們說面對對象它通過抽象和封裝將數(shù)據(jù)與功能結(jié)合,形成具有特定屬性和行為的對象。

typedef struct {int x;int y;void (*move)(struct Point*, int, int);
} Point;
?
void movePoint(Point* p, int dx, int dy) {p->x += dx;p->y += dy;
}
?
int main() {Point p = {0, 0, movePoint};p.move(&p, 5, 3);return 0;
}

這個(gè)就是一個(gè)將點(diǎn)看作對象的例子。

我們設(shè)計(jì)對象的時(shí)候,要思考對象能干什么,進(jìn)一步的,才需要知道他需要有什么。這種方式可以輔助一個(gè)習(xí)慣于面對過程的朋友設(shè)計(jì)一個(gè)對象。

面對對象C的程序設(shè)計(jì)(應(yīng)用)

我們現(xiàn)在把上面談到的用一下。

  • 他能顯示多級的文字菜單

  • 他能將目前選中的文本區(qū)域進(jìn)行反色

  • 他能再切換選中文本的時(shí)候演示一個(gè)阻塞的動(dòng)畫(提示,筆者的框架沒有做異步,這需要牽扯到中斷,筆者不打算現(xiàn)在做)

  • 如果一個(gè)子項(xiàng)存在子菜單,他能顯示出來這個(gè)子菜單,然后還能返回去(怎么樣觸發(fā)進(jìn)入和返回不是我們關(guān)心的,他能!

  • 他可以顯示和隱藏我們的icon,為此,我們還需要注冊接口。

為了做到上面的事情,我們要想他要擁有什么。

  • 一個(gè)簡略的文本編輯器,他能展示文字,我們菜單的文本繪制基本上依賴于這個(gè)文本編輯器

  • 一個(gè)負(fù)責(zé)動(dòng)畫演示的結(jié)構(gòu)體(對象),他能完成我們對“他能再切換選中文本的時(shí)候演示一個(gè)阻塞的動(dòng)畫”這個(gè)任務(wù)

  • 一個(gè)菜單子項(xiàng)結(jié)構(gòu)體數(shù)組,他維護(hù)了當(dāng)前這個(gè)菜單子項(xiàng)的文本顯示,是否有子菜單,甚至,還需要有callback行為的結(jié)構(gòu)體數(shù)組(這個(gè)是額外任務(wù),筆者沒有做callback)

typedef void* CCgraphicWidgetBase;
?
/* update requist functions */
typedef void(*Update)(CCgraphicWidgetBase);
typedef void(*Hide)(CCgraphicWidgetBase);
typedef void(*Show)(CCgraphicWidgetBase);
?
?
typedef struct{Update ? ?  update;Hide ? ? ?  hide;Show ? ? ?  show;
}CCGraphicWidgetCommonOperation;
?
typedef struct
{CCGraphicWidgetCommonOperation  common;void (*switchToIndex)(CCGraphic_Menu*, uint8_t index);void (*enabled_showAnimations)(CCGraphic_Menu*, uint8_t enabled);void (*setIcon)(CCGraphic_Menu*, CCGraphic_Image* image, uint8_t size);void (*showIcon)(CCGraphic_Menu*);void (*hideIcon)(CCGraphic_Menu*);CCGraphic_Menu* (*enterSub)(CCGraphic_Menu*);CCGraphic_Menu* (*backParent)(CCGraphic_Menu*);
}CCGraphic_MenuOperations;
?
typedef struct __CCGraphic_Menu{// 菜單項(xiàng)數(shù)組CCGraphic_MenuItem* ? ? ? ? menuItemArrays;// 菜單項(xiàng)數(shù)組個(gè)數(shù)uint8_t ? ? ? ? ? ? ? ? ? ? menuArraySize;// 內(nèi)部主控件CCGraphicTextEdit* ? ? ? ?  internelTextEdit;// 動(dòng)畫負(fù)責(zé)的結(jié)構(gòu)體CCGraphic_MenuAnimations* ? animation_holder;// 操作CCGraphic_MenuOperations ?  operations;// 當(dāng)前維護(hù)的其他信息uint8_t ? ? ? ? ? ? ? ? ? ? current_offset;uint8_t ? ? ? ? ? ? ? ? ? ? enabled_animations;CCGraphic_Image* ? ? ? ? ?  icons_sources;uint8_t ? ? ? ? ? ? ? ? ? ? icon_size;uint8_t ? ? ? ? ? ? ? ? ? ? icon_state;
}CCGraphic_Menu;

任務(wù):你可以改進(jìn)這個(gè)抽象!你可以看到零碎一地的變量成員不太美觀!

進(jìn)一步談?wù)撐疑厦娼o出的代碼——繼承

讓我們進(jìn)一步討論更多的概念,上面的代碼出現(xiàn)了一個(gè)很有意思的片段

typedef void* CCgraphicWidgetBase;
?
/* update requist functions */
typedef void(*Update)(CCgraphicWidgetBase);
typedef void(*Hide)(CCgraphicWidgetBase);
typedef void(*Show)(CCgraphicWidgetBase);
?
?
typedef struct{Update ? ?  update;Hide ? ? ?  hide;Show ? ? ?  show;
}CCGraphicWidgetCommonOperation;
?
typedef struct
{CCGraphicWidgetCommonOperation  common;void (*switchToIndex)(CCGraphic_Menu*, uint8_t index);void (*enabled_showAnimations)(CCGraphic_Menu*, uint8_t enabled);void (*setIcon)(CCGraphic_Menu*, CCGraphic_Image* image, uint8_t size);void (*showIcon)(CCGraphic_Menu*);void (*hideIcon)(CCGraphic_Menu*);CCGraphic_Menu* (*enterSub)(CCGraphic_Menu*);CCGraphic_Menu* (*backParent)(CCGraphic_Menu*);
}CCGraphic_MenuOperations;

仔細(xì)研究一下,你會(huì)發(fā)現(xiàn),我們似乎復(fù)用了一個(gè)結(jié)構(gòu)體:CCGraphicWidgetCommonOperation,也就是組件Widget的通用操作。為了理解這個(gè)特征,我們先不著急,實(shí)現(xiàn)一個(gè)完全面對對象的,一個(gè)簡單的文本編輯器

實(shí)現(xiàn)一個(gè)面對對象的文本編輯器

#ifndef CCGraphic_TextEdit_H
#define CCGraphic_TextEdit_H
#include "Graphic/widgets/common/CCGraphic_WidgetBase.h"
#include "Graphic/base/CCGraphic_Point/CCGraphic_Point.h"
#include "Graphic/widgets/common/CCGraphic_Size/CCGraphic_Size.h"
#include "Graphic/widgets/common/CCGraphic_WidgetBase.h"
#include "Graphic/widgets/base/CCGraphic_TextItem/CCGraphic_TextItem.h"
#include "Graphic/widgets/base/CCGraphic_TextItem/CCGraphic_TextConfig.h"
typedef struct __CCGraphicTextEdit CCGraphicTextEdit; ?
// 前向聲明:定義一個(gè)名為 `CCGraphicTextEdit` 的結(jié)構(gòu)體類型。 ?
?
typedef struct { ?CCGraphicWidgetCommonOperation operation; ?// 控件通用操作,提供基本控件功能。 ?
?void (*appendText)(CCGraphicTextEdit*, char* appendee); ?// 函數(shù)指針:向文本控件追加文本。 ?
?void (*setText)(CCGraphicTextEdit*, char* text); ?// 函數(shù)指針:設(shè)置控件內(nèi)的完整文本內(nèi)容。 ?
?void (*newLineText)(CCGraphicTextEdit*, char* text); ?// 函數(shù)指針:在控件中新起一行并寫入文本。 ?
?void (*clear)(CCGraphicTextEdit*); ?// 函數(shù)指針:清空控件中的文本。 ?
?void (*relocate)(CCGraphicTextEdit*, CCGraphic_Point p, CCGraphic_Size size); ?// 函數(shù)指針:重新定位控件位置并調(diào)整控件尺寸。 ?
?
} CCGraphicTextEdit_SupportiveOperations; ?
// 結(jié)構(gòu)體 `CCGraphicTextEdit_SupportiveOperations`:定義文本控件支持的操作集合。 ?
?
typedef struct __CCGraphicTextEdit { ?uint8_t acquired_stepped_update; ?// 標(biāo)記是否啟用分步更新機(jī)制的標(biāo)志變量。 ?
?CCDeviceHandler* borrowed_device; ?// 設(shè)備處理器指針,用于管理外部設(shè)備資源。 ?
?CCGraphicTextEdit_SupportiveOperations operations; ?// 文本控件支持操作的集合。 ?
?CCGraphic_AsciiTextItem* handle; ?// 控件中的具體文本項(xiàng)句柄,用于操作字符顯示內(nèi)容。 ?
?
} CCGraphicTextEdit; ?
// 結(jié)構(gòu)體 `CCGraphicTextEdit`:定義文本控件的屬性與操作。 ?
?
void CCGraphic_init_CCGraphicTextEdit( ?CCGraphicTextEdit* text_self, ?CCDeviceHandler* handler, ?CCGraphic_AsciiTextItem* inited ?
); ?
// 函數(shù)聲明:初始化 `CCGraphicTextEdit` 控件,傳入控件對象、設(shè)備處理器和已初始化的文本項(xiàng)。 ?
#endif

你可能會(huì)問,怎么看起來這么奇怪,我們應(yīng)該如何調(diào)用功能呢?你看,這就是思維沒有轉(zhuǎn)變過來,筆者想要說的是,現(xiàn)在功能被集成進(jìn)入了結(jié)構(gòu)體,意味著,我們想要調(diào)用的不叫函數(shù)了,是一個(gè)結(jié)構(gòu)體的方法。

static void __helper_on_set_text(CCGraphicTextEdit* edit, char* sources, uint32_t shown_time)
{edit->operations.setText(edit, sources);HAL_Delay(shown_time * 1000);edit->operations.clear(edit);
}

看到了嗎?當(dāng)我們想要設(shè)置文本的時(shí)候,不是

CCGraphicTextEdit_setText(edit, sources);

而是

edit->operations.setText(edit, sources);

看起來好像沒什么區(qū)別,我想說的是,你現(xiàn)在不知道,也沒法去引用一個(gè)函數(shù),叫“給一個(gè)是CCGraphicTextEdit的結(jié)構(gòu)體設(shè)置文本”的函數(shù),你找不到,我藏起來了(笑),而是,一個(gè)屬于CCGraphicTextEdit這個(gè)類的對象可以被設(shè)置文本,文本是sources,這就是面對對象的設(shè)計(jì)思考范式。換而言之,一個(gè)CCGraphicTextEdit的對象可以設(shè)置文本,他能設(shè)置文本而且優(yōu)先的投射到繪圖設(shè)備上,而你完全不知道底下發(fā)生了什么,只知道這樣做一定沒有問題!

在源文件中,我們才將如何實(shí)現(xiàn)暴露出來

#include "Graphic/widgets/components/CCGraphic_TextEdit/CCGraphic_TextEdit.h"
#include "Graphic/widgets/base/CCGraphic_TextItem/CCGraphic_TextItem.h"
#include "Graphic/CCGraphic_device_adapter.h"
?
static void __pvt_update_text(CCGraphicTextEdit* text_self) ?
// 靜態(tài)函數(shù):更新控件所依賴的設(shè)備內(nèi)容。 ?
{text_self->borrowed_device->operations.update_device_function(text_self->borrowed_device ?// 調(diào)用設(shè)備的更新函數(shù),使文本控件的內(nèi)容刷新顯示。 ?);
}
?
static void __pvt_show(CCGraphicTextEdit* text_self) ?
// 靜態(tài)函數(shù):繪制并顯示文本控件內(nèi)容。 ?
{CCGraphicWidget_drawAsciiTextItem(text_self->borrowed_device, text_self->handle ?// 繪制文本控件的內(nèi)容。 ?);if(text_self->acquired_stepped_update) ?// 如果啟用了分步更新,則執(zhí)行設(shè)備更新。 ?__pvt_update_text(text_self);
}
?
static void __pvt_hide(CCGraphicTextEdit* text_self) ?
// 靜態(tài)函數(shù):隱藏控件,即清除其顯示區(qū)域。 ?
{text_self->borrowed_device->operations.clearArea_function(text_self->borrowed_device, text_self->handle->tl_point.x, ?text_self->handle->tl_point.y, ?text_self->handle->TexthandleSize.width, ?text_self->handle->TexthandleSize.height ?// 清除控件所在區(qū)域的內(nèi)容。 ?);__pvt_update_text(text_self);
}
?
static void __pvt_clear_text(CCGraphicTextEdit* text_self) ?
// 靜態(tài)函數(shù):清除控件中的文本內(nèi)容。 ?
{CCGraphic_Point tl = text_self->handle->tl_point; ?CCGraphic_Size size = text_self->handle->TexthandleSize; ?// 獲取控件左上角坐標(biāo)和尺寸,用于清除操作。 ?
?text_self->borrowed_device->operations.clearArea_function(text_self->borrowed_device, tl.x, tl.y, size.width, size.height ?// 執(zhí)行清除操作。 ?);__pvt_update_text(text_self);
}
?
static void __pvt_append_text(CCGraphicTextEdit* text_self, char* text) ?
// 靜態(tài)函數(shù):向控件追加文本。 ?
{CCGraphicWidget_AsciiTextItem_setAsciiText(text_self->handle, text); ?// 設(shè)置追加的文本內(nèi)容。 ?__pvt_show(text_self); ?// 顯示控件內(nèi)容。 ?
}
?
static void __pvt_newLine_text(CCGraphicTextEdit* text_self, char* text) ?
// 靜態(tài)函數(shù):在控件中新建一行并寫入文本。 ?
{CCGraphic_Point new_begin = ?CCGraphicWidget_AsciiTextItem_on_newLine_point(text_self->handle); ?// 獲取新行起始點(diǎn)坐標(biāo)。 ?
?CCGraphicWidget_AsciiTextItem_setAsciiText(text_self->handle, text); ?// 設(shè)置文本內(nèi)容。 ?
?CCGraphicWidget_AsciiTextItem_setIndexedPoint(text_self->handle, &new_begin); ?// 更新文本項(xiàng)的繪制位置。 ?
?__pvt_show(text_self); ?// 顯示控件內(nèi)容。 ?
}
?
static void __pvt_setText(CCGraphicTextEdit* text_self, char* text) ?
// 靜態(tài)函數(shù):設(shè)置控件的完整文本內(nèi)容。 ?
{__pvt_clear_text(text_self); ?// 清除原有內(nèi)容。 ?
?CCGraphicWidget_AsciiTextItem_setIndexedPoint(text_self->handle, &(text_self->handle->tl_point) ?// 重置文本繪制位置為控件起點(diǎn)。 ?);
?CCGraphicWidget_AsciiTextItem_setAsciiText(text_self->handle, text); ?// 設(shè)置新的文本內(nèi)容。 ?
?__pvt_show(text_self); ?// 顯示控件內(nèi)容。 ?
}
?
static void __pvt_relocate(CCGraphicTextEdit* edit, CCGraphic_Point p, CCGraphic_Size size) ?
// 靜態(tài)函數(shù):重新定位控件位置并調(diào)整尺寸。 ?
{__pvt_hide(edit); ?// 隱藏控件內(nèi)容。 ?
?CCGraphicWidget_AsciiTextItem_relocate(edit->handle, p, size); ?// 重新設(shè)置控件位置和尺寸。 ?
}
?
void CCGraphic_init_CCGraphicTextEdit( ?CCGraphicTextEdit* text_self, ?CCDeviceHandler* handler, ?CCGraphic_AsciiTextItem* inited ?
) ?
// 初始化函數(shù):設(shè)置文本編輯控件的初始狀態(tài)。 ?
{text_self->acquired_stepped_update = 0; ?// 初始化為未啟用分步更新。 ?
?text_self->borrowed_device = handler; ?// 綁定設(shè)備處理器。 ?
?text_self->handle = inited; ?// 設(shè)置文本項(xiàng)句柄。 ?
?text_self->operations.appendText = __pvt_append_text; ?text_self->operations.clear = __pvt_clear_text; ?text_self->operations.newLineText = __pvt_newLine_text; ?text_self->operations.setText = __pvt_setText; ?text_self->operations.relocate = __pvt_relocate; ?// 綁定控件的各類操作函數(shù)。 ?
?text_self->operations.operation.hide = (Hide)__pvt_hide; ?text_self->operations.operation.show = (Show)__pvt_show; ?text_self->operations.operation.update = (Update)__pvt_update_text; ?// 綁定通用控件操作。 ?
}

可以看到,代碼被分成了一層一層的,關(guān)心哪一個(gè)方法,只需要進(jìn)入對應(yīng)的方法查看即可。

所以,什么是繼承

繼承允許一個(gè)類從另一個(gè)類中獲取屬性和方法,從而實(shí)現(xiàn)代碼復(fù)用和邏輯擴(kuò)展。也就是說,我們認(rèn)為繼承表達(dá)了“一個(gè)對象是另一個(gè)對象”的陳述。比如說。

typedef struct { ?CCGraphicWidgetCommonOperation operation; ?// 控件通用操作,提供基本控件功能。 ?
?void (*appendText)(CCGraphicTextEdit*, char* appendee); ?// 函數(shù)指針:向文本控件追加文本。 ?
?void (*setText)(CCGraphicTextEdit*, char* text); ?// 函數(shù)指針:設(shè)置控件內(nèi)的完整文本內(nèi)容。 ?
?void (*newLineText)(CCGraphicTextEdit*, char* text); ?// 函數(shù)指針:在控件中新起一行并寫入文本。 ?
?void (*clear)(CCGraphicTextEdit*); ?// 函數(shù)指針:清空控件中的文本。 ?
?void (*relocate)(CCGraphicTextEdit*, CCGraphic_Point p, CCGraphic_Size size); ?// 函數(shù)指針:重新定位控件位置并調(diào)整控件尺寸。 ?
?
} CCGraphicTextEdit_SupportiveOperations; ?

表達(dá)了CCGraphicTextEdit的功能集合是CCGraphicWidget的一個(gè)超集,他不光擁有一個(gè)Widget該有的操作,而且,還有自己跟Widget獨(dú)有的操作,這就是繼承的力量——復(fù)用接口,甚至可以是實(shí)現(xiàn)!

重申我們對菜單的抽象

根據(jù)最之前的描述,菜單本身應(yīng)該是一個(gè)樹狀的結(jié)構(gòu),你可以看到:

抽象菜單項(xiàng)目

#ifndef CCGraphic_MenuItem_H
#define CCGraphic_MenuItem_H
#include "Graphic/CCGraphic_common.h"
/* This version we use simple menu Item */
?
/* announced the menu type for the further usage */ ?
// 預(yù)聲明 `CCGraphic_Menu` 類型,用于菜單關(guān)聯(lián)。 ?
?
typedef struct __CCGraphic_Menu CCGraphic_Menu; ?
// 結(jié)構(gòu)體 `CCGraphic_Menu` 的前向聲明,以便在結(jié)構(gòu)中使用指針引用該類型。 ?
?
#define NO_SUB_MENU (NULL) ?
// 定義宏 `NO_SUB_MENU` 表示沒有子菜單,為空指針。 ?
?
typedef struct __CCGraphic_MenuItem { ?char* text; ?// 菜單項(xiàng)顯示的文本內(nèi)容。 ?
?CCGraphic_Menu* subMenu; ?// 指向子菜單的指針,若無子菜單則為 `NO_SUB_MENU`。 ?
?CCGraphic_Menu* parentMenu; ?// 指向父菜單的指針,用于返回或?qū)蛹壙刂啤??
} CCGraphic_MenuItem; ?
// 定義菜單項(xiàng)結(jié)構(gòu)體 `CCGraphic_MenuItem`,包含菜單文字、子菜單及父菜單指針。 ?
?
void CCGraphic_MenuItem_register_menuItem( ?CCGraphic_MenuItem* item, ?// 菜單項(xiàng)指針,用于注冊菜單項(xiàng)。 ?
?CCGraphic_Menu* parentMenu, ?// 父菜單指針,將菜單項(xiàng)附加到此菜單下。 ?
?char* text, ?// 菜單項(xiàng)文本內(nèi)容。 ?
?CCGraphic_Menu* subMenu ?// 子菜單指針,可為 `NO_SUB_MENU`。 ?
); ?
// 函數(shù)聲明:將菜單項(xiàng)注冊到指定父菜單下,同時(shí)設(shè)置菜單項(xiàng)文本和子菜單。 ?
#endif

提示:需要做callback?(用戶明確選擇了這個(gè)菜單項(xiàng)目)試一下在CCGraphic_MenuItem中添加抽象!完成你的代碼!

抽象菜單動(dòng)畫

typedef struct __CCGraphic_MenuAnimations CCGraphic_MenuAnimations; ?
// 前向聲明 `CCGraphic_MenuAnimations` 結(jié)構(gòu)體,表示菜單動(dòng)畫的管理結(jié)構(gòu)。 ?
?
typedef void (*DoByStep)(CCGraphic_MenuAnimations*); ?
// 定義一個(gè)函數(shù)指針類型 `DoByStep`,指向以 `CCGraphic_MenuAnimations*` 為參數(shù)的函數(shù),
// 該函數(shù)用于執(zhí)行逐步動(dòng)畫操作。 ?
?
typedef struct { ?DoByStep doByStep; ?// 操作結(jié)構(gòu)體,包含逐步執(zhí)行動(dòng)畫的函數(shù)指針。 ?
} CCGraphic_MenuAnimationsOperations; ?
// 定義 `CCGraphic_MenuAnimationsOperations` 結(jié)構(gòu)體,封裝了逐步動(dòng)畫執(zhí)行的操作。 ?
?
/*this struct shouldn't be registered by programmersit shoule be registered by program, so no interface ispubliced!
*/ ?
// 該結(jié)構(gòu)體不應(yīng)由程序員手動(dòng)注冊,而是由程序自動(dòng)注冊,因此沒有提供公開接口。 ?
?
typedef struct __CCGraphic_MenuAnimations { ?/* animating rectangle */ ?// 定義菜單動(dòng)畫的結(jié)構(gòu)體。 ?
?CCDeviceHandler* handler; ?// 設(shè)備處理器,用于控制設(shè)備的操作。 ?
?CCGraphic_Point tl_point; ?// 動(dòng)畫的起始點(diǎn)(左上角坐標(biāo))。 ?
?CCGraphic_Size animationOffsetSize; ?// 動(dòng)畫的偏移尺寸,用于表示動(dòng)畫區(qū)域的大小。 ?
?int16_t x_step; ?// x軸每步移動(dòng)的像素值,用于控制動(dòng)畫的水平位移。 ?
?int16_t y_step; ?// y軸每步移動(dòng)的像素值,用于控制動(dòng)畫的垂直位移。 ?
?CCGraphic_MenuAnimationsOperations op; ?// 操作對象,包含執(zhí)行逐步動(dòng)畫的函數(shù)指針。 ?
?uint8_t is_doing; ?// 標(biāo)志位,表示動(dòng)畫是否正在進(jìn)行中。 ?
} CCGraphic_MenuAnimations; ?
// 定義菜單動(dòng)畫結(jié)構(gòu)體,封裝了動(dòng)畫的狀態(tài)、操作及設(shè)備控制。 ?

初始化一個(gè)動(dòng)畫的辦法是:

static void __pvt_init_animations( ?CCGraphic_Menu* ? ? ? ? ? ? menu, ?CCGraphic_MenuAnimations* ? animations ?
) { ?/* no animations are registered */ ?// 如果沒有提供動(dòng)畫對象,直接返回。 ?if (animations == NULL) { ?return; ?}
?// 獲取菜單中的文本編輯項(xiàng)句柄,進(jìn)行后續(xù)動(dòng)畫初始化。 ?CCGraphic_AsciiTextItem* internelTextEdit = menu->internelTextEdit->handle; ?
?/* calculate the animations holding size */ ?// 計(jì)算動(dòng)畫的大小,首先設(shè)置動(dòng)畫起始點(diǎn)為文本編輯項(xiàng)的起始點(diǎn)。 ?animations->tl_point = internelTextEdit->tl_point; ?
?// 設(shè)置動(dòng)畫的高度為字體的大小(通過 `__fetch_font_size` 獲取字體的高度)。 ?animations->animationOffsetSize.height = __fetch_font_size(internelTextEdit->font_size).height; ?
?// 設(shè)置動(dòng)畫的寬度為文本處理器的寬度。 ?animations->animationOffsetSize.width = internelTextEdit->TexthandleSize.width; ?
?// 設(shè)置設(shè)備處理器,使用菜單中的文本編輯項(xiàng)借用的設(shè)備。 ?animations->handler = menu->internelTextEdit->borrowed_device; ?
?// 設(shè)置每步的水平和垂直步長(默認(rèn)值)。 ?animations->x_step = _DEFAULT_X_STEP; ?animations->y_step = _DEFAULT_Y_STEP; ?
?// 設(shè)置執(zhí)行逐步動(dòng)畫操作的函數(shù)指針,指向 `__pvt_doByStep` 函數(shù)。 ?animations->op.doByStep = __pvt_doByStep; ?
?/* set state */ ?// 設(shè)置動(dòng)畫狀態(tài)為未開始。 ?animations->is_doing = 0; ?
} ?

對于逐步開始動(dòng)畫的辦法是

/* do by steps */
static void __pvt_doByStep(CCGraphic_MenuAnimations* animations) ?
{ ?// 如果動(dòng)畫尚未開始(is_doing 為 0),則直接返回,避免不必要的操作。 ?if (!animations->is_doing) return; ?
?// 使用設(shè)備的操作對象反轉(zhuǎn)(擦除)動(dòng)畫區(qū)域,傳入當(dāng)前動(dòng)畫的起始位置(tl_point)和尺寸。 ?animations->handler->operations.reverseArea_function( ?animations->handler, ?animations->tl_point.x, animations->tl_point.y, ?animations->animationOffsetSize.width, ?animations->animationOffsetSize.height ?); ?
?// 更新動(dòng)畫的起始點(diǎn)(左上角坐標(biāo)),按水平步長(x_step)和垂直步長(y_step)更新。 ?animations->tl_point.x += animations->x_step; ?animations->tl_point.y += animations->y_step; ?
?// 再次調(diào)用反轉(zhuǎn)(擦除)區(qū)域,傳入更新后的動(dòng)畫位置和尺寸。 ?animations->handler->operations.reverseArea_function( ?animations->handler, ?animations->tl_point.x, animations->tl_point.y, ?animations->animationOffsetSize.width, ?animations->animationOffsetSize.height ?); ?
?// 調(diào)用更新設(shè)備函數(shù),刷新屏幕以顯示動(dòng)畫效果。 ?animations->handler->operations.update_device_function( ?animations->handler ?); ?
} ?

看到了嗎,非必要不調(diào)用刷新設(shè)備的操作就在這里體現(xiàn)了。當(dāng)然,當(dāng)我們配置不需要?jiǎng)赢嫷臅r(shí)候

static void __pvt_do_as_immediate(CCGraphic_MenuAnimations* animations, CCGraphic_Point* ? ? ?  end_tpl)
{if(!animations->is_doing) return;animations->handler->operations.reverseArea_function(animations->handler, animations->tl_point.x, animations->tl_point.y,animations->animationOffsetSize.width, animations->animationOffsetSize.height);animations->tl_point = *end_tpl;animations->handler->operations.reverseArea_function(animations->handler, animations->tl_point.x, animations->tl_point.y,animations->animationOffsetSize.width, animations->animationOffsetSize.height);animations->handler->operations.update_device_function(animations->handler);
}

直接拉到最后就好了。

實(shí)現(xiàn)菜單功能

到了真正實(shí)現(xiàn)的時(shí)候,一切反而水到渠成。

初始化我們的菜單
void CCGraphic_init_Menu(CCGraphic_Menu* ? ? ? ? ? ? blank_menu,CCGraphic_MenuItem* ? ? ? ? menuItemArrays,uint8_t ? ? ? ? ? ? ? ? ? ? menuArraySize,CCGraphicTextEdit* ? ? ? ?  configured_menu,CCGraphic_MenuAnimations* ? blank_animations,uint8_t ? ? ? ? ? ? ? ? ? ? enabled_animations 
)
{blank_menu->internelTextEdit ?  = configured_menu;blank_menu->menuItemArrays ? ?  = menuItemArrays;blank_menu->menuArraySize ? ? ? = menuArraySize;blank_menu->animation_holder ?  = blank_animations;blank_menu->current_offset ? ?  = 0;blank_menu->icon_state ? ? ? ?  = 0;blank_menu->enabled_animations = enabled_animations;
?// map the functionsblank_menu->operations.common.hide ? ?  = (Hide)__pvt_hide_CCGraphic_Menu;blank_menu->operations.common.show ? ?  = (Show)__pvt_show_CCGraphic_Menu;blank_menu->operations.common.update ?  = (Update)__pvt_update;blank_menu->operations.switchToIndex ?  = __pvt_switchIndex;blank_menu->operations.enabled_showAnimations = __pvt_setAnimationShowState_wrapper;// iconsblank_menu->operations.setIcon = __pvt_setIcon;blank_menu->operations.hideIcon = __pvt_hideIcon;blank_menu->operations.showIcon = __pvt_showIcon;blank_menu->operations.enterSub = __pvt_enterSub;blank_menu->operations.backParent = __pvt_backParent;__pvt_init_animations(blank_menu, blank_animations);
}
關(guān)于我們的圖標(biāo)設(shè)置,顯示和隱藏
static void __pvt_setIcon(CCGraphic_Menu* menu, CCGraphic_Image* sources, uint8_t size) ?
{ ?// 設(shè)置菜單的圖標(biāo)源和圖標(biāo)數(shù)量 ?menu->icons_sources = sources; ?menu->icon_size = size; ?
?// 初始化每個(gè)圖標(biāo)的尺寸和位置 ?for (uint8_t i = 0; i < menu->icon_size; i++) { ?// 設(shè)置圖標(biāo)的高度和寬度 ?sources[i].image_size.height = ICON_HEIGHT; ?sources[i].image_size.width = ICON_WIDTH; ?
?// 設(shè)置每個(gè)圖標(biāo)的位置,`y` 方向依次排列 ?sources[i].point.x = 0; ?sources[i].point.y = i * ICON_HEIGHT; ?} ?
?// 顯示圖標(biāo) ?__pvt_showIcon(menu); ?
} ?
?
static void __pvt_showIcon(CCGraphic_Menu* menu) ?
{ ?// 如果沒有圖標(biāo)源,則不執(zhí)行任何操作 ?if (!menu->icons_sources) return; ?// 設(shè)置圖標(biāo)的狀態(tài)為顯示(1) ?menu->icon_state = 1; ?CCGraphic_Point tlp; ?CCGraphic_Size _size; ?// 獲取顯示圖標(biāo)的位置和大小 ?__pvt_providePoint(menu, &tlp, 1); ?__pvt_provideSize(menu, &_size, 1); ?// 設(shè)置動(dòng)畫的顯示狀態(tài)為 0(關(guān)閉動(dòng)畫) ?__pvt_setAnimationShowState(menu->animation_holder, 0); ?// 將菜單項(xiàng)的文本編輯區(qū)域重新定位到指定的位置和大小 ?menu->internelTextEdit->operations.relocate(menu->internelTextEdit, tlp, _size); ?// 遍歷圖標(biāo)源,逐一繪制每個(gè)圖標(biāo) ?for (uint8_t i = 0; i < menu->icon_size; i++) { ?CCGraphicWidget_draw_image( ?menu->internelTextEdit->borrowed_device, ?&menu->icons_sources[i] ?); ?} ?// 設(shè)置動(dòng)畫的顯示狀態(tài)為 1(啟用動(dòng)畫) ?__pvt_setAnimationShowState(menu->animation_holder, 1); ?// 僅顯示文本編輯器 ?__pvt_show_textEditOnly(menu); ?
} ?
?
static void __pvt_hideIcon(CCGraphic_Menu* menu) ?
{ ?// 如果沒有圖標(biāo)源,則不執(zhí)行任何操作 ?if (!menu->icons_sources) return; ?CCGraphic_Point tlp; ?CCGraphic_Size _size; ?// 設(shè)置圖標(biāo)的狀態(tài)為隱藏(0) ?menu->icon_state = 0; ?// 獲取隱藏圖標(biāo)的位置和大小 ?__pvt_providePoint(menu, &tlp, 0); ?__pvt_provideSize(menu, &_size, 0); ?// 設(shè)置動(dòng)畫的顯示狀態(tài)為 0(關(guān)閉動(dòng)畫) ?__pvt_setAnimationShowState(menu->animation_holder, 0); ?// 將菜單項(xiàng)的文本編輯區(qū)域重新定位到指定的位置和大小 ?menu->internelTextEdit->operations.relocate(menu->internelTextEdit, tlp, _size); ?// 清除圖標(biāo)區(qū)域 ?menu->internelTextEdit->borrowed_device->operations.clearArea_function( ?menu->internelTextEdit->borrowed_device, ?0, 0, ICON_WIDTH, ICON_HEIGHT * menu->icon_size ?); ?// 僅顯示文本編輯器 ?__pvt_show_textEditOnly(menu); ?
} ?

圖標(biāo)的繪制就是讓位子繪制,清除掉重新繪制這個(gè)思路。

菜單本體功能
static void __pvt_update(CCGraphic_Menu* menu)
{// 調(diào)用文本編輯器的更新操作,刷新菜單顯示menu->internelTextEdit->operations.operation.update(menu->internelTextEdit);
}
?
// 更新動(dòng)畫狀態(tài)
static void __pvt_setAnimationShowState(CCGraphic_MenuAnimations* animations, uint8_t is_doing)
{// 如果動(dòng)畫狀態(tài)沒有變化,直接返回if(is_doing == animations->is_doing){return;}// 如果動(dòng)畫正在進(jìn)行,先逆向繪制區(qū)域,清除之前的顯示animations->handler->operations.reverseArea_function(animations->handler, animations->tl_point.x, animations->tl_point.y,animations->animationOffsetSize.width, animations->animationOffsetSize.height);// 更新動(dòng)畫狀態(tài)animations->is_doing = is_doing;// 更新設(shè)備,刷新顯示animations->handler->operations.update_device_function(animations->handler);
}
?
/*以下是顯示/隱藏圖標(biāo)時(shí),提供布局計(jì)算的函數(shù)
*/
static void __pvt_providePoint(CCGraphic_Menu* menu, CCGraphic_Point* p, uint8_t icons_enabled)
{// 根據(jù)是否啟用圖標(biāo),設(shè)置圖標(biāo)顯示的起始位置p->x = icons_enabled ? ICON_WIDTH : 0;p->y = 0;
}
?
static void __pvt_provideSize(CCGraphic_Menu* menu, CCGraphic_Size* size, uint8_t icons_enabled
){// 根據(jù)是否啟用圖標(biāo),調(diào)整文本區(qū)域的寬度和高度size->width = menu->internelTextEdit->handle->TexthandleSize.width - (icons_enabled ? ICON_HEIGHT : 0);size->height = menu->internelTextEdit->handle->TexthandleSize.height;
}
?
// 獲取當(dāng)前菜單項(xiàng)是否有子菜單
static inline CCGraphic_Menu* __pvt_current_owns_subMenu(CCGraphic_Menu* menu)
{return  menu->menuItemArrays[menu->current_offset].subMenu;
}
?
// 獲取當(dāng)前菜單項(xiàng)的父菜單
static inline CCGraphic_Menu* __pvt_owns_parent_current(CCGraphic_Menu* menu)
{return  menu->menuItemArrays[menu->current_offset].parentMenu;
}
?
// 僅顯示文本編輯器的內(nèi)容,更新菜單顯示
void __pvt_show_textEditOnly(CCGraphic_Menu* menu)
{// 如果菜單沒有項(xiàng),則直接返回if(menu->menuArraySize == 0){return;}// 設(shè)置動(dòng)畫狀態(tài)為不顯示__pvt_setAnimationShowState(menu->animation_holder, 0);// 設(shè)置文本編輯器的內(nèi)容,顯示第一項(xiàng)菜單CCGraphicTextEdit* edit = menu->internelTextEdit;edit->operations.setText(edit, menu->menuItemArrays[0].text);// 顯示后續(xù)菜單項(xiàng)for(uint8_t i = 1; i < menu->menuArraySize; i++){edit->operations.newLineText(edit, menu->menuItemArrays[i].text);}// 設(shè)置動(dòng)畫狀態(tài)為顯示__pvt_setAnimationShowState(menu->animation_holder, 1); ? ?
}
?
// 隱藏菜單和圖標(biāo)
void __pvt_hide_CCGraphic_Menu(CCGraphic_Menu* menu)
{// 隱藏圖標(biāo)__pvt_hideIcon(menu);// 隱藏文本編輯器menu->internelTextEdit->operations.operation.hide(menu->internelTextEdit);// 獲取動(dòng)畫控制器CCGraphic_MenuAnimations* animation = menu->animation_holder;// 如果沒有動(dòng)畫控制器,則返回if(!animation) return;// 如果動(dòng)畫正在進(jìn)行,則停止動(dòng)畫if(animation->is_doing){__pvt_setAnimationShowState(animation, 0);}
}
?
/* 繪制菜單顯示 */
void __pvt_show_CCGraphic_Menu(CCGraphic_Menu* menu)
{// 僅顯示文本編輯器內(nèi)容__pvt_show_textEditOnly(menu); ? 
}
?
// 執(zhí)行動(dòng)畫,逐步更新菜單位置直到目標(biāo)位置
void __pvt_do_stepped_animate(CCGraphic_MenuAnimations* animations, CCGraphic_Point* end_tl_p 
)
{// 如果動(dòng)畫步長為負(fù),表示需要向下移動(dòng)if(animations->y_step < 0){// 逐步向下執(zhí)行動(dòng)畫,直到達(dá)到目標(biāo)位置while(animations->tl_point.y > end_tl_p->y){__pvt_doByStep(animations);  // 執(zhí)行單步動(dòng)畫
#ifdef REQ_ANIMATION_DELAY// 延時(shí),模擬動(dòng)畫效果__device_delay(ANIMATION_DELAY_MS);
#endif}}// 如果動(dòng)畫步長為正,表示需要向上移動(dòng)else{// 逐步向上執(zhí)行動(dòng)畫,直到達(dá)到目標(biāo)位置while(animations->tl_point.y < end_tl_p->y){__pvt_doByStep(animations);  // 執(zhí)行單步動(dòng)畫
#ifdef REQ_ANIMATION_DELAY// 延時(shí),模擬動(dòng)畫效果__device_delay(ANIMATION_DELAY_MS);
#endif} ? ? ? ?}}
關(guān)于切換focus的菜單和進(jìn)入父子菜單的函數(shù)
// 切換菜單項(xiàng)索引并執(zhí)行動(dòng)畫
static void __pvt_switchIndex(CCGraphic_Menu* menu, uint8_t index)
{// 如果索引沒有變化,不做任何操作if(index == menu->current_offset) return;
?// 如果新索引大于當(dāng)前索引,表示需要向下移動(dòng)if(index > menu->current_offset){// 如果當(dāng)前動(dòng)畫步長為負(fù),改為正值if(menu->animation_holder->y_step < 0){menu->animation_holder->y_step = -menu->animation_holder->y_step;}}// 如果新索引小于當(dāng)前索引,表示需要向上移動(dòng)else{// 如果當(dāng)前動(dòng)畫步長為正,改為負(fù)值if(menu->animation_holder->y_step > 0){menu->animation_holder->y_step = -menu->animation_holder->y_step;}}// 更新當(dāng)前菜單項(xiàng)的索引menu->current_offset = index;// 計(jì)算目標(biāo)位置CCGraphic_Point end_tlp;end_tlp = menu->animation_holder->tl_point; end_tlp.y = index * menu->animation_holder->animationOffsetSize.height;// 如果啟用了動(dòng)畫,執(zhí)行逐步動(dòng)畫if(menu->enabled_animations)__pvt_do_stepped_animate(menu->animation_holder, &end_tlp);else// 否則,立即執(zhí)行動(dòng)畫__pvt_do_as_immediate(menu->animation_holder, &end_tlp);
}
?
// 進(jìn)入子菜單并顯示子菜單的內(nèi)容
static CCGraphic_Menu* __pvt_enterSub(CCGraphic_Menu* parentMenu)
{// 緩存父菜單的圖標(biāo)狀態(tài)uint8_t cached_icon_state = parentMenu->icon_state;// 獲取父菜單的子菜單CCGraphic_Menu* subone = __pvt_current_owns_subMenu(parentMenu);// 如果沒有子菜單,返回NULLif(!subone) return NULL;// 隱藏當(dāng)前菜單parentMenu->operations.common.hide(parentMenu);// 恢復(fù)父菜單的圖標(biāo)狀態(tài)parentMenu->icon_state = cached_icon_state;// 如果子菜單有圖標(biāo),顯示圖標(biāo),否則顯示子菜單if(subone->icon_state){subone->operations.showIcon(subone);}else{subone->operations.common.show(subone);}// 返回子菜單return subone;
}
?
// 返回父菜單并顯示父菜單的內(nèi)容
static CCGraphic_Menu* __pvt_backParent(CCGraphic_Menu* subMenu)
{// 緩存子菜單的圖標(biāo)狀態(tài)uint8_t cached_icon_state = subMenu->icon_state;// 獲取子菜單的父菜單CCGraphic_Menu* parentMenu = __pvt_owns_parent_current(subMenu);// 如果沒有父菜單,返回NULLif(!parentMenu) return NULL;// 隱藏當(dāng)前子菜單subMenu->operations.common.hide(subMenu);// 恢復(fù)子菜單的圖標(biāo)狀態(tài)subMenu->icon_state = cached_icon_state;// 如果父菜單有圖標(biāo),顯示圖標(biāo),否則顯示父菜單if(parentMenu->icon_state){parentMenu->operations.showIcon(parentMenu);}else{parentMenu->operations.common.show(parentMenu);}// 返回父菜單return parentMenu;
}

完整的測試文件

現(xiàn)在來看看完整的測試文件!

#include "Test/OLED_TEST/oled_test.h"
#include "Test/GraphicTest/graphic_test.h"
#include "Graphic/widgets/components/CCGraphic_TextEdit/CCGraphic_TextEdit.h"
void test_oled_iic_functionalities()
{OLED_Handle handle;user_init_hard_iic_oled_handle(&handle);test_set_pixel_line(&handle, 1, 2);HAL_Delay(1000);test_clear(&handle);test_set_pixel_line(&handle, 2, 1);HAL_Delay(1000);test_clear(&handle);
}
?
void test_oled_spi_functionalities()
{OLED_Handle handle;user_init_hard_spi_oled_handle(&handle);test_set_pixel_line(&handle, 1, 2);HAL_Delay(1000);test_clear(&handle);test_set_pixel_line(&handle, 2, 1);HAL_Delay(1000);test_clear(&handle);
}
?
static void __helper_on_set_text(CCGraphicTextEdit* edit, char* sources, uint32_t shown_time)
{edit->operations.setText(edit, sources);HAL_Delay(shown_time * 1000);edit->operations.clear(edit);
}
?
#define SET_TEXT_CONV(SRC, SECS) do{ sources = SRC;\__helper_on_set_text(&edit, sources, SECS);}while(0)
?
static void __test_common(CCDeviceHandler* handler)
{CCGraphicTextEdit ? edit;CCGraphic_AsciiTextItem item;CCGraphic_Point p;p.x = 0;p.y = 0;CCGraphic_Size acceptablesize = CCGraphicWidget_MaxAcceptable_Size(handler);CCGraphicWidget_init_AsciiTextItem(&item, p, acceptablesize, ASCII_6x8);CCGraphic_init_CCGraphicTextEdit(&edit, handler, &item);edit.acquired_stepped_update = 1;char* sources;SET_TEXT_CONV("Hello! Welcome CCGraphic SimpleTest!", 5);SET_TEXT_CONV("If you see this sentences, ""it means that you have passed the GraphicTest""and congratulations!", 7);
?SET_TEXT_CONV("Graphic Test On Base begin", 4);SET_TEXT_CONV("Test Points", 4);on_test_draw_points(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Lines", 4);on_test_draw_line(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Circles", 4);on_test_draw_circle(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Rectangle", 4);on_test_draw_rectangle(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Triangle", 4);on_test_draw_triangle(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Ellipse", 4);on_test_draw_ellipse(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Arc", 4);on_test_draw_arc(handler);HAL_Delay(1000);SET_TEXT_CONV("Graphic Test On Base end", 4);SET_TEXT_CONV("Graphic Test On widget begin", 4);SET_TEXT_CONV("Test Image Drawing", 4);
?/* widget test */on_test_draw_image(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Ascii Draw", 4);on_test_draw_ascii(handler);HAL_Delay(1000);SET_TEXT_CONV("Graphic Test On widget end", 4);SET_TEXT_CONV("Graphic Test On component begin", 4);SET_TEXT_CONV("Test TextEdit", 4);/* components test */on_test_component_textEdit_test(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Frame", 4);on_test_component_frame_test(handler);HAL_Delay(1000);SET_TEXT_CONV("Test Menu", 4);on_test_component_menu(handler);HAL_Delay(1000);SET_TEXT_CONV("Graphic Test On component end", 4);SET_TEXT_CONV("Finish Testing, enjoy!", 4);
}
?
?
void test_graphic_hardiic_functionalities()
{CCDeviceHandler handler;on_test_init_hardiic_oled(&handler);
?__test_common(&handler);
}
?
void test_graphic_soft_spi_functionalities()
{CCDeviceHandler handler;on_test_init_softspi_oled(&handler);
?__test_common(&handler);
}
?
?
void test_graphic_hard_spi_functionalities()
{CCDeviceHandler handler;on_test_init_hardspi_oled(&handler);
?__test_common(&handler);
}

效果就是這個(gè)完整的測試視頻:

完整測試視頻

目錄導(dǎo)覽

總覽

協(xié)議層封裝

OLED設(shè)備封裝

繪圖設(shè)備抽象

基礎(chǔ)圖形庫封裝

基礎(chǔ)組件實(shí)現(xiàn)

動(dòng)態(tài)菜單組件實(shí)現(xiàn)

http://aloenet.com.cn/news/41185.html

相關(guān)文章:

  • 國外超酷設(shè)計(jì)網(wǎng)站游戲推廣
  • 如何做服裝的微商城網(wǎng)站重慶森林經(jīng)典臺詞獨(dú)白
  • 云南省建設(shè)工程招標(biāo)投標(biāo)行業(yè)協(xié)會(huì)網(wǎng)站百度seo排名優(yōu)化教程
  • vs2010做網(wǎng)站時(shí)間控件廊坊網(wǎng)站排名優(yōu)化公司哪家好
  • readme.md做網(wǎng)站seo平臺是什么意思
  • 哪個(gè)網(wǎng)站可以做優(yōu)惠券seo技術(shù)培訓(xùn)寧波
  • qq是哪個(gè)公司開發(fā)seo排名平臺
  • 特價(jià)手機(jī)網(wǎng)站建設(shè)1688seo優(yōu)化是什么
  • 網(wǎng)頁設(shè)計(jì)實(shí)訓(xùn)報(bào)告實(shí)訓(xùn)小結(jié)深圳百度seo整站
  • 網(wǎng)站logo怎么做最清楚惠州網(wǎng)站制作推廣
  • 廈門app網(wǎng)站建設(shè)平臺推廣是什么工作
  • 建站之星如何建網(wǎng)站sem推廣是什么
  • 哪里做企業(yè)網(wǎng)站上海seo服務(wù)
  • 天津市做網(wǎng)站的公司查淘寶關(guān)鍵詞排名軟件
  • 香港一卡通app下載鄭州seo外包顧問熱狗
  • 去哪里找做網(wǎng)站的百度競價(jià)ocpc投放策略
  • 設(shè)計(jì)導(dǎo)航網(wǎng)站 左側(cè)菜單欄網(wǎng)絡(luò)營銷課程論文
  • 農(nóng)產(chǎn)品網(wǎng)站如何做地推網(wǎng)易企業(yè)郵箱
  • 網(wǎng)站開發(fā)語言 排行榜關(guān)鍵詞seo公司真實(shí)推薦
  • 醫(yī)藥網(wǎng)站建設(shè)客戶的需求廈門關(guān)鍵詞排名推廣
  • 西部數(shù)碼 空間做2個(gè)網(wǎng)站什么是新媒體運(yùn)營
  • 自適應(yīng)手機(jī)網(wǎng)站 css愛站網(wǎng)是什么
  • 博物館文化網(wǎng)站建設(shè)青島排名推廣
  • 投訴做網(wǎng)站的電話服務(wù)器域名查詢
  • 室內(nèi)設(shè)計(jì)網(wǎng)站大全網(wǎng)seo新手教程
  • 響應(yīng)式網(wǎng)站弊端互聯(lián)網(wǎng)公司
  • 池州市住房和城鄉(xiāng)建設(shè)委員會(huì)網(wǎng)站百度推廣聯(lián)系人
  • 山東安康建設(shè)項(xiàng)目管理有限公司網(wǎng)站北京谷歌優(yōu)化
  • 大宗商品現(xiàn)貨交易app天津seo優(yōu)化公司哪家好
  • 無錫網(wǎng)站優(yōu)化價(jià)格福鼎網(wǎng)站優(yōu)化公司