Post

Chapter 6. 제트팩 라이브러리

Chapter 6. 제트팩 라이브러리

‘깡샘의 안드로이드 앱 프로그래밍 with 코틀린’ 11장 학습 내용

프로젝트 바로가기

0절. ART

플랫폼 API

  • ART의 안드로이드 라이브러리

ch06-01-JavaAPI

1절. 제트팩

제트팩(JetPack)

  • 구글 안드로이드 앱 개발용 라이브러리
  • androidx로 시작하는 패키지명
    • 앱을 개발 시 필요 권장 아키텍처 제공
    • API 레벨 호환성 문제 해결
    • 플랫폼 API에서 제공하지 않는 기능 제공

2절. androidx

androidx 라이브러리

  • 화면 구성 라이브러리
라이브러리 종류설명
androidx.appcompat앱 API 레벨 호환성 해결
androidx.recyclerview목록 화면 구성
androidx.viewpager2스와이프로 넘기는 화면 구성
androidx.fragment액티비티처럼 동작하는 뷰 제공
androidx.drawerlayout옆에서 서랍처럼 열리는 화면 구성

3절. appcompat

appcompat 라이브러리

  • 안드로이드 앱 화면 구성 액티비티 생성
  • API 레벨 호환성 문제 해결
// appcompat 라이브러리 선언

implementation(lib.androidx.appcompat)
// appcompat 라이브러리 사용

import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

}

문자열 출력 구성

타이틀 문자열 출력 구성 방법
액션바 이용
툴바 이용

ch06-02-ToolBar

  • 액티비티 적용 테마 설정에 의해 결정

ch06-03-Theme

1
2
3
4
5
6
7
8
9
10
11
<!-- themes.xml 테마 스타일 선언 -->

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- 기본 테마 -->
    <style name="Base.Theme.AndroidLab" parent="Theme.Material3.DayNight.NoActionBar">
        <!-- light 테마 지정 -->
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
    </style>

    <style name="Theme.AndroidLab" parent="Base.Theme.AndroidLab" />
</resources>
1
2
3
4
5
6
7
8
9
10
11
12
<!-- AndroidManifest.xml 적용 내용 확인 -->

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.AndroidLab" >
    <!-- 생략 -->

</application>

툴바

  • activity.xml에 androidx.appcompat.widtet.Toolbar 선언
  • 액션바의 내용이 툴바에 적용되도록 지정
1
2
3
4
5
6
7
8
9
10
<!-- 레이아웃 XML에 툴바 적용 -->

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:elevation="4dp"
    android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
    app:titleTextColor="#FFFFFF" />

ch06-04-MatchToolBar

// 액션바 내용 툴바 적용

setSupportActionBar(binding.toolbar)
구성 요소
타이틀 문자열
내비게이션 아이콘
액션 아이템
오버플로 메뉴

ch06-05-ActionBar

툴바 색상 설정

  • 앱 적용 테마에 의한 결정
속성설명
colorPrimary툴바 · 버튼 배경색
colorAccent활성모드 상태 표현
statusBarColor상태바 배경색
1
2
3
4
5
6
<!-- 테마 색상 지정 -->
<style name="Base.Theme.AndroidLab" parent="Theme.Material3.DayNight.NoActionBar">
    <item name="colorPrimary">#FF0000</item>
    <item name="android:statusBarColor">#CC0000</item>
    <item name="colorAccent">#00FF00</item>
</style>

ch06-06-ToolBarColor

업 버튼 설정

  • 액션바 왼쪽에 이전 화면으로 되돌아가는 화살표 모양(←) 업 버튼

ch06-07-UpButton

등록 방법
AndroidManifest.xml 등록
액티비티 코드 등록
1
2
3
4
5
6
<!-- AndroidManifest.xml에서 업버튼 설정 -->

<activity
    android:name=".test1.TwoActivity"
    android:exported="false"
    android:parentActivityName=".MainActivity"/>
