This page was last modified 12:59, 23 March 2008.
Составные пользовательские элементы управления, обработка событий
From Forum Nokia Wiki
Contents |
Вступление
В статье приведен пример реализации составных пользовательских элементов управления, а также обработка событий этими элементами.
Исходный код этого проекта: Image:Cc.zip.
CSuperControl
Все пользовательские элементы управления, в данном примере, сосредоточены в классе CSuperControl, который является составным и содержит два вложенных элемента CSmileControl и CSliderGroup. В методе SizeChanged() выполняется расчет координат и размеров составных элементов. В зависимости от соотношений сторон выделенной под CSuperControl области ( ее можно получить с помощью метода Rect() ), составные элементы располагаются либо сверху вних, либо слева направо. Данный класс поддерживает обработку событий клавиатуры ( реализован метод OfferKeyEventL( ... ) ), передавая поступающие события вложенному элементу CSliderGroup. Кроме того, данный класс отрисовывает фон текущей темы, учитываю при этом возможные динамические изменения размеров.
CSmileControl
Класс CSmileControl не поддерживает обработку событий, является нефокусируемым - это достигается вызовом функции SetNonFocusing(). Основное предназначение этого элемента - отрисовка смайла с учетом заданных коэффициентов деформации (iDeltaX, iDeltaY).
CSliderGroup
Элемент CSliderGroup является составным и содержит два слайдера CSimpleSlider, с помощью которых можно задавать коэффициенты деформации смайла по ширине (dX) и по высоте (dY). CSliderGroup поддерживает обработку событий, для этого в нем реализован метод OfferKeyEventL( ... ), который либо меняет сфокусированный в данный момент слайдер (если нажато вправо или влево), либо передает событие на обработку активному слайдеру. Метод SizeChanged() данного класса расчитывает и задает координаты вложенных слайдеров.
CSimpleSlider
Класс CSimpleSlider является простым пользовательским элементом управления, позволяет наглядно задавать значения коэффициентов деформации ( от 0 - до 100 % ). Обрабатывает события, позволяя тем самым менять текущее значение.
Определения классов
#include <akncontrol.h> // Простой элемент управления - отрисовывает смайл на экране class CSmileControl : public CAknControl { public: void ConstructL( const CCoeControl* aParent ); void SetDelta( TInt aDeltaX, TInt aDeltaY ); private: void Draw( const TRect& aRect ) const; private: // коэффициенты деформации ширины и высоты TInt iDeltaX, iDeltaY; }; // Слайдер - используется для задания значений деформации размеров смайла class CSimpleSlider : public CAknControl { public: CSimpleSlider( const TDesC& aLabel, TInt aValue ); void ConstructL( const CCoeControl* aParent ); TSize MinimumSize(); TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType); TReal Value() { return iValue; } private: void Draw( const TRect& aRect ) const; private: TBuf<50> iLabel; // подпись TInt iValue; // текущее значение [ 0; 100 ] // параметры для отрисовки слайдера CFont* font; TInt iBvThickness, iDelimiter, iMaxLbWidth; }; // Составной элемент управления - содержит два слайдера // для задания значений коэффициентов деформации ширины и высоты // смайла class CSliderGroup : public CAknControl { public: void ConstructL( const CCoeControl* aParent, TInt aValueX, TInt aValueY ); TSize MinimumSize(); TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType); void Values( TInt& aDx, TInt& aDy ); protected: void SizeChanged(); private: CSimpleSlider *iSliderX, *iSliderY, *iActiveSlider; // активный в данный момент сладйер }; class CAknsBasicBackgroundControlContext; // Составной элемент - содержит два элемента управления: // - смайл // - группу слайдеров class CSuperControl : public CAknControl { public: ~CSuperControl(); void ConstructL( const CCoeControl* aParent ); TSize MinimumSize(); TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType); TTypeUid::Ptr MopSupplyObject(TTypeUid aId); void Calc(); protected: void SizeChanged(); private: void Draw( const TRect& aRect ) const; private: CAknsBasicBackgroundControlContext* iBackground; CSmileControl* iSmile; CSliderGroup* iSliderGroup; };
Реализация классов
#include "SmileControl.h" #include <aknutils.h> #include <AknsDrawUtils.h> #include <AknsBasicBackgroundControlContext.h> void CSmileControl :: ConstructL( const CCoeControl* aParent ) { // при создании ширина и высота берутся без изменений (100%) iDeltaX = iDeltaY = 100; SetContainerWindowL( *aParent ); ActivateL(); } void CSmileControl::SetDelta( TInt aDeltaX, TInt aDeltaY ) { // задание значений коэффициентов деформации iDeltaX = aDeltaX; iDeltaY = aDeltaY; } void CSmileControl ::Draw( const TRect& /*aRect*/ ) const { TRect rect = Rect(); CWindowGc& gc = SystemGc(); // учет коэффициентов деформации rect.Shrink( ( rect.Width() - rect.Width() * iDeltaX / 100 ) / 2 + 5, ( rect.Height() - rect.Height() * iDeltaY / 100 ) / 2 + 5 ); // отрисовка смайла gc.SetPenStyle( CGraphicsContext :: ESolidPen ); gc.SetPenColor( KRgbBlack ); gc.SetPenSize( TSize( 5, 5 ) ); gc.SetBrushStyle( CGraphicsContext :: ESolidBrush ); gc.SetBrushColor( KRgbYellow ); gc.DrawEllipse( rect ); // глаза gc.SetBrushColor( KRgbBlack ); TRect eyeRect = rect; eyeRect.Resize( -rect.Width() * 0.8, -rect.Height() * 0.8 ); eyeRect.Move( rect.Width() / 4, rect.Height() / 4 ); gc.DrawEllipse( eyeRect ); eyeRect.Move( rect.Width() / 2 - eyeRect.Width(), 0 ); gc.DrawEllipse( eyeRect ); // рот gc.SetPenSize( TSize( 5, 5 ) ); rect.Move( 0, rect.Height() / 7 ); rect.Shrink( rect.Width() * 0.2, rect.Height() * 0.3 ); gc.DrawArc( rect, TPoint( rect.iTl.iX, rect.iTl.iY + rect.Height() * 0.6 ), TPoint( rect.iTl.iX + rect.Width(), rect.iTl.iY + rect.Height()* 0.6 ) ); } CSimpleSlider :: CSimpleSlider( const TDesC& aLabel, TInt aValue ) : iBvThickness( 1 ), iDelimiter( 2 ), iValue( aValue ) { iLabel = aLabel; } void CSimpleSlider :: ConstructL( const CCoeControl* aParent ) { // для отображения метки используется один из системных шрифтов font = ( CFont* )AknLayoutUtils::FontFromId( EAknLogicalFontPrimarySmallFont ); // расчет максимальной длины итоговой метки TBufC<50> tmpLabel; TPtr tmpPtr = tmpLabel.Des(); tmpPtr.Append( iLabel ); tmpPtr.Append( ':' ); tmpPtr.Append( ' ' ); tmpPtr.AppendNum( 100 ); tmpPtr.Append( '%' ); iMaxLbWidth = font->TextWidthInPixels( tmpLabel ), SetContainerWindowL( *aParent ); ActivateL(); } TSize CSimpleSlider :: MinimumSize() { // минимальные размеры слайдера зависят от шрифта и макс. длины метки return TSize( iBvThickness * 4 + iDelimiter * 4 + iMaxLbWidth, iBvThickness * 4 + iDelimiter * 5 + font->HeightInPixels() ); } TKeyResponse CSimpleSlider :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode /*aType*/ ) { // реакция на нажатие - изменение текущего значения TKeyResponse response = EKeyWasConsumed; TInt modifiersMask = EModifierShift | EModifierAlt | EModifierCtrl; switch( aKeyEvent.iCode ) { case EKeyLeftArrow : iValue -= aKeyEvent.iModifiers & modifiersMask ? 10 : 1; if( iValue < 0 ) iValue = 0; break; case EKeyRightArrow : iValue += aKeyEvent.iModifiers & modifiersMask ? 10 : 1; if( iValue > 100 ) iValue = 100; break; default : response = EKeyWasNotConsumed; break ; } return response; } void CSimpleSlider :: Draw( const TRect& /*aRect*/ ) const { // отрисовка слайдера TRect rect = Rect(); TInt runner_w = iMaxLbWidth + 2 * iDelimiter + 2 * iBvThickness, runner_h = font->HeightInPixels() + 2 * iDelimiter + 2 * iBvThickness, x_offs = rect.iTl.iX + iBvThickness + runner_w / 2; TPoint line_A( x_offs + 2, rect.iTl.iY + rect.Height() / 2 ), line_B( rect.iBr.iX - runner_w / 2 - 2, line_A.iY ); TInt runner_c = line_A.iX + iValue * ( line_B.iX - line_A.iX ) / 100; TRect runner_rect( runner_c - runner_w / 2, line_A.iY - runner_h / 2, runner_c + runner_w / 2, line_A.iY + runner_h / 2 ); // формирование текста надписи TBufC<50> tmpLabel; TPtr tmpPtr = tmpLabel.Des(); tmpPtr.Append( iLabel ); tmpPtr.Append( ':' ); tmpPtr.Append( ' ' ); tmpPtr.AppendNum( iValue ); tmpPtr.Append( '%' ); TRgb penColor, brushColor; if( IsFocused() ) { // если слайдер сфокусирован penColor = KRgbBlack; brushColor = KRgbCyan; } else { penColor = KRgbWhite; brushColor = KRgbGray; } CWindowGc& gc = SystemGc(); gc.SetPenSize( TSize( iBvThickness, iBvThickness ) ); gc.SetPenStyle( CGraphicsContext :: ENullPen ); gc.SetPenColor( penColor ); gc.SetBrushStyle( CGraphicsContext :: ESolidBrush ); gc.SetPenStyle( CGraphicsContext :: ESolidPen ); gc.DrawLine( line_A, line_B ); gc.SetBrushColor( brushColor ); gc.DrawRoundRect( runner_rect, TSize( 4, 4 ) ); gc.UseFont( font ); gc.DrawText( tmpLabel, TPoint( runner_rect.iTl.iX + iDelimiter + iBvThickness, runner_rect.iBr.iY - iDelimiter - iBvThickness ) ); } void CSliderGroup :: ConstructL( const CCoeControl* aParent, TInt aValueX, TInt aValueY ) { SetContainerWindowL( *aParent ); // инициализация массива для хранения элементов InitComponentArrayL(); // создание и регистрация в массиве вложенных элементов управления _LIT( KDeltaX, "dX" ); iSliderX = new (ELeave)CSimpleSlider( KDeltaX, aValueX ); Components().AppendLC( iSliderX ); iSliderX->ConstructL( this ); CleanupStack::Pop( iSliderX ); _LIT( KDeltaY, "dY" ); iSliderY = new (ELeave)CSimpleSlider( KDeltaY, aValueY ); Components().AppendLC( iSliderY ); iSliderY->ConstructL( this ); CleanupStack::Pop( iSliderY ); iSliderX->SetFocus( ETrue, ENoDrawNow ); iActiveSlider = iSliderX; } TSize CSliderGroup :: MinimumSize() { // слайдеры всегда располагаются друг под другом TSize sz = iSliderX->MinimumSize(); sz.iHeight *= 2; return sz; } TKeyResponse CSliderGroup :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType) { TKeyResponse response; if( aKeyEvent.iCode == EKeyUpArrow || aKeyEvent.iCode == EKeyDownArrow ) { // изменение фокуса iActiveSlider->SetFocus( EFalse, ENoDrawNow ); if( iActiveSlider == iSliderX ) iActiveSlider = iSliderY; else iActiveSlider = iSliderX; iActiveSlider->SetFocus( ETrue, EDrawNow ); response = EKeyWasConsumed; } else // событие передается на обработку текущему слайдеру response = iActiveSlider->OfferKeyEventL( aKeyEvent, aType ); return response; } void CSliderGroup :: SizeChanged() { // слайдеры располагаются друг под другом // занимают равные области TRect rect = Rect(); TInt sliderHeight = rect.Height() / 2; TRect xRect = rect; xRect.SetHeight( sliderHeight ); TRect yRect = rect; yRect.iTl.iY += sliderHeight; yRect.SetHeight( sliderHeight ); iSliderX->SetRect( xRect ); iSliderY->SetRect( yRect ); } void CSliderGroup :: Values( TInt& aDx, TInt& aDy ) { // получение текущих значений aDx = iSliderX->Value(); aDy = iSliderY->Value(); } CSuperControl :: ~CSuperControl() { delete iBackground; iBackground = NULL; } void CSuperControl :: Draw( const TRect& /*aRect*/ ) const { // отрисовка фона текущей темы TRect rect = Rect(); CWindowGc& gc = SystemGc(); MAknsSkinInstance* skin = AknsUtils::SkinInstance(); MAknsControlContext* cc = AknsDrawUtils::ControlContext( this ); AknsDrawUtils::Background( skin, cc, this, gc, rect ); } void CSuperControl :: SizeChanged() { TRect r = Rect(); // изменение размеров нужно учесть для отображения фона if( iBackground ) iBackground->SetRect( r ); TRect r2 = r; TReal ratio = ( ( TReal ) r.Width() ) / r.Height(); // в зависимости от соотношений размеров области отрисовка элементов if( ratio < 1.5 ) { // сверху вниз TSize sz = iSliderGroup->MinimumSize(); r.SetHeight( r.Height() - sz.iHeight ); r2.iTl.iY = r.iTl.iY + r.Height(); r2.SetHeight( sz.iHeight ); } else { // слева направо r.SetWidth( r.Width() / 2 ); r2.iTl.iX += r.Width(); r2.SetWidth( r.Width() ); } iSmile->SetRect( r ); iSliderGroup->SetRect( r2 ); } void CSuperControl :: ConstructL( const CCoeControl* aParent ) { if( aParent == NULL ) CreateWindowL(); else SetContainerWindowL( *aParent ); // поддержка тем iBackground = CAknsBasicBackgroundControlContext :: NewL ( KAknsIIDQsnBgAreaMain, Rect(), EFalse ); // инициализация массива и добавление составных компонентов InitComponentArrayL(); iSmile = new (ELeave) CSmileControl(); Components().AppendLC( iSmile ); iSmile->ConstructL( this ); CleanupStack::Pop( iSmile ) ; iSliderGroup = new (ELeave)CSliderGroup(); Components().AppendLC( iSliderGroup ); iSliderGroup->ConstructL( this, 100, 100 ); CleanupStack::Pop( iSliderGroup ) ; iSliderGroup->SetFocus( ETrue, ENoDrawNow ); iSmile->SetNonFocusing(); ActivateL(); } TTypeUid::Ptr CSuperControl :: MopSupplyObject(TTypeUid aId) { // поддержка тем return iBackground != NULL ? MAknsControlContext :: SupplyMopObject( aId, iBackground ) : CAknControl :: MopSupplyObject( aId ); } TSize CSuperControl :: MinimumSize() { // в данном примере используется вся main pane TSize mainPaneSize; AknLayoutUtils :: LayoutMetricsSize( AknLayoutUtils::EMainPane, mainPaneSize ); return mainPaneSize; } TKeyResponse CSuperControl :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType) { // событие может обрабатываться только слайдерами return iSliderGroup->OfferKeyEventL( aKeyEvent, aType ); } void CSuperControl :: Calc() { // установка коэффициентов деформации из текущих знечений слайдеров TInt dx, dy; iSliderGroup->Values( dx, dy ); iSmile->SetDelta( dx, dy ); }
Пример использования
Класс CSuperControl можно использовать, например, в CAknDailog следующим образом:
Задание ресурса для диалога
RESOURCE DIALOG r_smile_dialog { flags = EEikDialogFlagNoDrag | EEikDialogFlagFillAppClientRect | EEikDialogFlagWait | EEikDialogFlagCbaButtons | EEikDialogFlagNoTitleBar; buttons = R_AVKON_SOFTKEYS_OK_EMPTY; }
Задание класса для диалога
#include <akndialog.h> class CSuperControl; class CSmileDialog : public CAknDialog { private: SEikControlInfo CreateCustomControlL(TInt aControlType); void PreLayoutDynInitL(); TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType); private: CSuperControl* iMyControl; };
Реализация класса диалога
#include "SmileDialog.h" #include "SmileControl.h" #include <aknutils.h> _LIT( KModule, "CSmileDialog :: CreateCustomControlL" ); SEikControlInfo CSmileDialog :: CreateCustomControlL( TInt aControlType ) { __ASSERT_ALWAYS( aControlType == EAknCtLastControlId, User::Panic( KModule, KErrArgument ) ); iMyControl = new (ELeave) CSuperControl; SEikControlInfo controlInfo; controlInfo.iFlags = EEikControlHasEars; controlInfo.iTrailerTextId = 0; controlInfo.iControl = iMyControl; STATIC_CAST(CSuperControl*, controlInfo.iControl)->SetContainerWindowL( *this ); return controlInfo; } void CSmileDialog :: PreLayoutDynInitL() { CAknDialog :: PreLayoutDynInitL(); const TInt KSmileControlId = 1; CCoeControl* ctrl = CreateLineByTypeL( KNullDesC, KSmileControlId, EAknCtLastControlId, NULL ); iMyControl = static_cast<CSuperControl*>( ctrl ); iMyControl->ConstructL( this ); } TKeyResponse CSmileDialog :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType) { TKeyResponse response = iMyControl->OfferKeyEventL( aKeyEvent, aType ); if( response == EKeyWasConsumed ) { iMyControl->Calc(); DrawNow(); } return response; }
Следует отметить, что в данном примере каждое обработанное событие приводит к полной перерисовке класса CSuperControl, что неоптимально.
| Related Discussions | ||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Отслеживание событий журнала звонков | Den123 | Russian Developer Forum - Форум Российских разработчиков | 2 | 2008-03-11 06:09 |
| Оффтоп | Cagemts | Russian Developer Forum - Форум Российских разработчиков | 107 | 2008-06-24 16:48 |
| Проблемы с CaptureKeyXXXXX | truf | Russian Developer Forum - Форум Российских разработчиков | 1 | 2008-02-07 12:16 |
| enumerated text popup add item | b_monkey | Russian Developer Forum - Форум Российских разработчиков | 10 | 2007-12-14 10:03 |
| прозрачность alpha параметр | Versoul | Russian Developer Forum - Форум Российских разработчиков | 19 | 2008-06-04 15:04 |

