开启single-tap - 解决问题的另一种思路

今天遇到一个问题,客户想要在选取图片时能够支持single-tap ,即单击一个就代表选择,而不用像以前那样单击一次代表选中,再单击一次才代表选择。
在symbian平台中我们可以直接用在AppUi调用BaseConstructL函数来开启,就像下面这样:

BaseConstructL(CAknAppUi::EAknEnableSkin | CAknAppUi::EAknSingleClickCompatible);

但在Qt平台中,由于UI的启动直接被封装起来,也没有接口供我们调用,这时让我们开启single-tap简直有点不可能。
让我们先来看看Qt中是怎么写的:

void QS60MainAppUi::ConstructL() { // Cone's heap and handle checks on app destruction are not suitable for Qt apps, as many // objects can still exist in static data at that point. Instead we will print relevant information // so that comparative checks may be made for memory leaks, using ~SPrintExitInfo in corelib. iEikonEnv->DisableExitChecks(ETrue); // Initialise app UI with standard value. // ENoAppResourceFile and ENonStandardResourceFile makes UI to work without // resource files in most SDKs. S60 3rd FP1 public seems to require resource file // even these flags are defined TInt flags = CEikAppUi::ENoScreenFurniture | CEikAppUi::ENonStandardResourceFile; #ifdef Q_WS_S60 flags |= CAknAppUi::EAknEnableSkin; // After 5th Edition S60, native side supports animated wallpapers. // However, there is no support for that feature on Qt side, so indicate to // native UI framework that this application will not support background animations. if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) flags |= KAknDisableAnimationBackground; #endif BaseConstructL(flags); }

看到没?全是写的死死的。其实就是在symbian也没有可以修改flags的接口,只有查询的接口,你可以调用IsSingleClickCompatible(),IsTouchCompatible()等来查询,但没有修改的接口。

怎么办呢?看起来简单就是不可能了。
看看symbian的源码吧,也许可以找到出路。

EXPORT_C void CAknAppUiBase::BaseConstructL( TInt aAppUiFlags ) { #ifdef AVKON_RDEBUG_INFO RDebug::Print(_L("CAknAppUiBase::BaseConstructL()")); #endif // note that the extension may have already been created by some other API call // for example SetLocalUiZoomL if(!iAppUiBaseExtension) { // To avoid having to test on this extension everywhere, we need to Leave if the alloc fails. TRAPD( err,iAppUiBaseExtension = new(ELeave) CAknAppUiBaseExtension); // ensure that CCoeEnv takes ownership before leaving SetAppUiAndLeaveIfErrorL( err ); } // enable receipt of changes to screen state iEikonEnv->RootWin().EnableScreenChangeEvents(); iAppUiBaseExtension->iApplicationRect = iCoeEnv->ScreenDevice()->SizeInPixels(); if (!iAppUiBaseExtension->iFeedbackAdaptation ) { iAppUiBaseExtension->iFeedbackAdaptation = CTouchFeedbackAdaptation::NewL(); } #ifdef AVKON_RDEBUG_INFO RDebug::Print(_L("CAknAppUiBase::BaseConstructL() application rect set")); #endif CRepository* repository = NULL; TRAPD(ret, repository = CRepository::NewL(KCRUidAvkon)); if (ret == KErrNone) { ret = repository->Get(KAknDefaultAppOrientation, iAppUiBaseExtension->iDefaultRotation); ret = repository->Get(KAknMiddleSoftkeyEnabled, iAppUiBaseExtension->iMSKEnabled); } #ifdef AVKON_RDEBUG_INFO RDebug::Print(_L("CAknAppUiBase::BaseConstructL() orientation ok")); #endif TInt orientationFlags = aAppUiFlags; CRepository* defaultOrientationCr = NULL; TRAPD( err, defaultOrientationCr = CRepository::NewL( KCRUidDefaultAppOrientation ) ); if (err == KErrNone) { // Repository found, check if the orientation has been set TInt value = 0; err = defaultOrientationCr->Get( RProcess().SecureId().iId, value ); if( err == KErrNone && value ) { // Application key found and orientation has been set orientationFlags = value; } delete defaultOrientationCr; defaultOrientationCr = NULL; } iAknFlags.Assign(EOrientationSpecified, orientationFlags&EAppOrientationSpecifiedFlag); iAknFlags.Assign(EOrientationLandscape, orientationFlags&EAppOrientationLandscapeFlag); iAknFlags.Assign(EOrientationAutomatic, orientationFlags&EAppOrientationAutomaticFlag); iAknFlags.Assign(EMSKEnabled, aAppUiFlags&EAknEnableMSKflag); iAknFlags.Assign( ESingleClickCompatible, aAppUiFlags & EAknSingleClickCompatibleFlag ); if ( aAppUiFlags & EAknTouchCompatibleFlag ) { iAknFlags.Set( ETouchCompatible ); } else if ( aAppUiFlags & EAknSingleClickCompatibleFlag ) { // If application is single click compatible then it needs to // be also touch compatible, so set that flag also if application // has forgotten. iAknFlags.Set( ETouchCompatible ); } else { // Set the touch compatible flag for all applications that // reside on the ROM, since all platform applications should // be touch compatible. RProcess process; TFileName fileName = process.FileName(); _LIT( KRomDrive, "z:" ); if ( fileName.FindF( KRomDrive ) == 0 ) { iAknFlags.Set( ETouchCompatible ); } } AknItemActionMenuRegister::SetConstructingMenuBarOwnerL( this ); #ifdef AVKON_RDEBUG_INFO RDebug::Print(_L("Entering CEikAppUi::BaseConstructL()")); #endif // Touch compatibility mode. Change application screen mode before // a call to CEikAppUi::BaseConstructL(). This way application // starts in a correct screen mode and there are no // HandleResourceChangeL() calls due to screen mode change. TInt compaScreenMode = KErrNotFound; TBool screenModeChanged = EFalse; // Check if the application doesn't need compa-mode or compa-mode is // disabled if (CAknCompaIf::IsNeeded(aAppUiFlags, repository)) { // Compa-mode may be needed. Instantiate ecom plugin to check // further. Change application screen mode if compa-mode is needed. TBool isConsoleApp; TRAPD(compaModeErr, iAppUiBaseExtension->iCompaIf = CAknCompaIf::NewL(); // SetCompaAppScreenModeL() returns KErrNotFound if compa-mode // is not needed compaScreenMode = iAppUiBaseExtension->iCompaIf->SetCompaAppScreenModeL( screenModeChanged, isConsoleApp, aAppUiFlags, *this, *iCoeEnv, *repository)); delete repository; repository = NULL; // Ensure that CCoeEnv takes ownership before leaving SetAppUiAndLeaveIfErrorL(compaModeErr); if (compaScreenMode == KErrNotFound) { delete iAppUiBaseExtension->iCompaIf; iAppUiBaseExtension->iCompaIf = NULL; } else { if (isConsoleApp) { // If console application is run in compa-mode, allow status // bar to be created. Status bar is required to be able to set // application screen mode. aAppUiFlags &= ~CEikAppUi::ENoScreenFurniture; } } } delete repository; repository = NULL; CEikAppUi::BaseConstructL( aAppUiFlags ); if ( !iAppUiBaseExtension->iDiallerLauncher ) { iAppUiBaseExtension->iDiallerLauncher = CAknDiallerLauncher::NewL(iAppUiBaseExtension->iCompaIf); AddToStackL( iAppUiBaseExtension->iDiallerLauncher, -100, ECoeStackFlagRefusesFocus ); } iAvkonEnv->TransitionEvent(KAknTransitionEventFlags); //look at fullsceen flags #ifdef AVKON_RDEBUG_INFO RDebug::Print(_L("CEikAppUi::BaseConstructL() out ")); #endif if (iEikonEnv->StartedAsServerApp()) EnableExternalViewSwitches(EFalse); // Note: iKeySounds is left uninitialized // iEventMonitor = CAknWsEventMonitor::NewL(); if(!(aAppUiFlags & EAknExplicitStartupEffectCompletion)) //does start idle time iAvkonEnv->TransitionEvent(AknTransEffect::EAppStartComplete);//normal case // Touch compatibility mode if (iAppUiBaseExtension->iCompaIf) { // Create compatibility mode keyboard and make it visible in the case // of a normal application. Compa-keyboard is also created for servers // that display global/notes notifications but left invisible. iAppUiBaseExtension->iCompaIf->CreateKbL(compaScreenMode, screenModeChanged); // Tactile feedback needs to be informed about layout change, because // otherwise it won't get correct information about pen support // (Tactile Feedback was instantiated earlier in this function, when // pen was still enabled). iAppUiBaseExtension->iFeedbackAdaptation->LayoutChanged(); } else { // Pointer event modifier is not needed while in compatibility mode so // its creation is delayed until here. // // Leaves during construction are ignored since pointer event modifier // isn't mandatory i.e. the device should be usable also without it. TRAP_IGNORE( iAppUiBaseExtension->iPointerEventModifier = CAknPointerEventModifier::NewL() ) } }

哦,原来管Single-tap的就是下面这句了:

iAknFlags.Assign( ESingleClickCompatible, aAppUiFlags & EAknSingleClickCompatibleFlag );

如果我们能获得iAknFlags就好办了,可是symbian并没有提供接口给我们。嘿嘿,不过我们还是有办法的,
那就是指针,CAknAppUiBase的运行时的实例我们是可以获得的,只要我们可以算出iAknFlags在CAknAppUiBase类中的位置,用指针只向它不就可以了嘛。
哈哈,真是无所不用其极啊。
让我们看看如何能指向它呢,经过计算得出如下代码(注意这里使用的是GCC编译器):

TBitFlags *flags = (TBitFlags *)((TInt *)iAvkonAppUiBase + 18);

18就是iAknFlags在CAknAppUiBase类中的offset了,iAvkonAppUiBase我就不用说了。
这样我们就顺利得到了flags,修改它就很容易了,通过以下代码我们就可以开启single-tap了:

flags->Set(14);

其中14是根据symbian源码中找到的对应数字,这里你也可以用enum,这样看起来可读性会更好一些。

以上是解决symbian开发中解决问题的一个思路,虽然看起来并不是那么的友好,但实在是没有办法啊,symbian没有公开的API接口,
同时Qt也不够完善,但我们的项目还是要做的,用邓爷爷的话说能抓到老鼠的猫就是好猫,哈哈。其实在我们探究出这个方法的过程中我们也可以学到很多
既熟悉了symbian源码又增进了对C++的认识,不是嘛,哈哈。

你可能感兴趣的:(Qt,Symbian)