카테고리 보관물: 코틀린 일반

[플러터] 플러터 쓰다가 생각난 이런 저런 상념

(1) build 함수에 위젯 주렁주렁 달기
build 함수에 return 문에 위젯을 배치해서 처리하는 것이 플러터의 특징이다. UI 구성을 하다보면 위젯 개수가 많아지고 속성도 많아져서 매우 길게 늘어진다. 이렇게 되면 코드가 가독성도 없고, 무거운 앱인 경우 오버헤드가 있다고 한다. 그래서 고안된 추천 사항은 children: [] 속성 설정이 가능한 위젯을 두고 달아둘 위젯을 클래스나 함수로 분리해서 배치하라는 기법이다. 함수보다는 클래스가 더 오버헤드가 적댄다. 빌드할때 반복빌드하는지의 여부 같다. 난 내 코드가 이렇게 반드시 해야 할줄 알았는데 깃헙을 둘러보다보니 유려한 앱을 만든 타개발자들도 나처럼 return 문에 위젯을 주렁주렁 달아서 쓰더라. 우선 이래도 되는 이유도 만만찮다. 나열해보면

(i) 코드가 분산되면 스크롤을 여러번 해야 해서 불편하다
(ii) PC 하드웨어 발달로 인해 램 크기가 커져서 빌드시 지장이 없는 한도가 있다
(iii) 안드로이드 스튜디오에서 heap에 2GB, VM에 1GB 정도 할당되어 있는데 이 설정이 빌드와 실행에 관여한다면 왠만하면 스택오버플로우 안난다
(iv) 태블릿과 모바일도 사양이 좋아져서 지장없는 한도가 있다
(v) 지금 만드는 앱의 처리가 매우 가볍다. 글은 텍스트로 된 커봤자 10KB도 안되는 길이의 글을 처리하는 앱이고 게임도 3D 리소스가 없다

일단 지금 지식 범위 내에서 테스트안해보고 게워낸 체험담인데 UI 배치 구문은 주렁주렁 달아도 괜찮은 것 같다. 다만, UI 외적인 처리가 보일러 플레이트처럼 안되려면 간결한 코드로 해결하는 프로젝트를 구해서 잘 살펴보는 것은 여전히 필요하다. 오늘 만든 게임 처리 코드도 작동은 하되, 구문이 지저분해보이기도 하는데 이것도 기우인가? 일단 쉬운 리팩토링으로 해야 할게 if 문을 switch 문으로 바꾸는 코드 정도. 나머지는 타개발자들도 그냥 나처럼 하는 게 확인이 되었다.

(2) 어떤 문제인지는 미확인인데 오래된 소스코드를 받아와서 컴파일하다보면 다트와 코틀린, JDK, 의존성 라이브러리들의 버전이 문제가 되는 경우가 많다. IDE가 정확하게 짚어주는 편이라, 링크된 문서를 보면서 하다보면 컴파일이 되는데, 때로는 플러터 코드뿐아닌 안드로이드나 iOS 코드도 직접 수정해야 할때가 있다. 일단 IDE가 짚어준대로 할 수 있는 논리적 범위내에서 잘 작동하는 프로젝트와 흡사한 설정인 경우, 문제가 된 버전을 잘 작동하는 프로젝트의 것을 빌려와서 컴파일하면 잘 되기도 하는데 이게 안되기도 한다. 이런 경우, lib, assets, pubspec.yaml 정도만 복사해두고 프로젝트를 다 밀어버린 후, 플러터 프로젝트를 새로 만들어서 거기에 복사해둔 파일을 덮어씌우고 하면 잘 될때가 많다.

플러터 프로젝트 생성시 안드로이드나 iOS 소스 베이스도 함께 생성하던데, 위에 언급한 다트, 코틀린, JDK, 의존성 라이브러리들의 버전 문제도 있을수 있고, 그레이들 캐시 문제거나 그레이들 설정, 저장소 문제 등등이 겹쳐서 문제가 있을 때 위 방법대로 하면 해결이 되는 경우가 많다. 일단 이 방법 쓰기전에 IDE가 짚어준 문서는 읽어보고 적용해보는게 좋을 수는 있다.

