AppBar(앱바)란, 앱에서 화면 위쪽의 꾸밀 수 있는 영역을 의미합니다. 이 앱바 영역에는 이동할 수 있는 내비게이션 아이콘을 배치하거나 로그인 등 여러가지 기능(액션:Action)을 넣을 수 있도록 하여 앱을 좀 더 편리하게 사용할 수 있도록 하는 영역을 말합니다.
2. ActionBar(액션바)의 정의?
ActionBar(액션바)란, 위에서 정의한 AppBar(앱바)의 영역에 실질적으로 네비게이션 아이콘 같은 기능을 넣을 수 있도록 안드로이드 초기부터 제공되던 클래스를 말하는데요, 다만 안드로이드 버전이 업그레이드될 때마다, 즉 버전이 달라질 때마다 다양한 기능이 추가되고 변경되었기 때문에, 기기에 설치된 안드로이드 버전마다 다른 동작을 하게 되는 결과를 초래하게 됩니다. (단점) 이는 다른 말로 버전 하위 호환성 문제가 있다고 합니다. (Backward Compatibility) 예전 버전의 안드로이드 스튜디오에서는 기본적으로 ActionBar가 나타나도록 설정되어 있었지만, 최근 버전부터는 액션바가 나타나지 않도록 NoActionBar 형태로 세팅되어 있습니다.
3. ToolBar(툴바)의 정의?
ToolBar(툴바)란, 위 액션바의 큰 단점을 해결하기 위해, 안드로이드 버전이 달라지더라도 동일한 동작(액션)을 하도록, 안드로이드 5.0 (API Level 21)부터 추가된 위젯(Widget)을 말하는데요, 주로 이 툴바를 사용하여 편리하게 앱바(AppBar) 영역을 만들 수 있도록 하였습니다. 그리고 위젯을 직접 사용하지 않더라도 기본적으로 activity_main.xml에 toolbar.xml을 추가(include)하여 툴바 기능을 하도록 처리할 수 있습니다.
4. ToolBar(툴바) 만들기 예제
그럼, 가장 기본적인 ToolBar(툴바)를 만들어 볼 텐데요. layout 폴더에 toolbar.xml 파일을 추가하여 activity_main.xml에서 이와 같은 <include layout="@layout/toolbar" /> include 태그를 이용해 툴바기능을 하도록 해볼게요. 우선, 툴바 사용을 위해서는 액션바가 없는 조건으로 해야 하는데요, 기본적으로 NoActionBar로 설정되어 있기에 특별히 건드릴 것은 없습니다.
< 예제 실행 결과 이미지 미리 보기 >
그리고 "← 화살표"와 "Home" 글자와 "☰" 메뉴기호를 각각 터치할 때 어떤 액션을 취할 수 있는데요, 여기서는 간단하게 Toast 명령어로 메시지를 띄워보도록 할게요.
① 그럼, 안드로이드 스튜디오를 실행시키고, 새 프로젝트를 여세요.
여기서는 프로젝트 이름을 "AppBar_ToolBar"로 했습니다.
② build.gradle.kts에 뷰바인딩 기능 사용을 위해, "viewBinding.isEnabled = true"를 추가하고 "Sync Now"를 클릭해 주세요.
③ layout 폴더에서 마우스 우클릭(RMB) >> New >> Layout Resource File을 선택하여 toolbar.xml 파일을 만들어 주세요.
layout 방식은 RelaytiveLayout으로 해주세요.
④ 툴바에는 하나의 텍스트와 두 개의 이미지를 넣을 건데요, 이 두 개의 이미지는 아래처럼 에셋에서 다운로드하여 처리할게요.
Vector Asset창이 열리면 Clip art를 클릭하세요.
검색창에 "arrow"로 검색하고 "arrow back"를 선택 후, OK 클릭하세요.
Color를 클릭하여 흰색(FFFFFF)을 선택하고 Next 클릭 후 Finish를 클릭하세요.
그럼, 안드로이드 스튜디오의 프로젝트 탐색창에 Res >> drawable >> 폴더에 아래와 같이 화살표 파일이 다운로드된 것을 볼 수 있어요.
이번 실습은 메인 창에서 흔히 사이드 메뉴라고 하는 Sub Menu를 여는 Drawer 기능에 대해 다루어 보려고 하는데요, 영어 뜻 그대로 마치 서랍(Drawer)을 열고 닫는 기능을 통해 평소에는 메인 화면을 깔끔하게 보여주고 필요시 서브 메뉴를 열어 특정 메뉴를 선택할 수 있도록 해주는 기능입니다.
먼저 안드로이드 스튜디오를 실행하고 New Project로 아래와 같이 Empty Views Activity를 선택해 주세요.
title 제목으로 Navigation Drawer를 입력했습니다.
Drawer 기능을 이용하려면 적용하고 싶은 곳, 예를 들어 activity_main.xml에 Drawer Layout을 적용해 주면 되는데요, 그리고 Drawer Layout은 네비게이션 아이콘을 클릭하거나 사이드를 드래그했을 때 기존 레이아웃 위에 새로운 메뉴를 보여주게 되는데, 이것이 바로 Navigation View입니다. 따라서 Drawer 기능을 이용한다는 뜻은 Drawer Layout과 Navigation View를 세트로 같이 사용하게 되며, 아래와 같은 구조로 사용해야 하니 기억해 주세요. 왜냐하면 메인뷰 화면(View)이 먼저 존재하고 네이게이션뷰가 그 위로 오버랩 되기 때문이죠. 따라서 배치 순서를 Drawer Layout을 먼저 배치하고 그 아래에 Navigation View가 위치해야 합니다. 만약 순서가 바뀔경우 에러가 발생하게 됩니다. (아래 순서 참고)
위 코드에서 Item1~3 앞에 아이콘이 나오도록 하려면 넣고 싶은 아이콘 이미지를 res>>drawable 폴더에 넣어주면 되는데요, 여기서는 간편하게 제공되는 클립아트(Clip Art) 기능인, Vector Asset에서 찾아서 넣었습니다. 그럼, 아래와 res>>drawable 폴더를 RMB 해서 Vector Asset을 클릭해 주세요. 여기서는 모두 동일한 집(Home) 아이콘을 선택했어요.
위 코드에서 작성 중간에 toggle = ActionBarDrawerToggle(this,drawerLayout, R.string.open, R.string.close) 코드에서,
open이라는 부분과 close라는 부분에서 붉은색으로 표시가 뜰 텐데요, 그때 마우스를 Open 글자 위에 클릭 후 올려놓고 있으면 Sub메뉴가 뜨는데 "Create String Value Resource"를 클릭해서 아래처럼 각각 Open이라는 Value값을 입력해 주세요. 그리고 이어서 Close도 동일하게 진행해 주세요.
< open >
< close도 동일하게 >
그리고 내용에 보면, Drawer 메뉴가 오버랩 되며 열렸을 때 Item 1이나 Item 2, Item 3 등을 각각 선택했을 때 특정 View 페이지로 넘어가게(열게) 할 수도 있으며, 여기서는 간단히 Toast 메시지를 출력하도록 했습니다.
이렇게 하면 기본적으로 작성해야 할 것은 모두 작성했는데요, 실행해 보면, Navigation View를 선택할 수 있는 Bar가 보이지 않는데요, 이는 안드로이드 기본 설정이 NoActionBar가 적용되어 있기 때문에 그렇습니다. 이 때는 Action Bar가 보이도록 아래처럼 res>> values >> themes 폴더에 있는 themes.xml 파일을 수정해 주세요. 필요 시 night(다크 디스플레이 모드)화면 모드의 themes.xml 메뉴에도 적용해 주세요.
그리고 실행해 보면 아래처럼 실행 되는 것을 볼 수 있습니다. 삼선 Action Bar 부분을 터치해도 Drawer 메뉴가 열리며, 폰의 왼쪽 라인 부분을 한 손가락으로 살짝 누른 후 왼쪽에서 오른쪽으로 드래그해도 Drawer 메뉴가 열리는 것을 볼 수 있어요. (No ActionBar 사용시)
앞선 실습인 "ViewPager2(뷰페이저 2) 실습#1"에 이어서 화면 상단에 Tab Layout(Bar)를 추가해서 탭바로도 이동할 수 있도록 하고, "One" 첫 페이지에 간단히 텍스트 에디트(Text Edit) 기능을 넣어서 간단히 다루어 볼게요. 보통은 Tab Layout과 뷰페이저2을 함께 연결하여 서로 간 스크롤(연동)이 가능하도록 사용하는 것이 일반적이에요.
3. fragment_one.xml 과 fragment_two.xml , fragment_three.xml 파일 작성하기 아래처럼 적당한 색상과 배경으로 3개의 파일의 id를 서로 다르게 하여 작성해 주세요. (단, 이번 실습에서는, FrgmentOne~Three.kt 클래스 파일의 내용은 사용하지 않기 때문에 자동 생성된 형태 그대로 두세요) ① fragment_one.xml
안드로이드 스튜디오에서 시뮬레이션(Run) 시도 중 아래와 같은 에러를 발견했을 때 해결방법입니다.
【 ↓ 에러 내용 ↓】
2 issues were found when checking AAR metadata: 1. Dependency 'androidx.core:core-ktx:1.15.0' requires libraries and applications that depend on it to compile against version 35 or later of the Android APIs. :app is currently compiled against android-34. Also, the maximum recommended compile SDK version for Android Gradle plugin 8.5.1 is 34. Recommended action: Update this project's version of the Android Gradle plugin to one that supports 35, then update this project to use compileSdk of at least 35.
Note that updating a library or application's compileSdk (which allows newer APIs to be used) can be done separately from updating targetSdk (which opts the app in to new runtime behavior) and minSdk (which determines which devices the app can be installed on).
2. Dependency 'androidx.core:core:1.15.0' requires libraries and applications that depend on it to compile against version 35 or later of the Android APIs. :app is currently compiled against android-34. Also, the maximum recommended compile SDK version for Android Gradle plugin 8.5.1 is 34.
Recommended action: Update this project's version of the Android Gradle plugin to one that supports 35, then update this project to use compileSdk of at least 35.
Note that updating a library or application's compileSdk (which allows newer APIs to be used) can be done separately from updating targetSdk (which opts the app in to new runtime behavior) and minSdk (which determines which devices the app can be installed on).
분명 코드 부분에서는 문제가 없었으나, Gradle plugin 버전을 업데이트(상향) 하라는 내용인데, 결론은 현재 안드로이드 스튜디오에 설치된 SDK 34 버전을 SDK 35 버전으로 상향하면 해결되는 경우입니다. 안드로이드 스튜디오를 오래된 버전을 사용하고 있다면 공식사이트에서 최신 버전을 받아서 설치하면 되며(UI 같은 것도 조금 바뀌었습니다), 만약, 설치한 지 24년 3월 이후 설치했다면 즉, 거의 최근 버전이라면, 안드로이드 스튜디오를 새로 다운로드할 필요 없이, 안드로이드 스튜디오 메뉴에서 간단하게 해결할 수 있습니다.
1. File → Project Structure → Modules → Compile Sdk Version을 선택하고 API 35(최신 또는 원하는 버전)으로 적용한 후 안드로이드 스튜디오를 재실행해 주면 해결됩니다.
◈ ATtiny 85 칩과 PCB를 이용하여 크리스마스 선물용 LED 멜로디 키트를 DIY 해 보세요.
캐롤이나 동요, 혹은 자신이 좋아하는 곡의 멜로디를 코딩하여 키트로 출력할 수 있어요. 아래 이미지처럼 PCB로 제작해서 선물용에 어울리게끔 만들어 봤습니다. 크리스마스트리와 눈사람 두 가지 버전으로 만들었는데요, 하지만, 가정에서는 PCB를 직접 제작하기 쉽지 않죠, 그런데 JLC PCB 사이트 같은 곳을 이용한다면 적은 비용으로 쉽고 빠르게 이렇게 완성된 PCB기판을 받아 볼 수 있습니다.
1. 제작 준비하기
실습을 위해 아래와 같은 파일들을 준비해 보세요.
먼저, 아두이노 처럼 프로그래밍이 가능한 ATtiny85 IC가 필요합니다. 그리고 ATtiny85에 코드를 업로드하기 위한 아두이노 보드가 필요합니다.
8 pin IC 소켓은 tiny85 칩을 PCB에 바로 납땜해 버리게 되면 다른 곡을 다시 업로드할 수 없게 되므로 필요합니다. 콘덴서 부품은 Reset과 IC를 보호하기 위한 용도이므로 용량값에 구애받지 말고 가지고 있는 것을 사용하면 됩니다. 7805 IC는 5V이상의 입력 전원을 5V로 일정하게 출력해주는 정전압 IC로서 tiny85 칩을 보호하기 위한 용도로 사용됩니다. 그래서 작동 시간을 더 늘리기 위해, 배터리를 별도로 연결할 때, 더 유용하게 활용됩니다.
멜로디를 울려줄 스피커나 부저, 어떤것이든 가능한데요 소리의 떨림이 좀 있지만 크기가 작은 부저를 사용해도 되고, 크기가 조금 크지만 좀 더 좋은 소리를 낼 수 있는 스피커를 사용해도 됩니다.
당장 PCB기판이 없더라도 브레드 보드에 회로를 구성해서 실습해 볼 수 있도록 브레드보드를 준비하면 됩니다.
ATtiny85의 기본 사용법과 부트로더 올리는 영상은 아래 링크 게시글을 참고해 주세요.
【 중요 】 위의 게시물에도 추가 하였지만, ATtiny 보드 라이브러리만 설치해서 진행할 경우 tiny85 칩에 코딩해서 LED를 동작시키고 하는 것은 문제없으나, 멜로디 음 출력이 되지 않는 문제가 있어요. 그래서 이를 해결한 다른 보드 라이브러리(ATtiny core)를 추가로 설치해서 진행하면 해결할 수 있습니다.
다만, ATtiny core 라이브러리만 진행했을 경우에는 코딩까지는 문제 없으나 코딩한 대로 동작이 되지 않고 이상한 출력을 내며 제대로 동작되지 않는데요, 특히 부트로더가 올려지지 않은 새 칩을 코딩하면 이런 현상이 벌어지는데, 따라서 이를 해결하는 (코딩하는) 순서는 아래처럼 해야 합니다.
음계란 음악에 쓰이는 음을 높이의 차례대로 배열한 음의 층계를 말하는데요. "도, 레, 미, 파, 솔, 라, 시, 도" 의 8가지 음계를 옥타브라고 합니다.
각 음계의 이름을 계이름이라하며 세 가지 언어로 혼용해서 부르고 있습니다. 주로 악보에는 영문 음계로(코드) 표기합니다. 그런데 이런 음(소리)은 어떤 물질의 떨림(진동)에의해 만들어지는데요. 물질이 빠르게 진동한다면 높은 음이 나며, 느리게 진동한다면 낮은음을 발생시키게 되죠. 빠르게 진동하는 것을 주파수가 높다라고 하며, 느리게 진동하면 주파수가 낮다고 해요. 1초에 1회의 진동(떨림)을 주파수 1Hz로 부르며, 100떨리면 100Hz라고 말합니다.
낮은 도와 높은 도(한 옥타브)의 주파수(진동수)는 음 높이가 두 배 차이나며 (레~레, 미~미, 파~파... 마찬가지) 인간의 귀는 이 두 음을 높이는 다르지만 서로 같은 음으로 인식하게 되죠. 서로 다른 종류의 악기로 합주를 할 때 음이 일치가 되는 이유는 각각의 음 높이가 특정 주파수로 정리되었기에 가능한 것입니다.
가장 낮은 1옥타브 C(도)의 주파수는 32.7032Hz이며 그 위로 2옥타브 C의 주파수는 그 두 배인 65.4064Hz 임을 알 수 있죠, 샾(#) 음계는 반정도를 올린 음으로 이해하면 되며, 반음을 내릴 때는 플랫(♭) 기호를 사용합니다.
이제 이런 주파수를 참고해서 악보의 각 음계를 주파수 값으로 출력해내면 스피커의 떨림에 의해 악보대로 음악이 연주되는 것이죠. 그리고 아두이노 코드를 작성할 때는 소수점 이하를 반올림 한 값으로 사용하고, 이 주파수 값들을 pitches.h라는 헤더파일 형태로 정리해서 이용합니다.
그리고 음에는 음의 길이를 정해 줄 수 있는데요, 음의 길이에 따라 그림과 같은 음표로 구분해서 악보에 표시합니다.
그럼, 예시로 쉬운 동요 한 곡을 코드로 만드는 과정을 보여드릴게요.
먼저, 악보를 보고 계이름과 음의 길이를 다음 처럼 표시합니다.
이것을 Pitches.h 헤더파일을 참고해서 코드로 적으면 아래와 같습니다.
지금은 4옥타브 영역대를 메인 음역대로 했으나 만약 음정을 높이고 싶다면, C5, E5, G5... 이런 형태로 작성하면 됩니다.
이런 방법으로 어떤 곡이든 악보를 보고 코드화하여 멜로디를 출력할 수 있어요. 그리고 이번 프로젝트에서 각각의 음을 출력할 때마다 LED가 깜빡이도록 회로를 구성했어요.
기본적으로는 아두이노에 이런 형태로 연결해서 동작시킬 수 있지만.
누군가에게 선물할 수 있는 형태로 만들기위해 이번 프로젝트를 준비했습니다.
따라서 나노 보드 대신, 크기가 매우 작은 tiny85 칩을 사용했고, 배터리도 작은 리튬 배터리를 사용했으며, LED도 많이 넣어 볼거리가 좀 있도록 하였어요.
그리고 PCB로 제작할 때 장점은 보드 디자인을 원하는 모양으로 만들 수 있고, 선 연결을 직접 할 필요 없이 부품만 꽂고 땜하면 바로 완성되기 때문에 장점이 많습니다.
회로 설계는 이와 같이 했고요, 좀 더 화려하게 하고 싶다면, LED와 저항을 병렬로 더 추가해서 제작하면 됩니다.
3V의 CR2032배터리는 직렬로 연결하여 6V의 입력 전압을 만들어 주었고요, ATtiny85 칩의 동작전압은 2.7~5.5V인데 6V정도의 전압은 별문제는 없겠지만, 9V건전지 등 다른 전원을 사용할 때를 대비해서, 5V의 고정적인 출력으로 제어해 주는 7805 정전압 IC를 추가하였어요. (tiny85 IC보호)
2. 멜로디 회로 설계 및 PCB 보드 디자인하기
지금 보이는 이미지는 OrCAD라는 전자캐드 툴을 이용해서 회로를 디자인 하고 있는 이미지예요.
PCB(인쇄회로기판) 보드 설계는 먼저 1. 도면을 설계 작성하고 2. PCB보드 외형을 디자인하는 형태로 진행됩니다. 만약, CAD툴에서 기본적인 부품은 제공되지만, 없는 부품이 있다면 이렇게 만들어서 사용합니다.
아래 이미지가 1. 도면을 설계하는 이미지입니다.
위 도면 디자인 파일(Cadence OrCAD Capture Design File)
아래는 직접 추가한 라이브러리 파일입니다. (CR2032)
아래는 PCB Board Design File입니다. Tree 버전과 눈사람 버전입니다.
아래는 CR2032와 Slide3P의 PCB 라이브러리 파일(Package Symbol)입니다.
도면 디자인에서는 각 부품 간 연결 정보만 있기 때문에, PCB디자인에서는 부품들의 실제 사이즈에 대한 정보가 필요합니다. 이것을 footprint 값이라고 하는데요.
이제 이정보들을 PCB Layout 툴로 넘겨서 부품의 배치와 패턴을 디자인해 줍니다.
패턴을 완성시킨 후 인쇄회로기판(PCB)을 출력하기 위한 각 층별 팬턴에 대한 필름을 생성하고, 그리고 부품을 삽입하기 위한 홀 드릴에 대한 정보 파일과 Tree 모양의 기판 형태를 제단 해주는 NC 데이터도 함께 만들어 주는데요, 이 파일들의 묶음을 거버(Gerber) 파일이라고 합니다.
3. JLC PCB에 보드 주문하기
그럼 JLC PCB에서 어떻게 PCB를 주문하는지 과정을 알려드릴게요.
1. JLCPCB.COM 웹페이로 접속해서 회원가입을 해주세요.
2. 압축한 거버 파일을 화면에 보이는 곳으로 드래그하거나 열기로 넣어 주세요.
실제 주문에 사용한 거버 파일입니다.
① 크리스마스트리 Tree 디자인 거버파일
② Snow Man 눈사람 디자인 거버 파일
3. 업로드가 완료되면 보드 디자인을 미리 볼 수 있는 거버뷰어를 제공해요(클릭).
4. 보드를 천천히 살펴보면서 자신의 설계에 문제가 없었는지, 꼼꼼히 확인해 주세요. 만약 자신의 설계에 문제가 있었다면 보드가 제대로 동작하지 않으니 반드시 확인해 주세요.
5. 지금 정도의 프로젝트라면 대부분 기본 설정 사항으로 해결이 될 거예요. 소재는 FR4로 선택하고, 앞뒤 양면으로 설계했을 경우 레이어는 2로 선택하면 됩니다.
치수는 자동으로 표시되는데요, 만약 보드 사이즈가 가로x세로 100mm 이하라면, 추가 비용 없이 이용가능합니다. 100mm 이상일 경우 화면에 표시된 금액에서 사이즈에 따라 추가 요금을 지불해야 합니다.
PCB수량은 기본 5장이 제공되고 필요할 경우 더 많은 수량을 선택하세요.
PCB 보드의 색은 7가지 중에서 선택가능하며, 녹색을 제외한 다른 색상을 선택할 경우 제작기간이 하루 더 소요되니 참고하세요.
나머지 옵션은 크게 변경할 일은 없으니 천천히 확인해 보세요.
그리고 JLC PCB에서는 추가 비용을 지불하면 PCBA라고 하여 회로에 실장 되는 부품을 직접 구해서 납땜까지 제공해 주는 옵션도 있습니다.
손으로 납땜이 조금 어려운 SMD 타입의 부품이 많이 있을 경우 이 옵션을 생각해 보면 좋겠네요.
추가로 보드 외형을 눈사람으로 디자인해 봤는데요, 이 보드도 같이 주문해 볼게요.
모든 사항이 체크되었으면 쇼핑 카트에 추가합니다. 장바구니 보기로 들어갑니다.
주문 품목을 모두 선택하고 Secure Checkout를 클릭하면 배송사를 선택할 수 있습니다.
회원가입 시 제공되는 쿠폰들을 사용할 수 있습니다. 쿠폰 적용은 지금 단계에서는 표시되지 않고 결제 마지막 단계에서 적용되니 넘어가세요. 배송지 주소를 영문으로 정확하게 입력해 주세요.
PCCC 항목에는 관세청 사이트에서 개인통관고유번호를 발급받아 입력하면 됩니다.
결제 방식은 보통 신용카드로 하면 됩니다.
결제 후 주문내역보기(Order history)로 들어가 보면, 데이터 확인을 하고 주문제작에 들어가게 됩니다.
만약, 업로드한 데이터가 잘못되거나 구성요소가 빠진 게 있다면 Audit Failed라는 화면이 교체하기(재업로드) 버튼을 클릭하여 수정된 파일을 다시 업로드하면 됩니다.
진행과정에서 중요한 사항을 등록한 이메일로 알려주니 주문 후에는 계속 메일을 체크해 주세요.
4. PCB 보드 배송과 품질 확인
DHL economy로 주문했고 매우 빠르게 배송받을 수 있었어요
안전하게 진공 버블 포장으로 문제없이 잘 배송되었네요.
제품의 마감이나 실크데이터 인쇄 품질이 아주 마음에 듭니다.
5. PCB 보드에 부품 실장과 동작
그럼, PCB 보드에 부품들을 올려 납땜해 볼게요.
건전지는 +방향이 위로 향하게 넣어 주어야 합니다.
주의해야 할 것은 LED, IC, 7805, 전해콘덴서, 부저 등의 부품은 + - 극성을 구분해서 장착해야 합니다.
IC에 아두이노 코드를 프로그래밍하고 교체할 때는 이렇게 핀셋을 이용해서 핀이 휘지 않도록 주의하세요.
그럼 전원 스위치를 켜서 잘 작동되는지 볼게요.
네, 아주 잘 작동되네요.
6. "3D 프린팅 스탠드" 디자인과 출력
그리고 PCB를 받쳐줄 받침대를 3D 프린터로 만들어서 세워볼게요.
우선 두 가지 버전으로 준비했는데요, 아래 이미지와 같은 Tree 트리나무 전용 스탠드 A 버전과
아래 이미지와 같은 Snow Man & Tree 겸용 스탠드 B 버전으로 준비해 봤어요. 이 파일들에는 모두 Fusino360 모델링 파일인 ***.f3d 파일과, ***.stl 파일과, ***.gcode 파일을 포함하였어요.
두 가지 버전을 스탠드에 세운 모습은 아래와 같습니다.
멜로디는 하나의 IC에 멜로디를 계속 바꾸어 업로드할 수 있고요, IC가 여러 개 있다면 서로 다른 멜로디를 업로드해서 이렇게 바꾸어 가며 사용할 수 있어요.
7. 아두이노 및 tiny85 멜로디 코드
[ 멜로디 코드 ]
① 먼저, "곰 세 마리" 곡의 아두이노 우노(나노)용 코드는 아래와 같아요.
#include "pitches.h"
#define Speaker 5 // 스피커를 연결할 출력핀 설정
#define LED1 2
#define LED2 3
#define LED3 4
// 멜로디를 아래 배열에 순서대로 넣으세요
int melody[] = { // [ 멜로디에 관한 배열 ]
NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, // 곰 세 마 리 가
NOTE_E4, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_C4, // 한 집 에 있 어
NOTE_G4, NOTE_G4, NOTE_E4, NOTE_G4, NOTE_G4, NOTE_E4, // 아 빠 곰 엄 마 곰
NOTE_C4,NOTE_C4,NOTE_C4, // 애기곰
NOTE_G4,NOTE_G4,NOTE_E4,NOTE_C4, NOTE_G4,NOTE_G4,NOTE_G4, // 아빠곰은 뚱뚱해
NOTE_G4,NOTE_G4,NOTE_E4,NOTE_C4,NOTE_G4,NOTE_G4,NOTE_G4, // 엄마곰은 날씬해
NOTE_G4,NOTE_G4,NOTE_E4,NOTE_C4, // 애기곰은
NOTE_G4,NOTE_G4,NOTE_G4,NOTE_A4,NOTE_G4, // 너무귀여워
NOTE_C5,NOTE_G4,NOTE_C5,NOTE_G4,NOTE_E4,NOTE_D4,NOTE_C4 // 으쓱으쓱 잘한다
};
float noteDurations[] = { // [ 음의 길이에 관한 배열 ]
// 1 = 1박(4분음표) , 0.5 = 반박(8분음표) , 2 = 2박(2분음표)
1, 0.5, 0.5, 1, 1,
1, 0.5, 0.5, 1, 1,
0.5, 0.5, 1, 0.5, 0.5, 1,
1, 1, 2,
// 아빠곰은...
1, 1, 1, 1, 1, 1, 2,
1, 1, 1, 1, 1, 1, 2,
// 애기곰은 너무 귀여워...
1, 1, 1, 1,
0.5, 0.5, 0.5, 0.5, 2,
1, 1, 1, 1, 1, 1, 2
};
void setup()
{
pinMode(LED1,OUTPUT); // LED1는 ATtiny85의 GPIO1번 핀을 의미함(※ 따로 선언을 하지 않아도 됨)
pinMode(LED2,OUTPUT);
pinMode(LED3,OUTPUT);
digitalWrite(LED1, LOW);
digitalWrite(LED2, LOW);
digitalWrite(LED3, LOW);
delay(1000);
// LED1, LED2, LED3에 각각 다른 LED가 연결되어 있고
// 배열에서 하나씩 멜로디를 읽을 때마다 LED1, LED2, LED3으로 번갈아 가며 출력(LED ON)하도록 합니다.
for (int thisNote = 0; thisNote < sizeof(melody); thisNote++)
{
if(thisNote%3 == 1) {
digitalWrite(LED1, LOW);
digitalWrite(LED2, HIGH);
digitalWrite(LED3, LOW);
}
else if(thisNote%3 == 2) {
digitalWrite(LED1, LOW);
digitalWrite(LED2, LOW);
digitalWrite(LED3, HIGH);
}
else {
digitalWrite(LED1, HIGH);
digitalWrite(LED2, LOW);
digitalWrite(LED3, LOW);
}
// 음표 길이를 계산하려면 1초를 500ms로 곱합니다
// 템포는 333ms로 설정합니다. 이 값을 변경하면 템포가 변경됩니다.
int noteDuration = 333 * noteDurations[thisNote];
tone(Speaker, melody[thisNote], noteDuration);
// 음표를 구분하려면 음표 사이의 최소 시간을 설정합니다.
// 음표의 길이에 +30% 정도가 잘 적당합니다
int pauseBetweenNotes = noteDuration * 1.20;
delay(pauseBetweenNotes);
noTone(0); // Tone 함수 재생 멈춤
}
digitalWrite(LED1, LOW);
digitalWrite(LED2, LOW);
digitalWrite(LED3, LOW);
}
void loop() {
}
아래는 곰 세 마리의 ATtiny85 버전용 코드예요.
// PB1 , PB2 ... wat not declared... 에러가 날 경우, 보드 선택을 ATtiny85로 했는지? 확인할 것!
#include "pitches.h"
#define Speaker 0 // 스피커를 연결할 출력핀 설정
// #define rest 0 // 음악 쉼표 주파수 0 Hz 설정
// 멜로디를 아래 배열에 순서대로 넣으세요
int melody[] = { // [ 멜로디에 관한 배열 ]
NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, // 곰 세 마 리 가
NOTE_E4, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_C4, // 한 집 에 있 어
NOTE_G4, NOTE_G4, NOTE_E4, NOTE_G4, NOTE_G4, NOTE_E4, // 아 빠 곰 엄 마 곰
NOTE_C4,NOTE_C4,NOTE_C4, // 애기곰
NOTE_G4,NOTE_G4,NOTE_E4,NOTE_C4, NOTE_G4,NOTE_G4,NOTE_G4, // 아빠곰은 뚱뚱해
NOTE_G4,NOTE_G4,NOTE_E4,NOTE_C4,NOTE_G4,NOTE_G4,NOTE_G4, // 엄마곰은 날씬해
NOTE_G4,NOTE_G4,NOTE_E4,NOTE_C4, // 애기곰은
NOTE_G4,NOTE_G4,NOTE_G4,NOTE_A4,NOTE_G4, // 너무귀여워
NOTE_C5,NOTE_G4,NOTE_C5,NOTE_G4,NOTE_E4,NOTE_D4,NOTE_C4 // 으쓱으쓱 잘한다
};
float noteDurations[] = { // [ 음의 길이에 관한 배열 ]
// 1 = 1박(4분음표) , 0.5 = 반박(8분음표) , 2 = 2박(2분음표)
1, 0.5, 0.5, 1, 1,
1, 0.5, 0.5, 1, 1,
0.5, 0.5, 1, 0.5, 0.5, 1,
1, 1, 2,
// 아빠곰은...
1, 1, 1, 1, 1, 1, 2,
1, 1, 1, 1, 1, 1, 2,
// 애기곰은 너무 귀여워...
1, 1, 1, 1,
0.5, 0.5, 0.5, 0.5, 2,
1, 1, 1, 1, 1, 1, 2
};
void setup()
{
pinMode(PB1,OUTPUT); // PB1는 ATtiny85의 GPIO1번 핀을 의미함(※ 따로 선언을 하지 않아도 됨)
pinMode(PB2,OUTPUT);
pinMode(PB3,OUTPUT);
digitalWrite(PB1, LOW);
digitalWrite(PB2, LOW);
digitalWrite(PB3, LOW);
delay(1000);
for (int thisNote = 0; thisNote < sizeof(melody); thisNote++)
{
if(thisNote%3 == 1)
{
digitalWrite(PB1, LOW);
digitalWrite(PB2, HIGH);
digitalWrite(PB3, LOW);
}
else if(thisNote%3 == 2)
{
digitalWrite(PB1, LOW);
digitalWrite(PB2, LOW);
digitalWrite(PB3, HIGH);
}
else
{
digitalWrite(PB1, HIGH);
digitalWrite(PB2, LOW);
digitalWrite(PB3, LOW);
}
// 음표 길이를 계산하려면 1초를 500ms로 곱합니다
// 템포는 333ms로 설정합니다. 이 값을 변경하면 템포가 변경됩니다.
int noteDuration = 333 * noteDurations[thisNote];
tone(Speaker, melody[thisNote], noteDuration);
// 음표를 구분하려면 음표 사이의 최소 시간을 설정합니다.
// 음표의 길이에 +30% 정도가 잘 적당합니다
int pauseBetweenNotes = noteDuration * 1.20;
delay(pauseBetweenNotes);
noTone(0); // Tone 함수 재생 멈춤
}
digitalWrite(PB1, LOW);
digitalWrite(PB2, LOW);
digitalWrite(PB3, LOW);
digitalWrite(PB0, LOW);
}
void loop() {
}
② 아래는 " We wish your Marry Christmas" 곡입니다.
// PB1 , PB2 ... wat not declared... 에러가 날 경우, 보드 선택을 ATtiny85로 했는지? 확인할 것!
#include "pitches.h"
#define Speaker 0 // 스피커를 연결할 출력핀 설정
// #define rest 0 // 음악 쉼표 주파수 0 Hz 설정
// 멜로디를 아래 배열에 순서대로 넣으세요
int melody[] = { // [ 멜로디에 관한 배열 ]
//We wish you a merry christmas
NOTE_C4, NOTE_F4, NOTE_F4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D4, NOTE_D4,
NOTE_D4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_C4,
//We wish you a merry christmas
NOTE_C4, NOTE_A4, NOTE_A4, NOTE_AS4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_D4,
NOTE_C4, NOTE_C4, NOTE_D4, NOTE_G4, NOTE_E4, NOTE_F4,
//Good tidings we bring to you and ...
NOTE_C4, NOTE_F4, NOTE_F4, NOTE_F4, NOTE_E4,
NOTE_E4, NOTE_F4, NOTE_E4, NOTE_D4, NOTE_C4,
//Good tidings for Christmas ...
NOTE_C4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_C5, NOTE_C4,
//and a happy new year
NOTE_C4, NOTE_C4, NOTE_D4, NOTE_G4, NOTE_E4, NOTE_F4
};
float noteDurations[] = { // [ 음의 길이에 관한 배열 ]
//We wish you a merry christmas
// 1 = 1박(4분음표) , 0.5 = 반박(8분음표) , 2 = 2박(2분음표)
1, 1, 0.5, 0.5, 0.5, 0.5, 1, 1,
1, 1, 0.5, 0.5, 0.5, 0.5, 1, 1,
//We wish you a merry christmas and a happy new year
1, 1, 0.5, 0.5, 0.5, 0.5, 1, 1,
0.5, 0.5, 1, 2, 1, 2,
//Good tidings we bring to you and ...
1, 1, 2, 1, 2,
1, 1, 2, 1, 2,
//Good tidings for Christmas ...
1, 1, 2, 1, 1, 2,
//and a happy new year
0.5, 0.5, 1, 2, 1, 2
};
void setup()
{
pinMode(PB1,OUTPUT);
pinMode(PB2,OUTPUT);
pinMode(PB3,OUTPUT);
digitalWrite(PB1, LOW);
digitalWrite(PB2, LOW);
digitalWrite(PB3, LOW);
delay(1000);
// iterate over the notes of the melody:
for (int thisNote = 0; thisNote < sizeof(melody); thisNote++)
{
if(thisNote%3 == 1)
{
digitalWrite(PB1, LOW);
digitalWrite(PB2, HIGH);
digitalWrite(PB3, LOW);
}
else if(thisNote%3 == 2)
{
digitalWrite(PB1, LOW);
digitalWrite(PB2, LOW);
digitalWrite(PB3, HIGH);
}
else
{
digitalWrite(PB1, HIGH);
digitalWrite(PB2, LOW);
digitalWrite(PB3, LOW);
}
// 음표 길이를 계산하려면 1초를 500ms로 곱합니다
// 템포는 333ms로 설정합니다. 이 값을 변경하면 템포가 변경됩니다.
int noteDuration = 333 * noteDurations[thisNote];
tone(Speaker, melody[thisNote], noteDuration);
// 음표를 구분하려면 음표 사이의 최소 시간을 설정합니다.
// 음표의 길이에 +30% 정도가 잘 적당합니다
int pauseBetweenNotes = noteDuration * 1.20;
delay(pauseBetweenNotes);
noTone(0); // Tone 함수 재생 멈춤
}
digitalWrite(PB1, LOW);
digitalWrite(PB2, LOW);
digitalWrite(PB3, LOW);
digitalWrite(PB0, LOW);
}
void loop() {
}
네, 이렇게 며칠 동안 나름? 신경 써서 성공적으로 프로젝트를 마무리하였습니다. 필요한 분들에게 도움이 되었길 바라겠습니다. 그럼, 오늘도 행복한 하루 보내세요~ ^o^
프로젝트 탐색창에서 "Gradle Scripts" 항목을 펼쳐보면 "build.gradle.kts (Module :app)" 이 보이고 더블클릭해 열어서 아래 이미지처럼 viewBinding.isEnabled = true를 android { ... } 사이에 넣어주세요. 그리고 변경사항이 적용될 수 있도록 "Sync Now"를 클릭해 주세요.
Main Activity에는 아래처럼 기본 설정 코드가 들어 있을 텐데요,
이를 아래와 같이 필요 없는 항목은 지우고 ViewBinding을 사용하기 위한 형태로 바꾸어 주세요.
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
3. 전화번호 프로필(profile) 화면 구성하기.
프로필의 화면 구성은 아래와 같이 해볼게요.
프로젝트 탐색창에서 res 》 layout 에서 마우스 우클릭(이후로는 RMB로 표현)해서 New 》 Layout Resource File을 클릭하세요.
File name : 에 list_item이라 적고 OK를 클릭해 주세요.
그럼, 아래와 같이 layout 폴더에 list_item.xml이라는 파일이 생성된 것을 볼 수 있어요.
4. 액티비티 메인(activity_main.xml) 화면 구성하기.
추가한 ConstraintLayout이 클릭되어 있는 상태에서 오른쪽에 있는 Constraint Widget의 ⊕ 표시를 눌러 상위에 있는 부모 Constrait Layout과 연결을 시켜주세요.
그리고 아래 이미지처럼 각각의 마진(magin)을 8씩 주세요.
우선, 전화번호 목록에 쓰일 사람들의 사진을 대신하여 이미지 아이콘을 다운로드하여 사용해 볼 텐데요, flaticon.com에 접속하여 " profile " 로 검색해서 다운로드하여 볼게요.
검색된 아이콘들 중에 맘에 드는 것으로 몇 가지를 다운로드하여 보세요.
다운로드 표시 부분을 클릭하지 말고 이미지 가운데를 클릭하면, 가로 세로 원하는 사이즈를 선택해서 받을 수 있어요.
128픽셀을 선택해서 다운로드하세요. 512픽셀로 받아도 되지만, 기본크기가 너무 커지므로 줄여서 받아볼게요.
이 사이트의 경우 무료로는 하루 10개 정도 받을 수 있기 때문에 5개는 남자 이미지 5개는 여자 이미지로 총 10개를 다운로드하여서 아래와 같이 man1~5 , woman1~5로 네이밍 합니다.
그럼, 윈도 탐색창에서 위의 다운로드한 파일을 모두 선택해서 Ctrl + C(복사) 한 후, 안-스에서 프로젝트 탐색창의 res 》 drawable 폴더를 선택하고 Ctrl + V(붙여 넣기) 하세요.
그리고 OK 버튼을 클릭하면 아래처럼 해당 폴더에 붙여 넣기 됩니다.
물론 RMB 하여 Explorer 창을 열어 윈도 탐색창에 직업 붙여 넣기 해도 동일합니다. (해당 폴더를 바로 찾아 열어 볼 수 있음)
그리고 Attributes 속성 설정창에 보이는 Constrait Widget에서 화살표로 표시한 부분만 클릭해서 부모 Constraint Layout과 각각 연결시켜 주세요. 이때 연결되는 3곳 각각의 magin은 8 정도로 세팅할게요.
그리고 삽입한 man 이미지의 가로 폭(layout_width)과 세로 높이(layout_height)를 60dp로 설정해 주세요.
다음, Component Tree창에서 두 번째 ConstraintLayout을 선택하고 오른쪽 Constraints항목에 있는 부분에서 layout_height 만 wrap_content로 바꾸어 주세요.
다음, 제일 위에 있는 부모 ConstraintLayout을 선택해서 동일하게 layout_height를 wrap_content로 감싸주게 되면 아래 이미지처럼 이미지가 위쪽으로 정렬이 됩니다.
그다음, 이미지를 선택한 상태에서 id를 이미지 뷰를 줄인 약자(iv)로 해서 iv_profile로 설정해 볼게요.
id를 변경하고 엔터를 치면 , 아래와 같이 창이 나타나고 OK를 한 번 더 눌러주면 됩니다.
① 텍스트뷰를 끌어다 놓고, id = tv_name , text = 홍길동 입력하고, 아래처럼 Constraint Widget의 연결을 해주세요.
다음, text 속성에서 textSize = 16sp , textColor = #040404 , textStyle = Bold로 변경합니다.
② "전화번호"에 대한 TextView항목도 아래처럼 추가해 주세요.
Component Tree 창에서 tv_name을 Ctrl+C , Ctrl+V 하면 동일한 속성으로 복사가 됩니다.
그럼, id = tv_number로 바꾸고, 전화번호를 입력해 주세요. 색상도 적당한 색으로 바꾸고, Constraint Widget의 연결을 왼쪽 이름에 연결하고 정당한 magin을 입력해 주세요.
③ 동일한 방법으로 이메일에 대한 textView도 추가해서 아래처럼 적당할게 설정해 주세요.
④ 동일하게 생년월일에 대한 tv_birthday texView도 아래처럼 추가해 볼게요.
정리된 모습
5. list_item.xml에 대한 객체 모듈의 클래스 만들어주기
app 》 kotlin+java 》 com.example.recyclerview3 <RMB> 해서 New 》 Kotlin Class/File을 클릭하세요.
그리고 이름에 Profile이라고 입력하고 엔터 하면
Profile이라는 확장자가 KT인 코틀린 Class 파일이 생성된 것을 볼 수 있어요.
클래스 파일 내용은 아래와 같이 입력해서 "사진이미지, 이름, 전화번호, 이메일, 생일" 각각에 대한 인자와 받아들일 속성에 대해 정의해 주세요.
이때 이미지의 경우 숫자로 처리할 수 있기 때문에 Int형으로 정의합니다.
class Profile ( val picture : Int , val name : String , val telNum : String , val email : String , val birthDay : String )
6. 어댑터 만들기 (Adapter)
어댑터는 뷰 홀더에서 만든 뷰 객체에 적절한 데이터를 대입해서 항목을 완성하는 역할을 하는데요, 이어서 레이아웃 매니저가(LayoutManager) 어댑터가 만든 항목들을 어떻게 배치할지 결정하여 리사이클러 뷰에 출력합니다.
리사이클러뷰의 전체적인 구조는 아래와 같습니다.
그럼, 뷰홀더를 만들 때처럼, app 》 kotlin+java》 com.example.recyclerview3 <RMB> 해서 New》 Kotlin Class/File을 클릭하세요. 파일 이름은 ProfileAdapter로 할게요.
클래스 파일이 생성되면 아래와 같이 우선 작성하고, 만약 RecyclerView 글자가 빨간색으로 뜬다면, 리사이클러 뷰 위젯을 자동으로 삽입되도록 Alt+Enter 하세요.
그리고 아래처럼 코드를 이어서 작성하면, class ProfileAdapter 부분이 붉은색 밑줄이 그어지는데 마우스를 올리고 나타나는 팝업창에서 Implement members를 클릭해서 필요한 구성요소들이 자동으로 생성될 수 있도록 해줍니다. 또는 Alt+Shift+Enter
이어서, CustomViewHolder 부분이 빨간 글자로 되는데, 이 부분도 마우스를 올리고 기다리고 있으면, 클래스를 자동으로 생성할 수 있도록 해줍니다.
그럼, 뷰홀더(View Holder) 클래스가 만들어지는데요,
아래와 같이 코드를 완성해 보세요. TODO 라고 되어 있는 부분들은 삭제하고,
list_item.xml에 있는 각각의 객체들을 findViewById로 변수명을 만들어 가져옵니다. getItemCount( ) 함수는 Main Activity에 입력되는 데이터의 총개수를 구해오는 함수입니다.
package com.example.recyclerview3
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class ProfileAdapter ( val profileList: ArrayList<Profile> ) : RecyclerView.Adapter<ProfileAdapter.CustomViewHolder> ( )
{
class CustomViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView){ // : RecyclerView... 를 상속 받아 CustomViewHolder class를 작성함.
val picture = itemView.findViewById<ImageView>(R.id.iv_profile) // 인물 사진
val name = itemView.findViewById<TextView>(R.id.tv_name) // 이름
val telNum = itemView.findViewById<TextView>(R.id.tv_number) // 전화번호
val email = itemView.findViewById<TextView>(R.id.tv_email) // 이메일
val birthday = itemView.findViewById<TextView>(R.id.tv_birthday) // 생일
}
override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ProfileAdapter.CustomViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false) // parent에 있는 모든 정보(context)를 가져오는 설정 함.
return CustomViewHolder(view) // view 형태를 첨부 하여 위에서 정의한 CustomViewHolder( )를 리턴(호출) 함.
}
override fun onBindViewHolder(holder: ProfileAdapter.CustomViewHolder, position: Int) {
holder.picture.setImageResource(profileList.get(position).picture) // (MainActivity에서 데이터를 받아서) profileList에서 손으로 클릭(터치)되는 위치에 대한 정보를 가져옴.
holder.name.text = profileList.get(position).name // position 값은 데이터 0 부터 시작 됨.
holder.telNum.text = profileList.get(position).telNum // 만약 유형이 Int 숫자라면, 코드 마지막에 숫자를 문자열로 변환하는 코드를( '.toString()' ) 삽입해주면 에러나지 않음.
holder.email.text = profileList.get(position).email
holder.birthday.text = profileList.get(position).birthDay
}
override fun getItemCount(): Int {
return profileList.size // profileList의 총 데이터 갯수를 반환함.
}
}
7. 메인 레이아웃 구성하기 (activity_main.xml)
MainActivity.kt 파일을 열고서, 기본 주어지는 "HolloWorld!" textView를 지우고, 아래처럼, Common 》 RecyclerView를 끌어다 놓으세요.
그리고, Constrait Wideget의 4곳을 모두 클릭해서 parant에 모두 연결시켜 주세요.
리사이클러 뷰의 id는 rv_profile로 할게요.
8. 메인 액티비티 구성하기 (MainActivity.kt)
먼저, findViewById 대신 바인딩을 사용하기 위해, onCreate ( ) 함수 내에 바인딩 선언을 해주세요.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
그리고 profileList라는 변수를 만들고 arrayListOf ( )라는 리스트 함수를 사용하여 앞서 정의한 데이터들의 리스트를 담을 거예요.
이때 앞서 만든 Profile 클래스의 데이터 형식에 맞추어 입력해 주면 됩니다. 인물 Icon 이미지를 10개 준비했으니, 데이터도 우선 10개를 채워볼게요. class Profile ( 사진, 이름, 전화번호, 이메일, 생일 ) 형태로 작성해 주면 됩니다.
그리고 레이아웃매니저(layoutManager) 함수를 통해 어댑터가 만든 항목들을 어떻게 배치할지? (가로:HORIZONTAL, 세로:VERTICAL) 정해줍니다. 주로 위아래로 스크롤하는 형태가 되기 때문에, VERTICAL로 해주면 되고요. 이제 제일 중요한, 어댑터를 최초로 사용하게 되는데, ProfileAdapter( )를 가져와서 위에서 작성한 profileList를 담아 activity_main.xml 레이아웃의 id를 rv_profile이라고 이름 지은 RecyclerView로 바인딩하여 전달(보여주기) 해 주면 됩니다. 이 코드들은 아래와 같아요.
binding.rvProfile.layoutManager = LinearLayoutManager(this, LinearLayoutManager.H VERTICAL, false)
binding.rvProfile.setHasFixedSize(true) // 리사이클러뷰에 대한 성능개선 관련
binding.rvProfile.adapter = ProfileAdapter(profileList)
그럼, MainActivity.kt 파일의 완성된 전체 코드를 한 번에 보여드리면 아래와 같아요.
에뮬레이터에 뜬 화면을 마우스로 위아래 스크롤 해보면, 준비된 10개의 데이터가 스크롤되면서(VERTICAL) 잘 보이는 걸 알 수 있어요.
네, 대부분 잘 보이는데요, 몇몇 데이터가 Constrait (제약) 연결된 상태에 따라 이름의 길이가 짧거나 하여 이메일 주소와 생일이 겹치는 부분이 보이는데요, 데이터에 따라 크게 겹치지 않도록 Constrait Wedjet에 있는 항목들의 간격을 조정해 보세요. 생일의 TextView의 오른쪽이 전화번호의 오른쪽에 연결(연동) 시켜 놓았기 때문에 이름이 짧은 사람은 전화번호가 앞으로 당겨지게 되고 이로인하서 생일도 전화번호의 오른쪽라인으로 기준이 당겨지기 때문에 이메일과 겹치는 부분이 발생하였습니다.
따라서, 해결방법은 전화번호의 왼쪽이 연결된 이름과의 여백(magin)을 충분히 띄워 주면 됩니다.
또는 이름의 오른쪽과 연결시킨 부분을 사진의 왼쪽 부분으로 바꾸어 연결시키고 간격을 90sp로 적정하게 벌어지도록 하면, 이름의 길이에 따라 전화번호가 앞뒤는 움직이는 일은 없어서 깔끔하게 정렬된 출력을 볼 수 있어요.
이렇게 조정 후, 다시 실행시켜 보면, 깔끔하게 정렬된 것을 볼 수 있습니다.
9. 목록 클릭하여 정보 표시하기
해당 목록을 클릭하면 Toast기능을 사용하여 간단한 메시지를 출력해 보도록 할게요.
예를 들어, itemView 목록에서 엘사를 터치하면, 엘사의 "이름 [전화번호] , 이메일"을 짧은 메시지 형태로 보여주도록 할게요.
ProfileAdapter.kt 파일을 열고, onCreateViewHolder ( ) 함수 부분을 아래와 같이 수정(추가) 해 주세요.
override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ProfileAdapter.CustomViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false) // parent에 있는 모든 정보(context)를 가져오는 설정 함.
return CustomViewHolder(view).apply {
itemView.setOnClickListener {
val cursorPosition : Int = adapterPosition
val profile : Profile = profileList.get(cursorPosition)
Toast.makeText(parent.context, "-이름:${profile.name} [전화번호: ${profile.telNum}] \n-E메일: ${profile.email}", Toast.LENGTH_SHORT).show()
}
}
}
그리고 실행해서 터치해 보면, 해당하는 정보를 띄워주는 것을 볼 수 있습니다.
10. 목록 클릭하여 전화걸기
마지막으로, 해당 목록을 클릭하면 해당 연락처로 전화를 걸 수 있도록 해서 좀 더 효용성을 높여 봤습니다.
동일한 코드에 아랫부분만 수정해 보세요.
override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ProfileAdapter.CustomViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false) // parent에 있는 모든 정보(context)를 가져오는 설정 함.
return CustomViewHolder(view).apply {
itemView.setOnClickListener {
val cursorPosition : Int = adapterPosition
val profile : Profile = profileList.get(cursorPosition)
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:${profile.telNum}")
itemView.context.startActivity(intent)
}
}
}
앱을 실행시켜 보면, 서로 다른 항목을 클릭할 때마다 그 항목에 있는 전화번호로 전화 걸기가 잘 실행 되는 것을 볼 수 있네요.
※ 주의! 임의로 넣은 번호이지만 실제 해당 번호를 소유하고 있는 사람이 있을 수 있으므로, 실제 테스트하실 때는 가족이나 자신의 번호로 테스트해 보시길 바랍니다!