주요 내용으로 건너뛰기

튜토리얼3: 멀티 스크린을 사용하는 애플리케이션

이 튜토리얼에서는 애플리케이션에서 다수의 스크린을 생성하여 두 스크린 사이에서 데이터를 공유하는 방법에 대해 알아보겠습니다. 먼저 시계를 시뮬레이션하는 애플리케이션을 생성하여 한 스크린에서 시간과 분을 설정한 후 시계가 있는 다른 스크린으로 시간을 전달하려고 합니다.

또한 TouchGFX Designer에서 스크린 변경과 같은 인터랙션에 따라 애니메이션을 생성하는 방법에 대해서도 알아보겠습니다.

튜토리얼에 사용되는 이미지는 이 링크에서 다운로드할 수 있습니다. 다운로드한 파일을 디스크 디렉터리에 압축 해제합니다.

1단계: 스크린 2개 설정하기

튜토리얼 2에서 한 것처럼 새 프로젝트를 생성합니다. 그래픽이 480x272 해상도에 맞춰 다시 한 번 디자인됩니다. "STM32F746G Discovery Kit"의 애플리케이션 템플릿에서 프로젝트를 생성할 경우 프로젝트는 이러한 해상도를 갖게 됩니다. 데모 보드의 해상도가 다를 경우 해상도에 맞춰 그래픽을 수정해야 할 수도 있습니다. 데모 보드가 없다면 "시뮬레이터" 애플리케이션 템플릿에서 생성하여 해상도를 480x272로 입력해야 합니다.

Screen1 설정하기

첫 번째로 생성하는 스크린은 Screen1입니다. Screen1에서는 시간/분 값을 설정하여 시계가 있는 스크린으로 전송할 수 있습니다. Screen1은 아래와 같이 원하는 시간을 설정할 수 있는 텍스트 박스 2개로 구성되어 있습니다. 박스의 값을 조정할 때는 위/아래 화살표를 사용합니다. 이후 선택한 값을 저장하여 시계로 전달할 때는 각 박스 아래 있는 Save 버튼이 사용됩니다. 마지막으로 Clock 버튼을 누르면 시계가 있는 Screen2로 전환됩니다.

먼저 애플리케이션에 배경과 텍스트 영역을 삽입합니다. 그런 다음 위젯을 삽입하고 아래 그림 및 표와 같이 속성을 업데이트합니다.

배경 및 텍스트 영역의 위치가 강조 표시된 Screen1의 스크린샷

위치위젯속성
1이미지
  • 이름: background
  • Location:
    • X: 0, Y: 0
  • 이미지: background_Screen1.png
2TextArea
  • 이름: textAreaHourCaption
  • Location:
    • X: 86, Y: 46
    • W: 85, H: 24
  • Text:
    • Text: Hour
    • Typography: 20px
    • Alignment: 중앙
  • Appearance:
    • Color: #FFABABAB
3TextArea
  • 이름: textAreaHour
  • Location:
    • X: 87, Y: 70
    • W: 83, H: 50
  • Text:
    • Text: <value>
    • Wildcard 1:
      • 와일드카드 버퍼 사용: 예
      • 초기 값: 00
      • 버퍼 크기: 3
    • Typography: 40px
    • Alignment: 중앙
  • Appearance:
    • Color: #FFABABAB
4TextArea
  • 이름: textAreaMinuteCaption
  • Location:
    • X: 309, Y: 46
    • W: 85, H: 24
  • Text:
    • Text: Minute
    • Typography: 20px
    • Alignment: 중앙
  • Appearance:
    • Color: #FFABABAB
5TextArea
  • 이름: textAreaMinute
  • Location:
    • X: 311, Y: 70
    • W: 83, H: 50
  • Text:
    • Text: <value>
    • Wildcard 1:
      • 와일드카드 버퍼 사용: 예
      • 초기 값: 00
      • 버퍼 크기: 3
    • Typography: 40px
    • Alignment: 중앙
  • Appearance:
    • Color: #FFABABAB

그런 다음 아래 그림 및 표와 같이 버튼을 애플리케이션에 삽입합니다.

버튼의 위치가 강조 표시된 Screen1의 스크린샷

위치위젯속성
1버튼
  • 이름: buttonHourUp
  • Location:
    • X: 184, Y: 51
  • Image:
    • Released Image: Up_arrow.png
    • Pressed Image: Up_arrow_pressed.png
2버튼
  • 이름: buttonHourDown
  • Location:
    • X: 184, Y: 93
  • Image:
    • Released Image: Down_arrow.png
    • Pressed Image: Down_arrow_pressed.png