// 툴바에 업버튼 설정

supportActionBar?.setDisplayHomeAsUpEnabled(true)
  • onSupportNavigateUp() 함수 재정의
    • 업버튼 실행 시 특별한 로직 실행
    • AndroidManifest.xml 설정 없이 코드에서 업버튼 설정 희망
// 사용자가 업버튼 실행 시 함수 자동 호출되는 onSupportNavigateUp() 함수 재정의
override fun onSupportNavigateUp(): Boolean{
    Log.d("Yunseo", "onSupportNavigateUp")
    return super.onSupportNavigateUp()
}

메뉴 구성

  • 메뉴
    • 툴바의 중요한 구성 요소
    • 액티비티 화면에서 사용자 이벤트 사용 허용
액티비티 메뉴 추가 함수설명
onCreateOptionsMenu()액티비티 첫 1회만 호출
onPrepareOptionsMenu()액티비티 첫 실행 한 번 호출 후 오버플로 메뉴가 나타날 때마다 반복 호출

ch06-08-Menu

// 메뉴 구성 함수

override fun onCreateOptionsMenu(menu: Menu?): Boolean{
    val menuItem1: MenuItem? = menu?.add(0, 0, 0, "menu1")
    val menuItem2: MenuItem? = menu?.add(0, 1, 0, "menu2")
    return super.onCreateOptionsMenu(menu)
}

ch06-09-Menu2

이벤트 처리

  • onOptionsItemSelected() : 사용자가 메뉴 선택 시 이벤트 처리
  • MenuItem : 함수 매개 변수이자 이벤트가 발생한 메뉴 객체
// 메뉴 선택 시 이벤트 처리

override fun onOptionsItemSelected(item: MenuItem): Boolean = when(item.itemId){
    0 -> {
        Log.d("Yunseo", "menu1 click")
        true
    }
    1 -> {
        Log.d("Yunseo", "menu2 click")
        true
    }
    else -> super.onOptionsItemSelected(item)
}

리소스 메뉴 구현

  • res/menu 디렉토리에 메뉴 구성 XML 파일 생성

ch06-10-Menu3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- menu_main.xml -->

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/menu1"
        android:title="menu1" />

    <item
        android:id="@+id/menu2"
        android:icon="@android:drawable/ic_menu_add"
        android:title="menu2"
        app:showAsAction="always" />

    <item
        android:id="@+id/menu3"
        android:icon="@android:drawable/ic_menu_search"
        android:title="menu3"
        app:showAsAction="iffRoom" />

</menu>

showAsActions 속성

  • 액션바에 아이콘 형태로 출력
속성값 종류설명
never(기본)항상 오버플로 메뉴로 출력
ifRoom액션바 공간 있으면 액션 아이템, 없으면 오버플로 메뉴 출력
always항상 액션 아이템으로 출력
// 액티비티 코드 메뉴 XML 적용

override fun onCreateOptionsMenu(menu: Menu?): Boolean{
    menuInflater.inflate(R.menu.menu_main, menu)
    return super.onCreateOptionsMenu(menu)
}

ch06-11-Menu4

액션 뷰 이용

  • 특별한 기능 제공 : androidx.appcompat.widget.SearchView
1
2
3
4
5
6
<!-- 서치 뷰 사용 -->

<item android:id="@+id/menu_search"
    android:title="search"
    app:showAsAction="always"
    app:actionViewClass="androidx.appcompat.widget.SearchView" />

ch06-12-SearchView

서치 뷰 이용

  • MenuItem 객체를 얻은 후 객체에 등록된 SearchView 객체 획득
// 서치 뷰 검색 기능 구현

override fun onCreateOptionsMenu(menu: Menu): Boolean{
    val inflater = menuInflater
    inflater.inflate(R.menu.menu_main, menu)

    val menuItem = menu?.findItem(R.id.menu_search)
    val searchView = menuItem?.actionView as SearchView

    searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
        override fun onQueryTextChange(newText: String?): Boolean{
            // 검색어 변경 이벤트
            return true
        }
        override fun onQueryTextSubmit(query: String?): Boolean{
            // 키보드 검색 버튼 클릭한 순간의 이벤트
            return true
        }
    })
    return true
}