(3) 대충 의식 흐름대로 쓴 글이라 표현이 독자들의 판단 의지보다 내 생각을 표시하는데 주력한 글이 되는데, 여튼 이런 저런 상념을 하고 있다. 플러터는 배운지 두달 정도라 더 다루어보고, 더 다루다가 의자가 발동되면 플러터 API 소스코드도 들여다봐야겠다. GlobalKey 관련해서 조금 해봤는데 늘 하는게 옳다. 특히 컴파일 오류 나면 예외 발생인 경우 실행 순서대로 오류 과정 추적하게 IDE가 배려를 해주는데 다 읽어봐야겠다. 지금까지는 코드 구문 바꿔가면서 했고 그래도 버그는 잡히는데 코드 구문 바꾸기전에 API 소스코드도 좀 봐야하겠다.

(4) 안드로이드 스튜디오에 플러터 플러그인을 설치해서 운용하면 안드로이드 API로 개발할때와 같은 사용성으로 플러터도 개발이 가능해진다. 플러터 프로젝트를 생성할때 멀티플랫폼으로 안드로이드, iOS, 리눅스, 웹, 윈도우 소스코드 베이스도 같이 생성한다. 안드로이드의 경우 그레이들을 빌드용으로 쓰는데 정확한 확인은 안했으나 실행 순서를 생각해보면, 그레이들 설정을 기본값으로 되돌릴때 세심한 주의가 필요한 것 같기도 하다.

안드로이드 스튜디오를 설치할때 그레이들도 설치한다. 그레이들이 설치되고 써봤다든지 하면, 프로젝트 루트에 생성된 android 디렉토리 아래 .gradle 디렉토리가 생성되어 필요한 파일을 캐시해두기도 하고 빌드할때 필요한 설정들이 일반적인 방법으로는 고치기가 어려운 파일들이 생성된다. 그런데 빌드 문제가 겹쳐서 그레이들 설정이 의심되는 경우, 이 디렉토리와, 운영체제 사용자 홈디렉토리 아래에도 생성되는 .gradle 디렉토리를 아예 그냥 확 밀어버리면 문제가 있게 되는 것 같기도 하다.

저 두 히든 디렉토리를 지워버리면 그레이들 재설치시, 의존성 라이브러리가 사용자가 지정도 안한 종류가 추가되는 것 같기도 하고, 내가 추가한 패키지가 참조해서 그런지는 미확인인데, 저 두 디렉토리 지우고 그레이들을 다시 설치한 이후부터 앱 컴파일된 apk 파일 크기가 5배가 늘었다.

그냥 강제로 지웠다면 안드로이드 스튜디오를 언인스톨하고 관련 디렉토리를 전부다 지우고 다시 설치하지 않으면 그레이들 설정을 복구하는게 어렵지 않나 싶다.

일단 그레이들은 gradlew.bat 파일에 옵션줘서 조치를 하는 방법이 있을 것 같은데, 디렉토리를 함부로 지우지 말고 저 명령어로 처리하는게 좋을 것 같다. 물론 옵션이 필요한게 제공되면.

이런 저런 상념인데 일단 말은 맞는 것 같다.

[플러터] 플러터로 앱 제작시 같은 자리에 오는 위젯을 조건에 따라 달리 표시하는 방법

플러터의 장점은 UI 배치가 매우 쉬운 배려가 되어 있는 언어라는 점입니다. 위젯 형태로 제공되는 기본 UI 구성 요소가 많이 제공되고, 각각의 위젯 내부에 배치할 수 있는 속성에 위젯 클래스를 배치하기만 하면 UI 구성이 잘 됩니다. 이 장점으로 인해 저도 아주 훌륭한 혜택을 받고 있습니다. 다트와 플러터에서 제공되는 제작사가 만든 UI 위젯뿐아니라, 사용자 생태계에서 제공되는 패키지도 쓰면 아주 쉽게 UI 배치가 되서 좋습니다.

