Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 74
Текст из файла (страница 74)
Therefore the current S60 version of oandxappview.h needs to define anadditional CAknView-derived class, COandXGameView. Following thepattern shown in Figure 14.7, this view class owns the game’s maincontrol (which has been renamed to COandXAppViewContainer).When reading about the code required to support the History view,you will need to bear in mind that corresponding changes have to bemade to the code of the Game view.14.4Creating and Managing the ViewsAs is generally the case, the Noughts and Crosses application’s views arecreated and managed from the application UI (see Figures 14.3 and 14.4).For convenience, the application UI stores pointers to the views, asshown in the following extract from the UIQ class definition.
They arenon-owning pointers, since ownership of the views is transferred to theview framework as soon as they are created.class COandXAppUi : public CQikAppUi{...private:COandXView* iAppView;// Non-owning pointersCOandXHistoryView* iHistoryView; // to the two views};Construction and RegistrationThe application creates its views in the application UI’s second-phaseconstructor, as illustrated below for the UIQ application.void COandXAppUi::ConstructL(){// Calls ConstructL that initiate the standard values.BaseConstructL();iEngine = COandXEngine::NewL();iController = COandXController::NewL();// Create the view and add it to the frameworkiAppView = COandXView::NewLC(*this);AddViewL(*iAppView);CleanupStack::Pop(); // game view// and the history viewiHistoryView = COandXHistoryView::NewLC(*this);AddViewL(*iHistoryView);CREATING AND MANAGING THE VIEWS407CleanupStack::Pop(); // history viewSetDefaultViewL(*iAppView);ReportWhoseTurn();}The code follows the same overall pattern for both S60 and UIQ.Once a view has been created, it is added to the framework by callingAddViewL().
At that point the framework takes over ownership of theview and calls RegisterViewL() to register the view with the viewserver. In UIQ, where the view is derived from CCoeControl, callingAddViewL() also adds the view to the control stack. In S60, whereCAknView has no knowledge of the class used for the control, we haveto include explicit code to add a view to the control stack.The final task in creating the application’s views is to specify the defaultview, by means of a call to the application UI’s SetDefaultViewL().Activation and DeactivationAs is common in many applications, the Noughts and Crosses examplecalls ActivateViewL() in response to the user selecting a menu option.From the History view, selecting the Return to game menu option resultsin a call to the simpler of the two overloads.void COandXHistoryView::HandleCommandL(CQikCommand& aCommand){switch(aCommand.Id()){...case EOandXDisplayGame:OandXAppUi()->ActivateViewL(TVwsViewId(KUidOandXApp,KUidOandXView));break;...}}From the Game view, selecting either Game statistics or Game historyresults in a call to the second overload of ActivateViewL().void COandXView::HandleCommandL(CQikCommand& aCommand){switch(aCommand.Id()){...case EOandXDisplayHistory :_LIT8(KDummy, "");OandXAppUi()->ActivateViewL(TVwsViewId(KUidOandXApp,KUidOandXHistoryView),TUid::Uid(EHistoryViewDisplayHistory),KDummy);408VIEWS AND THE VIEW ARCHITECTUREbreak ;case EOandXDisplayStats :OandXAppUi()->ActivateViewL(TVwsViewId(KUidOandXApp,KUidOandXHistoryView),TUid::Uid(EHistoryViewDisplayStats),KDummy);break ;...}}In this case, the message ID specifies whether the view is to displayeither the history or the statistical data, and the message data is not used.When the view is activated, it receives an event in the form of a call to itsViewActivatedL() function, which will include any message ID anddata that was passed to the application UI’s ActivateViewL() function.If there was a previously active view, it is automatically deactivated andit also receives an event to inform it of the deactivation.14.5Implementing the MCoeView InterfaceAs described in Section 14.2, the functions to be implemented are thefollowing:class MCoeView{public:virtual TVwsViewId ViewId() const=0;private:virtual void ViewActivatedL(const TVwsViewId& aPrevViewId,TUid aCustomMessageId,const TDesC8& aCustomMessage)=0;virtual void ViewDeactivated()=0;...IMPORT_C virtual void ViewConstructL();...};Although the classes in which these functions are defined are differentlyderived in S60 and UIQ, the implementations are broadly similar.The View IDsThe Noughts and Crosses application’s two view IDs are constructed ina standard way from the application’s globally unique UID, defined inoandxapplication.h as:const TUid KUidOandXApp = {0xE04E4143};// 0e 'N' 'A' 'C'IMPLEMENTING THE MCOEVIEW INTERFACE409and two values that are unique within the application.
These values aredefined in oandxdefs.h as:const TUid KUidOandXView= {0x00000001};const TUid KUidOandXHistoryView = {0x00000002};Each view’s ViewId() function constructs and returns the appropriateview ID, for example:TVwsViewId COandXHistoryView::ViewId() const{return TVwsViewId(KUidOandXApp, KUidOandXHistoryView);}View ActivationIn the UIQ version of Noughts and Crosses, the implementation of theGame view’s ViewActivatedL() function is particularly simple:void COandXView::ViewActivatedL(const TVwsViewId& /*aPrevViewId*/,TUid /*aCustomMessageId*/, const TDesC8& /*aCustomMessage*/){MakeVisible(ETrue);DrawNow();}On activation, this view is not supplied with any message data and theview is already fully constructed.
The ID of the previously activated viewcan be ignored, so all we need to do is to ensure that the view is madevisible, and force it to be drawn.The S60 implementation of DoActivateL() follows a similar pattern,with two minor differences.void COandXGameView::DoActivateL(const TVwsViewId& /*aPrevViewId*/,TUid /*aCustomMessageId*/, const TDesC8& /*aCustomMessage*/){__ASSERT_ALWAYS(!iGameViewStacked, Panic(EOandXControlAlreadyStacked));AppUi()->AddToStackL(*this, iContainer);iGameViewStacked = ETrue;iContainer->MakeVisible(ETrue);iContainer->DrawNow();}First, an S60 view owns a control, rather than being derived fromCCoeControl, so the calls to MakeVisible() and DrawNow() haveto be made to the owned control, pointed to by iContainer.410VIEWS AND THE VIEW ARCHITECTUREThe second difference is that, unlike in UIQ, adding an S60 view bymeans of the application UI’s AddViewL() function does not add theview’s control to the control stack.
There are several ways of implementingthe addition to, and subsequent removal from, the control stack. In thiscase, we’ve chosen to call the application UI’s AddToStackL() eachtime a view is activated (and to remove it from the control stack whenthe view is deactivated). The code ensures that the addition is leave-safe(as described in Chapter 12) and that the control isn’t added to the stacktwice.The History view activation, shown here for the UIQ version, needs abit more processing.void COandXHistoryView::ViewActivatedL(const TVwsViewId& /*aPrevViewId*/,TUid aCustomMessageId, const TDesC8& /*aCustomMessage*/){switch (aCustomMessageId.iUid){// switch using the message IDcase EHistoryViewDisplayHistory:ChangeDisplayL(ETrue);break;case EHistoryViewDisplayStats:ChangeDisplayL(EFalse);break;default:// display as last time.ChangeDisplayL(iDisplayingHistory);break ;}}This view needs to take note of the message ID and set itself to displayeither historical or statistical data, using a ChangeDisplay() function.void COandXHistoryView::ChangeDisplayL(TBool aDisplayHistory){iDisplayingHistory = aDisplayHistory;UpdateCommandsL();CreateNewItemsL(); // Called unconditionally, to update data every timeMakeVisible(ETrue);DrawNow();}Apart from the differences mentioned earlier in this section, the S60implementation of DoActivateL() in the History view is very similar.Its version of ChangeDisplayL() doesn’t call UpdateCommandsL(),because of the different way that S60 applications modify their command menus.
We’ll come back to this, and also describe the action ofCreateNewItemsL(), in the History View Content section below.IMPLEMENTING THE MCOEVIEW INTERFACE411View DeactivationWhen a view is activated, the view server calls the ViewDeactivated() function of the view that was previously active. As withViewActivatedL(), the functionality depends on the nature of theapplication. The simplest action is just to make the view invisible, as isdone in the UIQ version of the History view.void COandXHistoryView::ViewDeactivated(){MakeVisible(EFalse);}The debug build of the Game view additionally saves the game state(including data used to construct the History view).void COandXView::ViewDeactivated(){#ifdef _DEBUGOandXAppUi()->SaveGameStateL();#endifMakeVisible(EFalse);}This action is not normally necessary, at least in release builds of UIQ,since the framework will request the application to save its data as andwhen necessary, including when the application closes down.
Includingthis action in the example illustrates a difference between the Game andHistory views: the game data can change while the Game view is active,but never changes while the History view is active.In the S60 version, the implementations of DoDeactivate() aresimilar to those of UIQ’s ViewDeactivated(). The only differences arethe absence of debug code to save the game state and the addition of codeto remove the view’s control from the control stack. The control-stack errorchecking in the S60 code assumes that each call to DoActivateL() ispaired with a later call to DoDeactivate(), and will therefore fail ifthe application activates a window in another application.View ConstructionThe UIQ version of the History view performs the least amount of workpossible in its ConstructL().void COandXHistoryView::ConstructL(){// This should always be called in the concrete view implementations.BaseConstructL();}412VIEWS AND THE VIEW ARCHITECTUREThe bulk of the construction takes place in ViewConstructL().void COandXHistoryView::ViewConstructL(){ViewConstructFromResourceL(R_HISTORY_VIEW_UI_CONFIGURATIONS);iTitle = LocateControlByUniqueHandle<CEikLabel>(EHistoryViewLabelCtrl);for (TInt i=0; i<KNumDataLines; i++){iDataLines[i] = LocateControlByUniqueHandle<CEikLabel>(EHistoryViewData1Ctrl+i);}iEikonEnv->ReadResourceL(iNumGamesText, R_HISTORY_VIEW_GAMES_TEXT);iEikonEnv->ReadResourceL(iNumOWinsText, R_HISTORY_VIEW_OWINS_TEXT);iEikonEnv->ReadResourceL(iNumXWinsText, R_HISTORY_VIEW_XWINS_TEXT);iEikonEnv->ReadResourceL(iNumDrawsText, R_HISTORY_VIEW_DRAWN_TEXT);iEikonEnv->ReadResourceL(iStatOWonText, R_HISTORY_VIEW_O_WINNER_TEXT);iEikonEnv->ReadResourceL(iStatXWonText, R_HISTORY_VIEW_X_WINNER_TEXT);iEikonEnv->ReadResourceL(iStatDrawText, R_HISTORY_VIEW_NO_WINNER_TEXT);iEikonEnv->ReadResourceL(iHistoryTitle, R_HISTORY_VIEW_HISTORY_TITLE);iEikonEnv->ReadResourceL(iStatsTitle, R_HISTORY_VIEW_STATS_TITLE);}Since the UIQ History view’s structure is entirely specified in theapplication’s resource file, the bulk of the construction is done by asingle call to ViewConstructFromResourceL().