Trao đổi với tôi

http://www.buidao.com

11/28/10

[Symbian] Lập trình với Symbian OS P5

Một số thao tác vẽ cơ bản với đồ họa trong Symbian


Mỗi vùng màn hình chính là đối tượng CCoeControl, được xem như control mà bạn có thể vẽ nội dung bất kỳ lên vùng màn hình mà nó chiếm. Phương thức Draw() kế thừa từ CCoeControl cho phép truy cập tới vùng màn hình được định nghĩa bởi TRect. Tất cả các lớp kế thừa từ CCoeControl đều có hàm Draw(), và bạn chồng hàm này để thực hiện các thao tác vẽ.

Một control cần một cửa sổ để vẽ, và vùng cửa sổ này được thiết lập trong hàm ConstructL(). Và để sử dụng CCoeControl bạn cũng cần include tệp “coecontrol.h” và thêm link “cone.lib”. Ví dụ sau tạo một lớp kế thừa từ CCoeControl và thực hiện vẽ lên vùng màn hình của nó:
MÃ: CHỌN TẤT CẢ
class CStartContainer : public CCoeControl
{
public:
CStartContainer();
CStartContainer();
private:
void ConstructL(const TRect& aRect);
// from CCoeControl
void Draw(const TRect& aRect) const;
};

Hàm ConstructL() cần truyền kích thước cửa sổ, control, vùng mà ứng dụng cần truy cập. Bạn có thể sử dụng hàm ClientRect() để lấy kích thước vùng client của ứng dụng, và hàm ApplicationRect() để lấy kích thước của vùng ứng dụng.

Bạn truyền AppUi()->ApplicationRect() trong hàm ConstructL() để truy cập tới toàn bộ vùng màn hình. Dĩ nhiên bạn có thể tùy biến vùng kích thước này:
MÃ: CHỌN TẤT CẢ
void CStartContainer::ConstructL(const TRect& aRect)
{
CreateWindowL();
SetRect(aRect);
ActivateL();
}

- Hàm CreateWindowL() để tạo cửa sổ control
- SetRect() thiết lập phạm vi cửa sổ tới kích thước TRect.
- Hàm ActivateL() bảo cho framework biết rằng nó có thể truy cập vào màn hình.

Để vẽ lên màn hình, một ngữ cảnh đồ họa (Graphics Context - GC) được lấy ra. Và tất cả việc vẽ đều thực hiện trong ngữ cảnh đồ họa này, CWindowGc.
MÃ: CHỌN TẤT CẢ
void CStartContainer::Draw(const TRect& aRect) const
{
CWindowGc& gc = SystemGc();
gc.Clear(aRect);
...
}

- Hàm SystemGc() trả về ngữ cảnh đồ họa chuẩn cho biến gc. Và vùng control phải được xóa trước khi bạn thực hiện thao tác vẽ trên nó.
- Hàm Draw(), nó là hằng chứ không phải là leave, vì thế bạn không thể tạo các phần tử động trong hàmDraw() và bạn cũng không thể sửa trạng thái bất kỳ dữ liệu nào trong hàm này. Sau khi bạn thay đổi trạng thái bạn muốn cập nhật lại vùng vẽ bạn nên gọi hàm DrawNow(), hàm này sẽ tự động gọi hàmDraw() của chính nó.

Để thực hiện vẽ hình theo ý của bạn, bạn phải thiết lập các thông số bút vẽ trước khi thực hiện vẽ. Dạng và kích thước của bút vẽ cũng có thể thay đổi và được sử dụng để vẽ các hình. Đoạn mã sau thiết lập kiểu bút vẽ là dash và kích thước tới giá trị aSize:
MÃ: CHỌN TẤT CẢ
gc.SetPenStyle(CGraphicsContext::EDashedPen);
gc.SetPenSize(aSize);

Kiểu liệt kê TPenStyle bao gồm:
- ENullPen
- ESolidPen
- EDottedPen
- EDashedPen
- EDotDashPen
- EDotDotDashPen
Đoạn mã sau sử dụng để vẽ đường thẳng và hình chữ nhật:
MÃ: CHỌN TẤT CẢ
gc.DrawLine(aPoint, bPoint);
gc.DrawRect(rect);