플러터는 화면을 채우는 위젯에 하위 위젯을 배치하면서 UI 구성을 설계해갑니다. main() 함수에서 runApp()으로 호출되는 최상위 위젯 아래에 return 문을 확장해가면서 코딩을 할 수 있습니다.

이 파일을 실행하면 화면에 Hello, world!가 뜰 것입니다. 다른 언어처럼 실행 첫진입 지점을 포함한 파일이 읽히게 되고, 이 파일에 정의된 main() 함수에서 runApp() 함수가 실행됩니다. 위 예제에서는 다른 클래스를 return하지 않고 즉시 Center() 위젯을 불러와서 그 안에 텍스트를 표시하는 Text() 위젯을 배치해서 코딩이 되었습니다.

주목할 것은 Center() 함수 안에 child: 라고 표시된 부분입니다. 플러터에서 제공하는 위젯들은 계층구조를 가지고 배치가 되는데, 배치하는 위치에는 십중팔구 타위젯을 배치할 수 있게 설계된 속성이 옵니다. 위의 경우 child: 라고 표시된 부분이 그러한데요. Center() 위젯이 설계될 당시에 child: 속성을 써서 타위젯을 배치하게 되게 해둔 것입니다. 여기에 올 수 있는 위젯이라면 다 올 수 있습니다. (플러터 API doc에 나옴) 예제에서는 화면 중앙에 배치하기 위해 Center() 위젯을 최상위 위젯으로 두었고 그 안에 Text() 위젯을 써서 문자열을 표시하게 해두었습니다.

때로는 MaterialApp()과 Scaffold() 같은 위젯을 return 해서 보다 더 규모있는 앱을 만들기도 하죠.

이 경우 유의할 것은 child: 와 흡사하면서 여러개의 위젯을 병렬해서 배치가 가능한 위젯들이 있다는 것입니다. Column() 이나 Row()가 그 예로, child: 대신 children: [ ] 과 같은 속성이 제공되면, [ ] 안에 여러 위젯을 쉼표로 구분하면서 배치할 수 있게 해서 레이아웃 구성에 효율이 있게 될때가 많습니다.

이 경우 특히 좋은 것은 전역변수로 지정한 변수에 상태값을 대응시켜서 같은 자리에 내용만 바꿔 위젯 표시를 하고 싶을때 가능해게 해준다는 것입니다.

위에 예에서처럼 children: [ ] 내부에 특별한 방법으로 채워주면 실행이 가능한 기능이 있습니다. setState() 함수로 _conditional 값을 조정하면 _conditional 값에 따라 같은 위치에 Text() 값만 바뀌어 표시할 수 있습니다. 조정된 _conditional 이 충족되는 값일때 : 로 구분한 영역의 코드만 재실행되는 것입니다. 이렇게 하면 안내 메시지 표시할때 route 기능 없이도 같은 위치에 다른 표시를 할 수 있게 됩니다.

예를 들면 GestureDectector() 와 같이 onTap: 속성이 제공되는 위젯에서 사용자가 탭한 것을 받아서 setState() 함수로 _conditional 값을 바꿔주면 실행이 됩니다.

이 경우 _conditional 변수가 전역변수여야 하고, setState() 함수가 실행가능해야 합니다. (setState() 실행 가능한 한 조건은 createState() 함수로 State 클래스가 작성되어 있어야 합니다)

설명을 잘한지 모르는데, 일단 흐름은 이해가 될 것입니다. setState() 함수를 실행하는 코드 예시는 아래 링크에서 힌트를 얻어보세요.

https://docs.flutter.dev/development/ui/interactive

참고가 되셨을 것 입니다.

M2 맥 미니 신청해두었습니다