3버튼
  • 이름: buttonMinuteUp
  • Location:
    • X: 266, Y: 51
  • Image:
    • Released Image: Up_arrow.png
    • Pressed Image: Up_arrow_pressed.png
4버튼
  • 이름: buttonMinuteDown
  • Location:
    • X: 266, Y: 93
  • Image:
    • Released Image: Down_arrow.png
    • Pressed Image: Down_arrow_pressed.png
5라벨 버튼
  • 이름: buttonSaveHour
  • Location:
    • X: 80, Y: 137
  • Text:
    • Text: Save
    • Typography: 25px
    • Alignment: 중앙
  • Text Appearance:
    • Released Color: #FF424242
    • Pressed Color: #FFA6A6A6
  • Image:
    • Released Image: btn_round.png
    • Pressed Image: btn_round_pressed.png
6라벨 버튼
  • 이름: buttonSaveMinute
  • Location:
    • X: 303, Y: 137
  • Text:
    • Text: Save
    • Typography: 25px
    • Alignment: 중앙
  • Text Appearance:
    • Released Color: #FF424242
    • Pressed Color: #FFA6A6A6
  • Image:
    • Released Image: btn_round.png
    • Pressed Image: btn_round_pressed.png
7라벨 버튼
  • 이름: buttonClock
  • Location:
    • X: 192, Y: 204
  • Text:
    • Text: Clock
    • Typography: 25px
    • Alignment: 중앙
  • Text Appearance:
    • Released Color: #FF424242
    • Pressed Color: #FFA6A6A6
  • Image:
    • Released Image: btn_round.png
    • Pressed Image: btn_round_pressed.png

그래픽 요소가 설정되었으면 이제 선택한 값을 조정하여 저장할 수 있는 버튼 트리거를 추가해야 합니다.

인터랙션속성
시간 상향 조정 버튼 클릭
  • Trigger: Button is clicked
  • Clicked Source: buttonHourUp
  • Action: Call new virtual function
  • 함수 이름: buttonHourUpClicked
시간 하향 조정 버튼 클릭
  • Trigger: Button is clicked
  • Clicked Source: buttonHourDown
  • Action: Call new virtual function
  • 함수 이름: buttonHourDownClicked
분 상향 조정 버튼 클릭
  • Trigger: Button is clicked
  • Clicked Source: buttonMinuteUp
  • Action: Call new virtual function
  • 함수 이름: buttonMinuteUpClicked
분 하향 조정 버튼 클릭
  • Trigger: Button is clicked
  • Clicked Source: buttonMinuteDown
  • Action: Call new virtual function
  • 함수 이름: buttonMinuteDownClicked
시간 저장 버튼 클릭
  • Trigger: Button is clicked
  • Clicked Source: buttonSaveHour
  • Action: Call new virtual function
  • 함수 이름: buttonSaveHourClicked
분 저장 버튼 클릭
  • Trigger: Button is clicked
  • Clicked Source: buttonSaveMinute
  • Action: Call new virtual function
  • 함수 이름: buttonSaveMinuteClicked

"Generate Code" 또는 "Run Simulator"를 누르면 지정된 가상 함수가 Designer에서 생성됩니다. 먼저 화살표 버튼 함수 4개를 통합하겠습니다. 또한 시간 값과 분 값을 추적하기 위해 카운터 2개도 추가됩니다.

이제 다음 코드를 추가합니다.

Screen1View.hpp
...
public:
...
virtual void buttonHourUpClicked();
virtual void buttonHourDownClicked();
virtual void buttonMinuteUpClicked();
virtual void buttonMinuteDownClicked();

protected:
int16_t hour;
int16_t minute;
...

화살표를 누르면 해당하는 값이 시계 값의 범위 안에 들도록 바뀝니다.

함수 4개의 로직을 다음과 같이 추가해야 합니다.

TouchGFX/gui/src/screen1_screen/Screen1View.cpp
Screen1View::Screen1View()
: hour(0), minute(0) //clear the new counters
{
}
...
void Screen1View::buttonHourUpClicked()
{
hour = (hour + 1) % 24; // Keep new value in range 0..23
Unicode::snprintf(textAreaHourBuffer, TEXTAREAHOUR_SIZE, "%02d", hour);
textAreaHour.invalidate();
}

void Screen1View::buttonHourDownClicked()
{
hour = (hour + 24 - 1) % 24; // Keep new value in range 0..23
Unicode::snprintf(textAreaHourBuffer, TEXTAREAHOUR_SIZE, "%02d", hour);
textAreaHour.invalidate();
}