Bút vẽ sử dụng để vẽ các đường, còn chổi vẽ để tô các vùng. Kiểu liệt kê TBrushStyle được sử dụng trong hàm SetBrushStyle. Màu của chổi vẽ được thiết lập thông qua hàm SetBrushColor().

Các phương thức vẽ khác như DrawEllipse()DrawPolygon(), chúng sử dụng chổi vẽ để tô vùng và bút vẽ để vẽ đường biên.

Sử dụng font và vẽ chữ trên Symbian


+ Hiển thị chữ
Theo lý thuyết việc vẽ xâu văn bản lên màn hình là rất đẹp nhưng việc định dạng có thể là một phiền toái.
MÃ: CHỌN TẤT CẢ
_LIT(KExampleText, "Chocolate Cake"));
gc.DrawText(KExampleText, TPoint(80,40));

Tham số vị trí là tương đối với Control vì vậy TPoint(0,0) là góc trên bên trái vùng màn hình của control. Vị trí này có thể khác nhau trên các thiết bị khác nhau vì vậy bạn phải rất cẩn thận. Nhưng thật may mắn có một số hàm như hàm CGraphicsContext::ECenter giúp can lề giữa đoạn văn bản. Một vài hàm khác giúp bạn thay đổi font và màu của văn bản.

Để thay đổi màu chữ khi vẽ bạn sử dụng hàm SetPenColor():
MÃ: CHỌN TẤT CẢ
gc.SetPenColor(KRgbRed);

Có một số màu được định nghĩa trước có kiểu TRgb:
MÃ: CHỌN TẤT CẢ
KRgbBlack
KRgbDarkGray
KRgbDarkRed
KRgbDarkGreen
KRgbDarkYellow
KRgbDarkBlue
KRgbDarkMagenta
KRgbDarkCyan
KRgbRed
KRgbGreen
KRgbYellow
KRgbBlue
KRgbMagenta
KRgbCyan
KRgbGray
KRgbWhite

Lớp TRgb cho phép bạn tạo màu theo ý mình:
- Hàm khởi tạo: Ví dụ như TRgb(0x00112387), TRgb(50, 50, 0)
- Hàm thay đổi màu: như SetRed()
- Hàm thay đổi số lượng màu: như Gray16()

Để thay đổi font chữ bạn sử dụng đoạn mã:
MÃ: CHỌN TẤT CẢ
const CFont* font = iEikonEnv->AnnotationFont();
gc.UseFont(font);

Có một số font được định nghĩa trước, bao gồm:

Để lấy font bình thường (mặc định) bạn sử dụng lệnh:
MÃ: CHỌN TẤT CẢ
gc.UseFont(iCoeEnv->NormalFont());

Nhớ rằng một khi bạn hoàn thành việc sử dụng font, bạn phải gọi hàm:
MÃ: CHỌN TẤT CẢ
gc.DiscardFont();

Việc bố trí các phần tử trên màn hình với các font khác nhau là công việc khá phức tạp. Một lựa chọn đó là không sử dụng cách vẽ trực tiếp mà sử dụng lớp CRichText. Lớp này giúp bạn dễ dàng định dạng chữ và chèn ảnh.

+ Lựa chọn font chữ trong Symbian OS
Để lựa chọn font chữ bạn làm các thao tác sau:
- Khởi tạo đối tượng CFont
- Thiết lập các thông số cho font (Thông qua cấu trúc TFontSpec)
- Gán CFont tới font sẵn có gần nhất để cấu trúc TFontSpec phù hợp với màn hình thiết bị
- Thiết lập làm font hiện tại (Ngữ cảnh đồ hoạ sử dụng font này) để vẽ (Sử dụng hàm UseFont)
- Vẽ text
- Loại bỏ font khỏi ngữ cảnh đồ hoạ
- Loại bỏ font bằng hàm CGraphicsDevice::ReleaseFont().