플러터로 멀티플랫폼 모바일 앱 개발을 하는데 iOS용 빌드는 맥에서만 가능합니다. 본래 가격이 좀 쎈 편인 기기들이라 안드로이드 앱을 우선 출시하고 돈벌어 구하려다가 비용 사정이 나아져서 맥 미니 M2 버전을 신청해두었네요. 8GB 모델이고 M2도 8코어 CPU에 10코어 GPU 라는데 우선 플러터 개발을 위한 머신으로 신청했고, 어느 정도 성능이 나온다면 캡처 장치를 연결할 목적으로도 쓰고 싶은데요. 선더볼트 4 포트가 2개 있어서 연결하면 되는데, SSD가 256GB 이고 따로 다른 SSD가 안달려 있어서 ProRes 캡처가 필요할때 SSD 성능이 어떨련지 궁금하네요. 캡처 장치가 선더볼트 3에 무압축 코덱을 지원하는데 무압축으로 캡처받으면 SSD가 macOS 설치된 SSD라, 분리된 SSD가 아니라서 어떻게 될지 미상입니다. 왠만하면 잘 되겠죠.

대략적인 사양은 아래와 같습니다.

M2 프로세서가 좋다고 하는데 일단 제가 신청한 제품 CPU는 M2 Pro 가 아닌 M2 이구요. Xcode를 주로 돌릴 생각인데 빌드가 잘 될지 미상입니다. 규모가 안큰 프로젝트들이 될 것 같고 macOS은 어떤지 모르지만 부팅하고나서 6GB 이상만 여분이 있으면 컴파일할때도 크게 지장이 없을 것 같습니다. 전에 들은바로는 컴파일시 램이 8GB 밖에 안되면 안된다는 의견도 있던데요. 스택과 VM에 램을 최대한 할당해볼 생각입니다. 여의치 않으면 램 증설하거나요.

그런데 업글이 가능한지는 직접 봐야 하는데 일단 구입하고나면 업글이 불가능하다는 말이 있네요. 아무래도 밀폐된 케이스라서 그런 듯한데 아마존에서 구해서 할부 한달 지불 비용을 낮출 수 있었으나 램은 기본탑재 크기인 8GB 그대로입니다. 구입후에도 업글이 되면 좋겠습니다. 케이스를 여는게 가능하다면요. 램말고 보조기억장치도 SSD를 하나 더 달거나 램을 직접 더 달거나 등등의 작업이 안되면 어떡할까요. 그래도 초기 세팅된 하드웨어로도 가능할 것 같긴 한데요.

일단 Xcode 잘 돌아가면 됩니다. 하지만 캡처 기능의 경우 SSD 하나 더 못달면 OS를 설치한 SSD로 캡처도 처리해야 하는데 잘 될지 확인해봐야 하겠습니다. SSD니, 과부하가 늦게 올테지만 코덱에 따라서는 문제가 있을 것 같기도 합니다.

여튼 주목적이 플러터로 iOS 앱 개발도 하는 것이라 앱 테스트 용도로 앞으로 비용 추이봐서 아이폰이나 아이패드도 구해야 하는데 비용이 넘칠 수도 있습니다. 비용이 넘치면 iOS 실행 테스트는 에뮬레이터로 버텨야 할 것 같습니다. 이번달에서도 비용이 거의 커버할 것 같으나 일단 살펴봐야 합니다.

M2 맥 미니 머신을 구하면 좋은 점은

(1) iOS 앱 개발 가능
(2) M1 맥 미니 제품에 비해 100불 저렴함
(3) ProRes 캡처 가능

입니다.

이정도로 대략 생각을 표현해봅니다.

잘써야죠.

https://macmini.inf-news.com/

아무래도 M2가 SoC인 것 같습니다. 해체하는 영상이 있는데 로직보드가 굉장히 작네요. ARM 계열입니다. CPU의 기능들을 담당하는 컴포넌트와 GPU, Neural Processor, WiFi 등이 한칩에 들어간 것 같습니다. 확인해보면 이중에 몇개는 아닐 수 있더라도 SoC의 정의가 그러하네요. 그래서 작은 크기로도 성능이 나오는 것 같습니다.