void Screen1View::buttonMinuteUpClicked()
{
minute = (minute + 1) % 60; // Keep new value in range 0..59
Unicode::snprintf(textAreaMinuteBuffer, TEXTAREAMINUTE_SIZE, "%02d", minute);
textAreaMinute.invalidate();
}

void Screen1View::buttonMinuteDownClicked()
{
minute = (minute + 60 - 1) % 60; // Keep new value in range 0..59
Unicode::snprintf(textAreaMinuteBuffer, TEXTAREAMINUTE_SIZE, "%02d", minute);
textAreaMinute.invalidate();
}

Screen2 설정하기

Screen2 스크린샷을 보면 Screen1에서 저장한 값부터 시작되는 시계가 있습니다. Screen2는 시계 외에도 시계를 중심으로 애니메이션 처리되는 원으로 구성되며, 이 애니메이션으로 시계가 작동한다는 것을 알 수 있습니다. 마지막으로 Screen1으로 돌아갈 수 있도록 스크린을 Screen1으로 변경하는 버튼이 구현되어 있습니다.

시뮬레이터에서 애플리케이션을 실행한 Screen2의 스크린샷

그래픽 요소를 Screen2에 추가하려면 먼저 새로운 스크린을 생성해야 합니다. 아래 그림과 같이 TouchGFX Designer에서 “Add Screen” 버튼(1)을 사용해 새로운 스크린을 생성합니다.

1

TouchGFX Designer에서 Add Screen 버튼의 위치

"screen2"로 전환할 때 시계는 스크린 외부 왼쪽에서, 그리고 원은 스크린 외부 오른쪽에서 내부 제자리로 이동하도록 애니메이션 처리하려고 합니다. 따라서 처음에는 두 위젯이 TouchGFX Designer에서 캔버스 바깥에 배치됩니다.

아래 그림과 표에 따라 위젯을 배치합니다.

Screen2의 위젯 위치

위치위젯속성
1이미지
  • 이름: background
  • Location:
    • X: 0, Y: 0
  • 이미지: background_Screen2.png
1버튼
  • 이름: buttonSettings
  • Location:
    • X: 422, Y: 10
  • Image:
    • 해제되었을 때 이미지: configuration.png
    • 눌렀을 때 이미지: configuration.png
3TextArea
  • 이름: textClock
  • Location:
    • X: -124, Y: 109
    • W: 124, H: 54
  • Text:
    • Text: <hour>:<min>
    • Wildcard 1:
      • 초기 값: 00
      • 와일드카드 버퍼 사용: 예
      • 버퍼 크기: 3
    • Wildcard 2:
      • 초기 값: 00
      • 와일드카드 버퍼 사용: 예
      • 버퍼 크기: 3
    • Typography: 40px
    • Alignment: 중앙
  • Appearance:
    • Color: #FFABABAB
4
  • 이름: circle
  • Location:
    • X: 479, Y: 36
    • W: 200, H: 200
  • Image & Color:
    • Color: #FFBABABA
  • Appearance:
    • Center Position:
      • X: 100, Y: 100
    • Start & End Angle:
      • Start: 0, End: 180
    • Radius: 72
    • Line Width: 6
    • Cap Style: 삼각형

모든 위젯을 추가한 스크린은 아래와 같은 모습이 되어야 합니다. TextArea와 원은 스크린 밖에 있기 때문에 보이지 않습니다.

모든 위젯이 Screen2에 추가된 모습

스크린 전환

이제 스크린 전환 기능을 추가해야 합니다. 이를 위해 Screen1의 Clock 버튼과 Screen2의 Configuration 버튼에 상호작용을 할당합니다. 또한 Screen2 전환 시 Screen1에서 스크린 영역 밖에 배치된 그래픽 요소 2개를 제자리로 옮기려고 합니다.

이를 위해 Screen1에 다음과 같은 상호작용을 추가합니다.

인터랙션속성
"Screen2"로 변경
  • Trigger: Button is clicked
  • Clicked Source: buttonClock
  • Action: Change screen
  • Screen: Screen2
  • Transition: Cover
  • Transition Direction: North

Screen2에는 다음과 같은 인터랙션을 추가합니다.

인터랙션속성
"Screen1"로 변경
  • Trigger: Button is clicked
  • Clicked Source: buttonSettings
  • Action: Change screen
  • Screen: Screen1
  • Transition: Slide
  • Transition Direction: South