4절. 프래그먼트

프래그먼트

  • 액티비티처럼 동작하는 뷰
    • 액티비티에 구현되는 모든 내용을 프래그먼트 클래스에 작성

ch06-13-Fragment

프래그먼트 구현

  • Fragment 상속 후 작성하는 클래스
  • onCreateView() : 작성 함수가 자동 호출되며 반환한 View 객체가 화면에 출력
// 프래그먼트 구현

// (...생략...)
import androidx.fragment.app.Fragment

class OneFragment : Fragment() {
    lateinit var binding: FragmentOneBinding
    override fun onCreateView(
        inflater: LayoutInflater, containter: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentOneBinding.inflate(inflater, container, false)
        return binding.root
    }
}

프래그먼트 출력

1
2
3
4
5
6
7
<!-- 프래그먼트 출력 -->

<fragment
    android:name="com.example.test11.OneFragment"
    android:id="@+id/fragmentView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  • <fragment> 태그
    • activity.xml 에 등록
    • name 속성 : 프래그먼트 클래스 지정
    • 화면에 프래그먼트 출력
      • 프래그먼트 동적 제어(추가, 제거 등) 시 FragmentManager의 FragmentTransaction 클래스 함수 이용
FragmentTransaction 클래스 함수 종류설명
add(int containerViewId, Fragment fragment)새로운 프래그먼트 화면에 추가
replace(int containerViewId, Fragment fragment)추가된 프래그먼트 대체
remove(Fragment fragment)추가된 프래그먼트 제거
commit()화면 적용
// 프래그먼트 동적 제어

val fragmentManager: FragmentManager = supportFragmentManager
val transaction: FragmentTransaction = fragmentManager.beginTransaction()
val fragment = OneFragment()

transaction.add(R.id.fragment_content, fragment)
transaction.commit()

프래그먼트 생명주기

  • 액티비티의 생명주기 함수
    • 아래의 함수를 가지며 호출 시점도 액티비티와 동일
생명 주기 함수 종류
onCreate()
onStart()
onResume()
onPause()
onStop()

ch06-14-SDLC

생명 주기 단계
초기화(initialized)
생성(created)
시작(started)
재개(resumed)
소멸(destroyed)

백 스택

  • 프래그먼트가 화면에 보이지 않는 순간 제거하지 않고 저장 후 다시 이용하는 기능
사용 여부설명
미사용프래그먼트 교체 시 기존 프래그먼트는 onDestroy까지 호출 후 제거
사용프래그먼트 제거없이 onDestoryView 함수까지 호출
// 백 스택 사용 설정

transaction.addToBackStack(null)

5절. 리사이클러 뷰

리사이클러 뷰

  • 목록 화면 구성

ch06-15-RecyclerView

구성 요소설명
ViewHolder(필수)항목에 필요한 뷰 객체 보유
Adapter(필수)항목 구성
LayoutManager(필수)항목 배치
ItemDecoration(옵션)항목 꾸미기

뷰 홀더 준비

  • 각 항목에 해당하는 뷰 객체 소유
  • RecyclerView.ViewHolder 상속
// 뷰 홀더 준비

class MyViewHolder(val binding: ItemMainBinding): RecyclerView.ViewHolder(binding.root)

어댑터

  • 각 항목 생성 역할
어댑터 함수 종류설명
getItemCount()항목 개수 판단을 위해 자동 호출
onCreateViewHolder()항목 뷰를 가지는 뷰 홀더 준비를 위해 자동 호출
onBindViewHolder()뷰 홀더 뷰에 데이터 출력을 위해 자동 호출
// 어댑터 준비