Sau đây là đoạn mã ví dụ về lựa chọn Font:
MÃ: CHỌN TẤT CẢ
_LIT(KFontName, "Verdana");
CFont* iUseFont = NULL;
TFontSpec myFontSpec(KFontName, 18);
TFontStyle myFontStyle(EPostureUpright, EStrokeWeightNormal, EPrintPosSubscript);
myFontSpec.iFontStyle = myFontStyle;
iCoeEnv->ScreenDevice()->GetNearestFontInPixels(iUseFont,myFontSpec);
iFbsGc->UseFont(iUseFont);
_LIT(KTextExample, "\x43h\xE0o m\x1EEBng \x62\x1EA1n th\x61m \x64i\x1EC5n \x111\xE0n MyShop\x34Vn"); // Xâu "Chào mừng bạn tham diễn đàn MyShop4Vn"
iFbsGc->DrawText(KTextExample, TPoint(5,80));
iFbsGc->DiscardFont();
iCoeEnv->ScreenDevice()->ReleaseFont(iUseFont);

Chú ý:
- Để có thể sử dụng font Verdana và hiển thị được tiếng việt bạn phải copy hai tệp verdana.ttfFREETYPE.DLL vào thư mục c\system\fonts.
- Hàm UseFont khá chậm nên bạn nên dùng nó ngay trong hàm khởi tạo. Khi sử dụng hàm này, thì phải dùng thêm hàm DiscardFont() để loại bỏ font khỏi ngữ cảnh đồ hoạ. Nếu bạn sử dụng nhiều font vẽ cùng lúc thì tốt nhất bạn nên sử dụng hàm UseFontNoDuplicate (iFbsGc->UseFontNoDuplicate((const CFbsBitGcFont *) iUseFont);), khi sử dụng hàm này bạn ko cần sử dụng hàm DiscardFont nữa.


+ Tự động load và unload tệp font *.gdr
Nhiều khi ứng dụng của bạn sử dụng bộ font chữ riêng *.gdr do bạn tự tạo, khi đó mỗi khi khởi động ứng dụng bạn tự động load font này và mỗi khi kết thúc ứng dụng của bạn, bạn phải unload nó. Giả sử bạn có font chữ với tên là "My Font" và tên tệp font là "myfont.gdr" và bạn để nó trong thư mục "c:\data".

Đầu tiên bạn phải khai báo các biến cần dùng (nên là biến thành viên của lớp AppUi):
MÃ: CHỌN TẤT CẢ
_LIT(KFontName, "My Font");
_LIT(KFontFile, "c:\\data\\myfont.gdr");
...
TInt iFontID;
CFont* iUseFont;

Khi khởi động bạn load font này lên bằng đoạn mã:
MÃ: CHỌN TẤT CẢ
TFileName fntFilename(KFontFile);
TInt nRes = CEikonEnv::Static()->ScreenDevice()->AddFile(fntFilename, iFontID);
if (nRes==KErrNone)
{
// Load được font
// Tạo biến CFont để sử dụng font này
TFontSpec myFontSpec(KFontName, 12);
TFontStyle myFontStyle(EPostureUpright, EStrokeWeightNormal, EPrintPosNormal);
myFontSpec.iFontStyle = myFontStyle;
iCoeEnv->ScreenDevice()->GetNearestFontInPixels(iUseFont,myFontSpec);
}
else
{
// Không load được tệp font
// ...
}

Bây giờ bạn có thể sử dụng font chữ này vẽ bình thường. Khi kết thúc ứng dụng, bạn phải unload font này khỏi hệ thống bằng đoạn mã:
MÃ: CHỌN TẤT CẢ
if (iUseFont)
iCoeEnv->ScreenDevice()->ReleaseFont(iUseFont);
if (iFontID!=0)
CEikonEnv::Static()->ScreenDevice()->RemoveFile(iFontID);

* Chú ý:
- Bạn có thể dùng tiện ích "Easy GDR Creator" để tạo font gdr.
- Bạn có thể dùng tiện ích "KVT Symbian Font Converter" để chuyển từ font *.ttf sang *.gdr
- Các tiện ích này bạn tìm trên mạng, rất nhiều và rất dễ tìm
- Hiển thị bằng font *.gdr thực sự không được đẹp lắm
- Bạn có thể dùng đoạn mã trên để load và hiển thị font *.ttf. Tôi đã thử trên dòng Symbian S60 3rd, chạy rất okie.