원을 제자리로 이동
  • Trigger: Screen transition ends
  • Action: Move widget
  • Widget to move: circle
  • Position: 140, 36
  • Easing: Cubic, Out
  • Duration: 750ms
텍스트 시계를 제자리로
  • Trigger: Screen transition ends
  • Action: Move widget
  • Widget to move: textClock
  • Position: 178, 109
  • Easing: Cubic, Out
  • Duration: 750ms

인터랙션이 추가된 스크린의 모습은 다음과 같습니다.

모든 위젯이 Screen2에 추가된 모습

런타임에서 시계를 업데이트하고 원을 애니메이션 처리할 때 사용하는 가상 함수는 handleTickEvent입니다.

handleTickEvent는 TouchGFX 프레임워크에서 주기적으로(각 프레임에서) 호출되어 활성화된 스크린의 요소(여기서는 시계와 원)를 동적으로 업데이트합니다.

Screen1과 마찬가지로 시간 및 분 카운터가 시계를 추적하는 데 사용됩니다. handleTickEvent는 시계 업데이트보다 더 자주 호출되기 때문에 tickCounter를 추가하여 시계가 업데이트되는 주기 사이의 tick 수를 구합니다. 또한 원호의 각도를 업데이트할 때는 addStart 함수와 addEnd 함수가 사용됩니다. handleTickEvent 함수와 변수는 아래와 같이 Screen2View.hpp에 추가됩니다.

TouchGFX/gui/include/gui/screen2_screen/Screen2View.hpp
public:
...
virtual void handleTickEvent();

protected:
int16_t hour;
int16_t minute;
int16_t tickCount;
int16_t addStart;
int16_t addEnd;
...

Screen2View.cpp에 추가되는 handleTickEvent 구현체와 이에 따라 시계와 원을 업데이트하는 코드는 아래와 같습니다.

TouchGFX/gui/src/screen2_screen/Screen2View.cpp
Screen2View::Screen2View()
: hour(0), minute(0), tickCount(0), addStart(1), addEnd(2)
{ }
...
void Screen2View::handleTickEvent()
{
if (tickCount == 60)
{
minute++;
hour = (hour + (minute / 60)) % 24;
minute %= 60;

Unicode::snprintf(textClockBuffer1, TEXTCLOCKBUFFER1_SIZE, "%02d", hour);
Unicode::snprintf(textClockBuffer2, TEXTCLOCKBUFFER2_SIZE, "%02d", minute);

textClock.invalidate();

tickCount = 0;
}

if (!textClock.isMoveAnimationRunning())
{
tickCount++;
if (circle.getArcStart() + 340 == circle.getArcEnd())
{
addStart = 2;
addEnd = 1;
}
else if (circle.getArcStart() + 20 == circle.getArcEnd())
{
addStart = 1;
addEnd = 2;
}
circle.updateArc(circle.getArcStart() + addStart, circle.getArcEnd() + addEnd);
}
}
...

튜토리얼 2에서 살펴본 것처럼 와일드카드에 사용되는 문자를 추가해야 합니다. 여기에서는 TextArea에 사용되는 타이포그래피에서 Wildcard Ranges 열에 “0~9”를 추가해야 합니다.

Further reading
이번 단계에서는 원 위젯을 사용했습니다. 원 위젯에 대한 자세한 내용은 페이지를 참조하십시오.

2단계: 데이터 저장

이번에는 스크린 전환 시 데이터를 저장하는 방법과 저장된 데이터를 가져오는 방법에 대해 알아보겠습니다.

TouchGFX 애플리케이션은 Model-View-Presenter 설계 패턴을 따릅니다. 따라서 view(예: 스크린)에서 조작된 데이터를 영구적으로 보존하려면 presenter를 통해 Model 클래스로 전송해야 합니다. Model-View-Presenter 설계 패턴에 대한 자세한 내용은 모델-뷰-프리젠터 설계 패턴 페이지를 참조하십시오.

Model에 시간/분 추가하기

Model은 애플리케이션 데이터를 저장하는 역할을 합니다. 버튼 상태, 현재 표시할 수 있는 위젯 등 임시 데이터는 model에 저장되지 않습니다.

Model을 통해 데이터를 저장하거나 가져오려면 다음과 같이 protected가 설정된 시간 값과 분 값을 model에 추가하는 동시에 이러한 값에 접근할 수 있는 공개 함수도 추가해야 합니다.

TouchGFX/gui/include/gui/model/Model.hpp
...
public:
void saveHour(int16_t saveHour)
{
hour = saveHour;
}