아마존 캐나다에서는 애플 캐나다에서보다 더 낮은 한달 비용으로 할부를 더 세부적으로 고르는게 되는 것 같습니다. 물론 할부 기간을 늘리고 한달 비용을 낮추면 이자가 붙기도 하네요. 일단 구입적으로 메리트가 있는게 아마존 캐나다에서 애플 스토어 채널을 이용하는 것 같습니다.


참고로 애플 캐나다와 아마존 캐나다에서 형성된 가격이 M2 기종이 M1 기종 동급 라인업보다 100불이 저렴하네요. 새로 나온 제품인데…

안드로이드에서 글 저장을 R.array 참조값으로 처리하기 위한 방법 연구

안드로이드에서는 글을 저장할때 DB를 많이 씁니다. 편리한 처리를 위해 Room을 써서 SQLite를 쓰는 방법이 유력합니다. 그런데 Room을 안쓰고 리소스 디렉토리에 XML로 저장해서 R.array 참조값으로 문자열과 드로어블 리소스를 받아와서 배열로 처리하는 방법도 있습니다. XML로 저장한 글을 불러와서 처리하는 한 방식으로 여러모로 쓸만합니다.

간단하게 한마디로 말하자면, res/values 디렉토리에 XML 파일을 만들고, TypedArray나 StringArray로 가져와서 이를 코틀린 코드로 처리해서 화면에 표시합니다.

저장한 XML 파일의 구조는 아래와 같습니다.

res/values/contents.xml

이렇게 저장해두고 루프를 돌려 텍스트뷰에다가 표시할 수 있습니다.

contents는 배열변수이고, R.array 참조로 item값을 가져와서 루프로 돌리면 여러개의 글을 Room이나 DB 처리 없이도 처리가 됩니다.

그런데 한 XML 파일의 크기가 커지게 되면 글을 무한정 추가할 수 없습니다. 컴파일 에러가 납니다. 이럴 때는 파일을 분리해서 저장하고 에 @string/R.string참조값 을 기재해서 쓰면 컴파일 에러가 안납니다.

res/values/contents.xml:

이 코드와 윗 코드의 차이점은 직접 글을 입력하기보다 분리된 다른 XML마다 한편씩 저장해서 분산시키고 contents.xml 에다가는 해당 글들의 R.string 참조값을 기재하는 방법입니다. 500자로 되던 글이 스무자 정도로 압축되게 되어 컴파일 에러를 피할 수 있습니다.

res/values/writing1.xml:

res/values/writingn.xml:

이렇게 글 개수대로 따로 XML을 한편씩 만들어 분산 해두고 아래처럼 불러오면 윗단에 제일 먼저 소개한 코드와 똑같이 작동합니다. 컴파일 에러가 안납니다.

이렇게 해서 똑같이 루프 돌려 처리하면 됩니다.

이 방법은 Room과 SQLite를 쓰기가 번거로울때 쓰기 좋고 또한 번역해야 할 문자열로 된 글일때 더욱 더 좋다고 생각합니다. 안드로이드 스튜디오에서 XML 리소스 파일을 처리하는 기능으로 간편하게 다국어 처리가 되기 때문입니다.속도가 느려지는지는 미상입니다.

일단 참고가 되실 것입니다.

참고로 윗 글에서 각 item들이 500자라고 한 것은 임의로 기재한 것입니다. 최대 몇자까지 들어갈 수 있는지는 확인을 안했는데 거의 100KB 가까이 한 XML에 저장하니 컴파일 에러가 나더군요. 이 내용을 일곱개의 XML에 분산저장후 @string/R.string참조값으로 참조하게 하였습니다. 잘 작동합니다.

안드로이드 리사이클러뷰의 뷰홀더 클래스에 onClick 함수 재정의시 선택한 아이템의 위치(position) 넘겨서 처리하기

안드로이드에서 리사이클러뷰를 구현할때 뷰홀더 클래스를 지정해서 해야 합니다. 뷰홀더 클래스에는 onClick 함수가 재정의되어야 하는데 리사이클러뷰가 표시하는 목록에서 아이템이 클릭되면 일어나야 하는 기능을 구현하는데 쓰입니다. 그러나 구조적인 문제로 몇번째 아이템인지 알려주는 position 값을 넘겨받기가 애매해집니다.