+ Liệt kê các font sẵn có trong hệ thống
Nhiều lúc bạn muốn xem hệ thống hiện tại đã có những font gì, hay bạn muốn xem font của bạn đã thực sự được load vào hệ thống hày chưa. Như vậy bạn cần liệt kê được các font sẵn có trong hệ thống. Đoạn mã sau giúp bạn làm điều này:
MÃ: CHỌN TẤT CẢ
typedef TBuf<256> MyFontName;
TInt iFontNum = 0;
CArrayFix* iArrFonts = NULL;

iFontNum = iCoeEnv->ScreenDevice()->NumTypefaces();
if (iFontNum>0)
iArrFonts = new(ELeave)CArrayFixFlat(iFontNum);
if (iArrFonts)
{
MyFontName myFontName;
TTypefaceSupport myTypefaceSupport;
for(TInt i=0; i {
iCoeEnv->ScreenDevice()->TypefaceSupport(myTypefaceSupport, i);
myFontName = myTypefaceSupport.iTypeface.iName.Des();
iArrFonts->AppendL(myFontName);
}
}

// Sử dụng mảng iArrFonts ở đây
// ...

// Xóa mảng cấp phát
if (iArrFonts)
delete iArrFonts;

Toàn bộ font tìm được sẽ lưu vào mảng iArrFonts để tiện cho việc sử dụng. Sau khi liệt kê bạn có thể hiển thị bằng cách vẽ lên màn hình hoặc hiển thị bằng hộp thoại Information.

Xử lý phím nhấn


Có nhiều cách khác nhau để tương tác với các ứng dụng trên điện thoại di động. Hình sau thể hiện một số cách mà người dùng có thể tương tác với điện thoại di động:

Phương thức vào thông dụng là thông qua các phím trên điện thoại, bao gồm ba vùng phím:
- Keypad: Các phím số từ 0 đến 9, phím *, #
- Soft keys
- Navigation key: Phím định hướng, bao gồm phím lên, xuống, trái, phải và phím OK

Ngoài ra người dùng có nhiều cách khác để tương tác với điện thoại. Vì điện thoại được thiết kế với mục đích chính là đàm thoại, vì thế giọng nói là phương thức vào chính. Gần đây các nhà phát triển quan tâm tới các ứng dụng khởi động bằng giọng nói và chắc chắn các ứng dụng loại này sẽ trở thành thông dụng trong tương lai không xa. Ngoài ra, sử dụng hồng ngoại cũng như Bluetooth cũng cho phép vào ra dữ liệu với điện thoại. Ở đây chúng ta quan tâm tới phương thức vào thông dụng nhất, đó là sử dụng phím.

Trong lớp CoeControl có cài đặt một phương thức ảo OfferKeyEventL() để điều khiển sự kiện nhấn phím, nó được framework gọi mỗi khi sự kiện nhấn phím xảy ra. Vì vậy để bắt sự kiện phím nhấn, bạn phải kế thừa từ lớp CoeControl và định nghĩa phương thức OfferKeyEventL().
MÃ: CHỌN TẤT CẢ
#include
class CKeyPressContainer : public CCoeControl, MCoeControlObserver
{
...
TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,
TEventCode aType);
...
}

Khi sự kiện phím xảy ra, phương thức OfferKeyEventL() của tất cả các điều khiển trong stack được gọi. Khi phương thức này được gọi, nó có thể xử lý sự kiện hoặc bỏ qua nó thông qua giá trị trả về của hàm này: Trả về TKeyResponse: EKeyWasNotConsumed nếu sự kiện bỏ qua và trả về EKeyWasConsumednếu sự kiện được xử lý.

Khi sự kiện phím xảy ra, đầu tiên bạn phải kiểm tra xem đó là sự kiện phím nào: phím nhấn (Key down), phím nhả (Key up) hay sự kiện nhấn phím (Key press). Nếu bạn chỉ quan tâm tới sự kiện phím nhấn, bạn có thể sử dụng đoạn mã:
MÃ: CHỌN TẤT CẢ
TKeyResponse CKeyPressContainer::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
{
if (aType != EEventKey)
return EKeyWasNotConsumed;
else
{
if (aKeyEvent.iCode == '5')
{
...
return EKeyWasConsumed;
}
}
}