class MyAdapter(val datas: MutableList<String>):
    RecyclerView.Adapter<RecyclerView.ViewHolder>(){
        override fun getItemCount(): Int{
            TODO("Not yet implemented")
        }
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
        RecyclerView.ViewHolder{
            TODO("Not yet implemented")
        }
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int){
            TODO("Not yet implemented")
        }
    }
// 항목 개수 구하기

override fun getItemCount(): Int = datas.size
// 항목 구성에 필요한 뷰 홀더 객체 준비

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
    MyViewHolder(ItemMainBinding.inflate(LayoutInflater.from(parent.context),
        parent, false))
// 뷰에 데이터 출력

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int){
    Log.d("yunseo", "onBindViewHolder : $position")
    val binding = (holder as MyViewHolder).binding

    // 뷰에 데이터 출력
    binding.itemData.text = datas[position]

    // 뷰에 이벤트 추가
    binding.itemRoot.setOnClickListener {
        Log.d("yunseo", "item root click : $position")
    }
}

리사이클러 뷰 출력

// 리사이클러 뷰 액티비티

class RecyclerViewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?){
        super.onCreate(savedInstanceState)
        val binding = ActivityRecyclerViewBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val datas = mutableListOf<String>()
        for(i in 1..10){
            datas.add("Item $i")
        }

        binding.recyclerView.layoutManager = LinearLayoutManager(this)
        binding.recyclerView.adapter = MyAdapter(datas)
        binding.recyclerView.addItemDecoration(DividerItemDecoration(this,
        LinearLayoutManager.VERTICAL))
    }
}

notifyDataSetChanged()

  • 항목 구성 데이터에 새로운 항목을 동적으로 추가·제거 후 호출
// 항목 추가

datas.add("new data")
adapter.notifyDataSetChanged()

레이아웃 매니저

  • 어댑터로 만든 항목을 리사이클러 뷰에 배치
항목 종류설명
LinearLayoutManager항목 가로·세로 방향 배치
GridLayoutManager항목 그리드 배치
StaggeredGridLayoutManager항목을 높이가 불규칙한 그리드 배치

LinearLayoutManager

  • 항목 가로·세로 배치
    • 가로 배치 : LinearLayoutManager.HORIZONTAL
    • 세로 배치 : LinearLayoutManager.VERTICAL or 생략

ch06-16-Vertical

// 항목 세로 배치

binding.recyclerView.layoutManager = LinearLayoutManager(this)

ch06-17-Horizontal

// 항목 가로 배치

val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
binding.recyclerView.layoutManager = layoutManager

GridLayoutManager

  • 그리드 배치
    • 생성자 숫자 : 그리드 열의 개수
    • 방향 설정 가능
// 그리드 항목 배치

val layoutManager = GridLayoutManager(this, 2)
binding.recyclerView.layoutManager = layoutManager

ch06-18-Grid

  • 가로 설정 : 생성자에 GridLayoutManager.HORIZONTAL 지정
// 그리드 항목 가로 배치

val layoutManager = GridLayoutManager(this, 3,
GridLayoutManager.HORIZONTAL, false)
binding.recyclerView.layoutManager = layoutManager

ch06-19-GridH

  • 네 번째 매개변수 Boolean 값 : 왼쪽, 오른쪽 정렬
// 그리드 항목 오른쪽부터 배치

val layoutManager = GridLayoutManager(this, 3,
GridLayoutManager.HORIZONTAL, true)
binding.recyclerView.layoutManager = layoutManager

ch06-20-GridB

StaggeredGridLayoutManager

  • 뷰의 크기가 다를 경우 지그재그 형태 배치
// 지그재그 그리드 형태 배치

val layoutManager = StaggeredGridLayoutManager(2,
StaggeredGridLayoutManager.VERTICAL)
binding.recyclerView.layoutManager = layoutManager

ch06-21-GridS

아이템 데코레이션

  • 리사이클러 뷰를 다양하게 꾸밀 때 사용
  • DividerItem Decoration : 항목의 구분선 출력
  • ItemDecoration을 상속받는 개발자 클래스 생성 후 다양한 꾸미기 작업 수행