이 경우 코틀린에서 제공하는 higher order function을 써야 합니다. higher order function은 클래스 시그니처에 지정하는 함수 문법인데 함수를 파라메터로 넘겨서 함수의 리턴값 등으로도 쓰이게 하는 함수 지정 방법입니다. 이를 이용하면 뷰홀더 함수에 특정 함수를 넘겨서 onClick 함수에서 받아서 쓸 수 있게 됩니다. 이 문법을 쓰면 position 값이 onClick 함수 내에 전달이 되어 선택된 아이템 클릭시 그 내용을 보여주는 기능을 구현할 수 있게 됩니다.

구현 순서는

(1) 액티비티나 프래그먼트에 두가지 지정을 해줍니다.
— adapter 설정시 position 값을 전달하는 람다식 사용
— onListItemClick(position: Int) 함수를 구현해서 navigation의 action에 position 변수를 전달 (Safe Args를 쓴 예)

예:

(2) adapter에서 onCreateViewHolder의 리턴값으로 뷰와 함수 전달

예:

(3) 뷰홀더 클래스에서 View.onClickListener와 onClick 함수를 구현한다.

예:

뷰홀더와 아답터는 프래그먼트 클래스의 inner 클래스로 정의하면 코딩 제한이 없어진다.

Safe Args로 position값을 아이템 내용 보여주는 프래그먼트에서 받아서 아래처럼 처리해준다.

이해가 잘 되게 예시한지는 미상인데 참고는 되실 것이다.

Safe Args는 별도의 기능이지만 위에 navArgs로 불러온 position 값 처리를 위한 또다른 Safe Args 처리로 아래와 같이 navigation XML에 정의해둔다.

R.array 참조로 드로어블 참조값 배열로 가져오기

보통 드로어블은 일단 형식에 맞게 저장이 되어 있다면, R.drawable.파일명 참조로 읽어들일 수 있습니다.

때로는 파일명이 앞부분을 같게 하고 일괄적으로 번호를 붙여 저장하기도 하는데요. 이 경우 R.string이나 R.id로 불가능합니다. 배열에 저장해야 되는데 아래처럼 하면 안됩니다.

이것을 코틀린 코드에서 받아내서 루프로 배열에 저장하면 저장된 값은 String이지, R.drawable 참조값이 아니라서 아무런 기능을 못합니다.

대신 TypedArray로 지정해서 가져오는 방법이 있습니다.

이렇게 지정해두고

처럼 하면 됩니다. 이코드를 override fun onBindViewHolder(holder: ListHolder, position: Int) 함수처럼 position 값을 넘겨받는 함수에 기재하면 배열로 읽어들인 TypedArray를 R.drawable 참조와 동일하게 써서 처리할 수 있게 됩니다.

안드로이드 코틀린으로 스플래시 화면 제작

스플래시 화면에 사진을 표시하고 배경음악을 들려주는 코드를 만들었다. 코드상으로는 단순한 구조인데 액티비티만 구현해도 가능하다. 그러나 자잘한 챙겨둘 조건들이 있어서 관건이다.

  • 사진 파일의 선명도를 유지하는 방법 (화면 크기, 소프트웨어적인 해상도에 따라 선명도가 달라짐)
  • 물리적 화면 크기의 비율에 따른 화면 표시상의 빈 공간 제거 방법이 필요
  • 음악파일의 크기가 작으면 좋을 것
  • 액티비티 생명 주기에 따라 음악재생 객체를 리셋하거나 완전 삭제 필요

우선

  • 현존하는 기기의 최고 해상도보다 가로 세로 픽셀을 크게 사진 파일 제작
  • 화면의 물리적 종횡비를 맞추어 세가지 종류의 사진 파일 제작
  • 프리미어 프로 같은 소프트웨어로 음악 파일 잘라 저장
  • 액티비티에서 제공하는 기정의 함수를 재정의해서 생명주기에 따라 MediaPlayer 생성, 소멸 코드 작성