Tiếp theo bạn phải kiểm tra các tham số của sự kiện phím trả về thông qua tham số có kiểu TKeyEvent, đây là cấu trúc gồm 4 trường:
- iCode (định nghĩa trong TKeyCode): Mã của phím nhấn trong sự kiện Key Press.
- iModifiers (định nghĩa trong TEventModifier): Trạng thái phím sửa và thiết bị trỏ
- iScanCode (định nghĩa trong TStdScanCode): Mã scan của phím, sử dụng trong các sự kiện Key Down và Key Up.
Bạn cũng có thể sinh ra một sự kiện phím bằng cách gán các trường trong cấu trúc TKeyEvent, sau đó gọi hàm OfferKeyEventL() để giả sự kiện:
MÃ: CHỌN TẤT CẢ
TKeyEvent myEvent;
myEvent.iCode = EEventKeyDown;
myEvent.iModifiers = EAllModifiers;
myEvent.iRepeats = 0;
myEvent.iScanCode = EAllModifiers;
OfferKeyEventL(myEvent, EEventKey);

Sự kiện cho Menu


Phương thức HandleCommandL() được sử dụng để điều khiển menu chọn và các lệnh khác được định nghĩa trong tệp resource. Nó là hàm ảo định nghĩa trong CEikAppUi (hoặc CAknAppUi trong Symbian S60 3rd) và được gọi bởi phương thức ProcessCommandL() trong Framework. Nơi thường được sử dụng để cài đặt phương thức này là trong lớp AppUi (kế thừa từ CEikAppUi hoặc CAknAppUi).
MÃ: CHỌN TẤT CẢ
void CKeyPressAppUi::HandleCommandL(TInt aCommand)
{
switch (aCommand)
{
case EEikCmdExit:
{
Exit();
break;
}
case EAknSoftkeyBack:
{
ActivateLocalViewL(KMainViewId);
break;
}
case EStartGame:
{
iContainer->StartGame();
break;
}
}
}


Còn nhiều phần quan trọng khác tôi không thể nói hết ở đây, bạn xem thêm cuốn "Wiley S60 Programming - A Tutorial Guide (Apr - 2007).pdf" mà tôi giới thiệu ngay ở bài viết đầu tiên.


Một số lỗi liên quan đến lập trình và cài đặt trên Symbian

Khi lập trình hay cài đặt phần mềm đôi khi bạn gặp những rắc rối, mà bạn không biết tại sao. Sẽ mất thời gian để bạn phải tìm hiểu nguyên nhân, hay serch trên mạng để tìm cách giải quyết. Khi lập trình và cài đặt các phần mềm trên Symbian tôi cũng gặp nhiều rắc rối, xin đưa ra để nếu các bạn có gặp phải sẽ không phải mất nhiều thời gian để giải quyết.