함수 종류설명
onDraw()항목 배치 전 호출
onDrawOver()항목 모두 배치 후 호출
getItemOffsets()개별 항목 꾸밀 때 호출
// 아이템 데코레이션 구현

class MyDecoration(val context: Context): RecyclerView.ItemDecoration(){
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State){
        super.onDraw(c, parent, state)
    }

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State){
        super.onDrawOver(c, parent, state)
    }

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        super.getItemOffsets(outRect, view, parent, state)
    }
}
// 항목 배치 전 호출되는 onDraw()

override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State){
    super.onDraw(c, parent, state)
    c.drawBitmap(BitmapFactory.decodeResource(context.resources,
    R.drawable.stadium), 0f, 0f, null)
}
// 모든 항목 배치 후 호출하는 onDrawOver()

override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State){
    super.onDrawOver(c, parent, state)

    // 뷰 크기 계산
    val width = parent.width
    val height = parent.height

    // 이미지 크기 계산
    val dr: Drawable? = ResourcesCompat.getDrawable(context.resources,
        R.drawable.kbo, null)
    val drWidth = dr?.intrinsicWidth
    val drHeight = dr?.intrinsicHeight

    // 이미지가 그려질 위치 계산
    val left = width / 2 - drWidth?.div(2) as Int
    val top = height / 2 - drHeight?.div(2) as Int
    c.drawBitmap(
        BitmapFactory.decodeResource(context.resources, R.drawable.kbo),
        left.toFloat(),
        top.toFloat(),
        null
    )
}
// 개별 항목 꾸미는 getItemOffsets()

override fun getItemOffsets(
    outRect: Rect,
    view: View,
    parent: RecyclerView,
    state: RecyclerView.State
){
    super.getItemOffsets(outRect, view, parent, state)
    val index = parent.getChildAdapterPosition(view) + 1

    if(index % 3 == 0) outRect.set(10, 10, 10, 60) // left, top, right, bottom
    else outRect.set(10, 10, 10, 0)

    view.setBackgroundColor(Color.LTGRAY)
    ViewCompat.setElevation(view, 20.0f)
}

addItemDecoration()

  • 아이템 데코레이션 객체를 리사이클러 뷰에 적용하는 함수

ch06-22-ID

// 리사이클러 뷰에 적용한 아이템 데코레이션

binding.recyclerView.addItemDecoration(MyDecoration(this))

6절. 뷰 페이저2

뷰 페이저2

ch06-23-ViewPager

  • 스와이프로 넘기는 화면 구성
  • 2019년, viewpager 대신 viewpager2 제공
  • 항목이 순서대로 나열
  • 한 화면에 항목 하나 표현
어댑터 종류
RecyclerView.Adapter(기존)
FragmentStateAdapter(New!)
// 뷰 페이저2 구현 : 리사이클러 뷰 어댑터 이용

class MyPagerViewHolder(val binding: ItemPagerBinding) :
    RecyclerView.ViewHolder(binding.root)
class MyPagerAdapter(val datas: MutableList<String>) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>(){
        override fun getItemCount(): Int{
            return datas.size
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
            RecyclerView.ViewHolder =
                MyPagerViewHolder(ItemPagerBinding.inflate(LayoutInflater.from(
                    parent.context), parent, false))

        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int){
            val binding = (holder as MyPagerViewHolder).binding

            // 뷰에 데이터 출력
        binding.itemPagerTextView.text = datas[position]
            when (position % 3) {
                    0 -> binding.itemPagerTextView.setBackgroundColor(Color.RED)
                    1 -> binding.itemPagerTextView.setBackgroundColor(Color.BLUE)
                    2 -> binding.itemPagerTextView.setBackgroundColor(Color.GREEN)
            }
        }
    }
  • FragmentStateAdapter로 뷰 페이저2 구현