void saveMinute(int16_t saveMinute)
{
minute = saveMinute;
}

int16_t getHour()
{
return hour;
}

int16_t getMinute()
{
return minute;
}

protected:
int16_t hour;
int16_t minute;
...
Note
위에서 model.hpp 파일에 #include <touchgfx/hal/types.hpp>도 함께 추가되어야만 int16_t 유형을 사용할 수 있습니다.

또한 생성자에서 시간과 분을 초기화해야 합니다.

TouchGFX/gui/src/model/Model.cpp
...
Model::Model() : modelListener(0), hour(0), minute(0)
{
}
...

위 코드를 사용하면 스크린을 변경하는 과정에서 시간과 분이 Model에 저장됩니다(스크린에 저장된 데이터는 다른 스크린으로 변경할 때 손실됩니다). 이는 모든 presenter들이 Model을 사용하기 때문에 Presenter와(및 view) 사이에 정보를 공유하기에 좋은 방법입니다. Model에서도 UI가 하드웨어 주변 장치나 기타 소프트웨어 모듈 등 나머지 시스템에 연결될 수 있습니다.

View에서 Model에 접근하기

이제 view에서 model에 저장된 데이터에 접근하려면 presenter가 다음과 같이 함수를 제공하여 Screen1View가 model에서 데이터를 로드하여 저장할 수 있도록 허용해야 합니다.

Screen1Presenter.hpp
...
public:
void saveHour(int16_t hour)
{
model->saveHour(hour);
}

void saveMinute(int16_t minute)
{
model->saveMinute(minute);
}

int16_t getHour()
{
return model->getHour();
}

int16_t getMinute()
{
return model->getMinute();
}
...

또한 Screen2 역시 model에 저장된 데이터에 접근할 수 있어야 하므로 Screen2Presenter.hpp에 동일한 라인을 추가합니다.

Model의 데이터

Model에 저장된 시간과 분에 접근하기 위한 코드가 삽입되었으므로 이제 Screen1Screen2를 업데이트하여 지역 변수만 사용하지 않고 Model에서 두 값을 가져와야 합니다.

Screen1 업데이트하기

다음과 같이 Screen1View의 시간과 분을 model의 값으로 초기화하고, TextArea의 버퍼를 초기화할 수 있습니다.

Screen1View.cpp
...
void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();

hour = presenter->getHour();
minute = presenter->getMinute();

Unicode::snprintf(textAreaHourBuffer, TEXTAREAHOUR_SIZE, "%02d", hour);
Unicode::snprintf(textAreaMinuteBuffer, TEXTAREAMINUTE_SIZE, "%02d", minute);
}
...

시간/분 값을 저장하기 위해 Interactions에서 저장 버튼 2개에 생성된 가상 함수가 Screen1View.hpp에 구현되어 presenter를 통해 model에 값을 저장합니다.

Screen1View.hpp
...
public:
virtual void buttonSaveHourClicked()
{
presenter->saveHour(hour);
}

virtual void buttonSaveMinuteClicked()
{
presenter->saveMinute(minute);
}
...

이제 Screen1이 model에서 시간/분의 초기값을 가져옵니다.

Screen2 업데이트하기

Screen2 역시 값을 모델과 동기화해야 합니다.

Screen1과 마찬가지로 텍스트 시계에 표시되는 초기 값은 Model에 저장된 데이터와 일치해야 합니다.

Screen2View.cpp
...
void Screen2View::setupScreen()
{
Screen2ViewBase::setupScreen();

hour = presenter->getHour();
minute = presenter->getMinute();

Unicode::snprintf(textClockBuffer1, TEXTCLOCKBUFFER1_SIZE, "%02d", hour);
Unicode::snprintf(textClockBuffer2, TEXTCLOCKBUFFER2_SIZE, "%02d", minute);
}
...

위 코드를 실행하면 Model에서 시간과 분을 가져옵니다. 업데이트된 값은 스크린에서 나갈 때(Screen1의 구성 화면으로 이동할 때) Model로 다시 전송되어야 합니다.

Screen2View.cpp
...
void Screen2View::tearDownScreen()
{
presenter->saveHour(hour);
presenter->saveMinute(minute);

Screen2ViewBase::tearDownScreen();
}
...

위 코드를 실행하면 구성 화면으로 이동하기 전에 업데이트된 시간/분 값이 model로 전송됩니다.

이것으로 간단한 애플리케이션과 이에 따른 튜토리얼 3을 마치겠습니다.