* Một vài chú ý khi lâp trình với Symbian OS
- Nếu bạn muốn sử dụng thư viện toán học math.h trong thư viện chuẩn thì bạn phải thêm dòng
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc trong tệp mmp (Chú ý phải cập nhật lại tệp này trước khi build).
- Khi build GCCE, nếu báo lỗi "undefined reference to `atan2`", lỗi này là do bạn sử dụng thư viện toán học nhưng lại chưa đưa lib “estlib.lib”. Nếu bạn không khai báo thư viện này thì vẫn chạy được máy ảo nhưng build cho máy thật lại bị lỗi. Sửa lỗi này đơn giản là bạn khai báo thêm thư viện “estlib.lib”.
- Trong Symbian S60 3rd, nếu bạn nhận được thông báo “*** missing separator. Stop.” tại một dòng nào đó trong tệp *.GCCE thì lỗi này do bạn chưa cài “CSL Arm Toolchain” hoặc đường dẫn khai báo tới chương trình này trong biến môi trường PATH không đúng. Đường dẫn mặc định của chương trình này là“C:\Program Files\CSL Arm Toolchain\bin”. Sau khi cài xong, bạn đóng Project và mở lại, dịch Releaselà được.
- Khi bạn dịch trên Symbian S60 3rd mà gặp lỗi báo: “error PRJ0019: A tool returned an error code from "Building help file"”. Thì lỗi này là do bạn cài Perl phiên bản không đúng (chẳng hạn nhưActivePerl-5.8.8), bạn remove phiên bản Perl này và cài lại phiên bản ActivePerl-5.6.1
- Thiết lập bàn phím QWERTY cho máy ảo trên Symbian 9: Hiện nay các con E61 hay E90 có hỗ trợ bàn phím Qwerty. Để tiện lợi cho việc test ứng dụng trên con này, bạn có thể thiết lập bàn phím Qwertycho máy ảo. Để thiết lập, rất đơn giản bạn chỉ cần vào thư mục“\Symbian\9.1\S60_3rd\Epoc32\Data”, edit tệp “epoc.ini” rồi thêm dòng “configuration epoc_352x416_qwerty.ini”, bạn save lại, rồi bật máy ảo là okie.
- Khi dịch Project Symbian S60 1st sử dụng VC nếu báo lỗi “This project does not support platform or program "VC6””, lỗi này là do bạn cài SDK cho S60 1st rồi lại cài SDK cho S60 3rd. Để khắc phục lỗi này bạn remove SDK S60 1st và cài lại nó, sau đó Restart lại. Để không gặp lỗi này khi cài đặt tốt nhất là bạn cài đặt SDK S60 3rd trước rồi sau đó mới cài đặt SDK S60 1st.
- Khi build xong bạn ấn Ctrl+F5 mà máy ảo không nên được là do đường dẫn tới file “EpocWrapper.exe”(trên S60 3rd) hoặc "epoc.exe" (trên S60 1st) không đúng. Trên S60 3rd, bạn vào Properties củaProject, ở mục Debugging, thay đổi lại đường dẫn chính xác trong trường Command. Còn trên S60 1st, bạn chỉ đến đúng tệp "epoc.exe" là okie (chú ý debug và release thì bạn trỏ tương ứng nhé)

* Một số lỗi khi cài đặt phần mềm (đặc biệt trên Symbian S60 3rd)
- Certificate may not yet be valid , is expired or phone's date setting may be incorrect: Lỗi này thường gặp khi bạn mới format máy khi gặp lỗi này các bạn chỉ cần chỉnh lại ngày giờ cho chính xác với thời điểm hiện tại.
- not support: Lỗi nó là phần mềm bạn cài ko phải là của s60v3 mà của s60v2 hay là phần mềm của của các loại symbian khác (uiq, s90..), nhưng cũng có trường hợp do các files có tên quá dài chấm nhiều chấm quá nên máy ko hiểu và báo lỗi, nếu báo lỗi này thì trước tiên các bạn thử đổi tên nó ngắn gọn lại nhé và cài thử lại xem có được ko.
+ File Corupted: Là files đó đã bị lỗi bạn nên tải lại files khác.
+ error update: Symbian OS 9.1 ko cho bạn update vesion của soft đã CR, bạn nên xóa bỏ soft trong máy và cài lại sẽ ok, muốn update phải là ứng dụng ko CR (đăng ký = chìa khóa).
+ Certificate error contact the application supplier: Lỗi này báo là pm này chưa được signsis (đăng ký).
+ Certificate exprired: Lỗi "chứng chỉ hết hạn" này xảy ra đối với 1 số dòng máy mới & firmware mới, bạn có thể đổi ngày giở lui lại khoảng 1 vài tháng, nhưng cũng có 1 số soft ko cài được -> chỉ có cách là đợi vesion mới của soft được CR mà cài. Đối với dòng E (E65, E61 ....): thì bạn vào app manager -> software intall -> chọn all la ok.

Một số mã lỗi trong Symbian OS


Khi lập trình, việc gặp lỗi là không thể tránh khỏi. Ở đây tôi xin liệt kê một số lỗi để tiện cho các bạn tra cứu và dễ dàng biết được nguyên nhân của lỗi.
Đối với tôi sách vở là người thầy và cuộc đời là trường đại học