ch06-24-ViewPager2

// 뷰 페이저 2 구현 - 프래그먼트 어댑터 이용

class MyFragmentPagerAdapter(activity: FragmentActivity): FragmentStateAdapter(activity){
    val fragments: List<Fragment>
    init{
        fragments = listOf(OneFragment(), TwoFragment(), ThreeFragment())
        Log.d("Yunseo", "Fragments Size : ${fragments.size}")
    }
    override fun getItemCount(): Int = fragments.size
    override fun createFragment(position: Int): Fragment = fragments[position]
}
// 뷰 페이저 2 어댑터 적용

binding.viewpager.adapter = MyPagerAdapter(datas)

7절. 드로어 레이아웃

DrawerLayout

  • activity.xml에서 드로어 메뉴 출력 부분의 태그를 DrawerLayout으로 선언
  • DrawerLayout 아래에 2개의 뷰 선언
    • 첫 번째 하위 태그: 액티비티 화면에 출력
    • 두 번째 하위 태그: 평소에 안 보이고 끌려 나옴

ch06-25-DrawerLayout

<!-- 드로어 레이아웃 구성 -->

<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout ...>
        ...
    </LinearLayout>
    <TextView
        android:layout_gravity="start"/>
</android.drawerlayout.widget.DrawerLayout>
  • 툴바 영역에 토글(toggle) 버튼 함께 제공
  • 토글 버튼 : ActionBarDrawerToggle 클래스에서 제공

ch06-26-Toggle

// 드로어 메뉴 토글 버튼 적용

class DrawerActivity : AppCompatActivity() {
    lateinit var toggle: ActionBarDrawerToggle
    override fun onCreate(savedInstanceState: Bundle?){
        // (...생략...)
        toggle = ActionBarDrawerToggle(this, binding.drawer, R.string.drawer_opened, R.string.drawer_closed)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        toggle.syncState()
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean{
        // 이벤트가 토글 버튼에서 발생할 경우
        if(toggle.onOptionsItemSelected(item)){
            return true
        }
        return super.onOptionsItemSelected(item)
    }
}

8장. 제트팩 화면 구성

1) 새 모듈 생성 & 그래들 설정

  • Ch11_JetPack 새 모듈 생성
  • 뷰 바인딩 기법 추가
viewBinding.isEnabled = true

2) 문자열 리소스 생성

  • res/values/strings.xml 파일에 ActionBarDrawerToggle 생성 시 지정할 문자열 리소스 추가

3) 테마 추가

  • res/values/themes.xml 파일에 액티비티 윈도우를 출력할 때 액션바 출력이 없도록 설정

4) 프래그먼트 생성

  • New -> Fragment -> Fragment(Blank) 선택
  • OneFragment, TwoFragment, ThreeFragment 이름의 프래그먼트 3개 추가

5) 파일 복사

  • res/drawable : kbo.png 파일 복사
  • res/layout : fragment_one.xml, fragment_two.xml, fragment_three.xml 파일 복사
  • OneFragment.kt, TwoFragment.kt, ThreeFragment.kt : 해당 소스 영역에 복사

6) fragment_one.xml 파일 작성

  • 리사이클러 뷰 구성을 위해 fragment_one.xml 파일 작성
  • OneFragment.kt 파일 작성

7) 메뉴 리소스 생성

  • New -> Android Resource Directory 선택
  • Resource type 에서 menu 디렉토리 생성
  • New -> Menu Resource File 선택[cite: 49].
  • File name 에 menu_main 입력 후 파일 생성
  • menu_main.xml 파일 열고 작성

8) 메인 레이아웃 XML 작성

  • activity_main.xml 파일에 등록
    • 드로어 레이아웃
    • 툴바
    • 뷰 페이저2

9) 메인 액티비티 작성

  • MainActivity.kt 파일 작성

10) 앱 실행

ch06-27-App

프로젝트 바로가기

This post is licensed under CC BY 4.0 by the author.