이 방법을 쓰면 된다. 관건은 리소스를 제작할때 고려할 것들이고, 규칙에 따라 하면 좋다는 것이다.

리소스 파일의 디렉토리 구조는 이렇다.

이 구조다.

우선 density 계산해서 xhdpi, xxhdpi 구분할 필요없이, 종횡비 비율만 맞추어 해상도를 크게 해서 저장하면 된다. 현존하는 최고 해상도를 조사해서 대략 2000x3200px 정도로 저장하면 된다.

그리고 splash_image_sw600dp.png는 3:4 던가? 실행해보면서 7인치와 8인치 태블릿 화면에 꽉차게 픽셀 조정해서 저장한다.

splash_image_sw720dp.png는 10인치 태블릿을 위한 파일인데 이역시도 실험해서 꽉차게 픽셀 조정해서 저장하면 된다.

res/layout/splash_layout.xml

res/layout-sw600dp/splash_layout.xml

res/layout-sw720dp/splash_layout.xml

이렇게 해두고 스플래시 액티비티에서 setContentView(R.layout.splash_main) 해주면 된다.

음악 파일은 무료 음원 중에 클래식으로 저작권이 작곡가와 연주가 모두 소멸된 파일을 받아다가 프리미어 프로에서 26초로 잘라낸 파일을 썼다. 4MB 정도 크기가 160KB 정도로 줄었다.

이 파일을 res/raw 에 저장해서 아래 코드로 불러온다.

이렇게 하면 음악이 연주되는데, 백버튼으로 다른 화면으로 가면 연주가 이어진다. 그래서 액티비티 생명주기에 맞게

이렇게 해두면 스플래시 화면에서 백버튼을 눌러 최종적으로 홈화면으로 나와도 음악이 꺼지고, 타 액티비티로 넘어가도 음악이 멈추게 된다. 의도한대로다.

그리고 액티비티 onCreate 함수에

처럼 구현한다. 버튼과 스플래시 이미지를 한 개의 이미지로 제작했고, 분간이 쉽게 button 변수에 담아 처리했다. Intent 생성자는 두번째 인수로 이동할 액티비티의 이름을 붙여서 넘겨준다. 그러면 이미지를 클릭시 이동할 액티비티가 화면에 표시된다. 코틀린 코드지만, 인수 끝에 .java가 붙는다.

안드로이드 젯팩에 포함된 탐색 라이브러리에 대한 설명 1

안드로이드 앱은 여러개의 액티비티와 프래그먼트가 쓰인 화면으로 구성됩니다. 각각의 화면은 레이아웃 규칙에 의해 만들게 되어있고 이 화면들을 옮겨다니는 것도 규칙이 필요합니다. 흔히 말하는 네비게이션 기능을 구현해야 하는 것입니다. 이 네비게이션 기능은 프로그래머가 직접 하드코딩해서 만들어도 되지만, 아무래도 프로그램 구조가 복잡해지고 보다 더 체계적으로 만들기 위해서는 통합된 기준이 있으면 좋을 것입니다.

안드로이드 젯팩에 포함된 탐색 라이브러리(navigation component in jetpack)를 쓰면 통합된 기준을 써서 메뉴 기능도 관리하고 각각의 화면 전환시 이동하는 루트도 정할 수 있습니다.

탐색 라이브러리가 작동하려면 크게 세가지 요소를 구현해야 합니다. 우선

(1) 네비게이션 그래프(navigation graph) = 새로 추가된 XML 리소스 파일의 하나로, 네비게이션 기능에 청사진이 되는 파일입니다. navigation이라는 루트 엘리먼트가 fragment를 포함하고 있고 fragment 끼리 이동하는 루트가 action 엘리먼트에 의해 정의됩니다. 이 파일이 마련되면 탐색 라이브러리가 읽어들여 처리하게 됩니다.
(2) NavHost = 프래그먼트의 일종으로 레이아웃에 추가해야 하는 특별한 위젯입니다. 네비게이션 그래프에서 정의된 각각의 프래그먼트 목적지를 보여주게 됩니다.
(3) NavController = 자바나 코틀린으로 구현된 구현체로 탐색 라이브러리의 실질적인 지휘를 하는 객체.

이 세가지 요소가 구현되어 잘 작동하면 기본적인 처리가 완료됩니다.

탐색 라이브러리를 코틀린으로 쓰려면 최신 버전의 안드로이드 스튜디오를 설치한 전제 하에 그래들 설정 파일에 아래 라인을 추가하면 됩니다.

기본적인 코딩은 타이핑도 가능하고, 네비게이션 그래프는 리소스 파일로 등록한 후 네비게이션 에디터로 그래픽컬하게 목적지와 출발지를 설정할 수 있습니다. 그래픽컬한 설정이 특징적인데 여러개의 액티비티나 프래그먼트가 있을 경우 복잡하게 얽힌 네비게이션 구조를 그래프처럼 한눈에 알 수 있어서 좋습니다. 일단 에디터로 경로를 설정하고나서 코드 화면으로 전환해서 XML 구문을 조정하면 됩니다.

위 코드에서 id는 프래그먼트의 식별자이고 layout은 아래에 소개할 레이아웃 파일에서 프래그먼트의 id를 의미합니다. destination은 어떤 동작을 할지 목적지를 설정한 것입니다.

NavHost는 특수한 프래그먼트로 액티비티 UI 레이아웃에 배치하는 코드들입니다. 네비게이션에서 정의한 목적지의 지정 프래그먼트로, 여러 프래그먼트로 된 화면들을 바꾸어주는 기능을 담당합니다. 레이아웃 리소스에 정의합니다.

이역시도 윗단의 네비게이션 그래프에서 지정한 것과 연결되어 작동합니다. id는 네비게이션 그래프 XML 파일에 기재된 것과 잘 연관시켜 코딩해야 합니다. 구조가 잘 맞으면 일단 준비가 거의 끝난 것이고, 실질적인 코틀린 코드는 NavController 객체를 써서 작업하면 됩니다.

NavController는 자바나 코틀린 코드인데 네비게이션 그래프에서 정의한 네비게이션 구조를 따라 NavHostFragment가 보여주는 화면을 이동하게 합니다. 대표적인 코드 구문으로 클릭리스너에 navigate() 함수를 쓰는 방법이 있습니다.

이 최소 구조를 토대로 여러 형태로 변환을 해서 쓰면 될 것입니다. 아직 완성된 구현을 안해보았는데 성공하면 소스코드를 공개해보도록 하겠습니다.

R.string 참조 대신 R.array 참조로 배열 생성하기

strings.xml 파일의 엘리먼트들은 값을 가지고 있습니다. 태그가 둘러싼 가운데 끼어있는 값이죠. 이를 읽어올때 R.string 참조를 쓰면 R.string 다음에 오는 문자열을 조사해서 strings.xml 파일에 들어있는 엘레먼트 값을 읽어옵니다. 아래와 같이 실험해볼 수 있습니다.

만일 name 어트리뷰트가 hello1부터 hello10까지 있어서 규칙적으로 엘리먼트 속성의 하나가 일괄적으로 숫자가 증가하도록 된 경우 아래 코드처럼 배열로 저장해야 할 때가 있습니다.

이렇게 해서 함수에 전달하면 작동하지 않습니다. greetings[i] 값은 문자열 값이지, R.string 참조가 지시하는 값이 아니기 때문입니다.

이 경우 배열 생성을 꼭 해야 한다면, R.array 참조를 써볼만합니다.

strings.xml 파일에

처럼 하나의 R.array 참조 이름 아래에 item을 기재하고

처럼 쓰는게 가능해집니다. 그러면 위에 예시된 코드처럼 배열을 일부러 루프 돌려 하지 않아도 되고, R.string 참조 문자열을 쓸 궁리를 안해도 해결됩니다.

https://developer.android.com/guide/topics/resources/string-resource?hl=ko