
0. 준비
1. res - values - string.xml
<resources>
<string name="app_name">MyProject</string>
<string name="drawer_opened">Opened Drawer</string>
<string name="drawer_closed">Closed Drawer</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>

2. res - values - themes - themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.MyProject" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.MyProject" parent="Base.Theme.MyProject" />
<style name="ToolbarIconColor" parent="ThemeOverlay.AppCompat.ActionBar">
<item name="colorControlNormal">#FFFFFF</item>
</style>
</resources>

1. 툴바와 메뉴바를 위한 activit_main.xml
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<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"
app:theme="@style/ToolbarIconColor"
app:titleTextColor="#FFFFFF"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<TextView
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FF0000"
android:fitsSystemWindows="true"
android:gravity="center_horizontal"
android:id="@+id/drawer"
android:text="I am Drawer"
android:textColor="#FFFFFF"
android:textSize="20dp"
android:textStyle="bold"/>
</androidx.drawerlayout.widget.DrawerLayout>

2. 툴바
1. menu폴더 만들고 menu.xml만들기
- 툴바는 androidx에서 제공되고 있다.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_search"
android:title="search"
app:showAsAction="always" //never, ifRoom
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
</menu>

2. main_activity.kt 와 연결
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 이거 추가해주기
setSupportActionBar(binding.toolbar)
}
- menu에 있는 것들과 툴바를 연결해준다.
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.menu_main, menu)
return super.onCreateOptionsMenu(menu)
}

- 사용자 입력 후, 검색이벤트 처리하기
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 {
val intent = Intent(Intent.ACTION_WEB_SEARCH)
intent.putExtra(SearchManager.QUERY, query)
startActivity(intent)
Log.d(TAG, "search text: $query")
return true
}
})
return super.onCreateOptionsMenu(menu)
}


3. 메뉴 추가하기
- menu_main.xml 에서 수정하기
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_search"
android:title="search"
app:showAsAction="always"
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
<item
android:id="@+id/menu0"
android:title="Menu0"/>
</menu>
- main_activity에서 수정하기

4. 메뉴 선택 이벤트 처리하기
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
R.id.menu0 ->{
val lat = 37.651450
val lon = 127.016637
var uri = Uri.parse("geo:" + lat + "," + lon)
var intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)
true
}
1 -> {
Log.d(TAG, "menu1")
true
}
2 -> {
Log.d(TAG, "menu2")
true
}
}
return super.onOptionsItemSelected(item)
}


3. 드로우 레이아웃
- actitity_main.xml
- 드로우 레이아웃은 2개의 자식만을 갖을 수 있다.
1. 토글 버튼 만들기
- main_activity.kt
// 추가
lateinit var toggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
// 이 부분 추가하기
toggle = ActionBarDrawerToggle(this, binding.main, R.string.drawer_opened,
R.string.drawer_closed) // drawer가 열렸을 때와 갇혔을 때 스트링 ( strings.xml에 저장되어 있다.)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
toggle.syncState() // 토글 열때마다 싱크 하겠다.
}

2. 토글 실행시키기
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
(...)
if(toggle.onOptionsItemSelected(item)){
return true
}
return super.onOptionsItemSelected(item)
}

4. 프레그먼트
1. 프레그먼트 만들기


2. ViewPasser2에 프리그먼트 연결하기
class MyFragmentPagerAdapter(activity: FragmentActivity): FragmentStateAdapter(activity){
// 초기화
val fragments: List<Fragment>
init {
//세개의 프레그먼트 아답터 만들기
fragments = listOf(OneFragment(), TwoFragment(), ThreeFragment())
}
}
override fun onCreate(savedInstanceState: Bundle?) {
(...)
// 프레그먼트 뷰페이저와 연동
val fadapter = MyFragmentPagerAdapter(this)
binding.viewpager.adapter = fadapter
}
- 마이 프리그먼트 페이지 아답터: 세개의 프레그먼트를 하나의 뷰 페이저에 넣고 뷰페이저에서 어떤 프레그먼트를 보여줄지를 결정
- <Fragment>가 빨간줄이라면: ALt + Enter로 프레스먼트를 import하고 androidx 선택
3. 각각의 프레그먼트 구분을 위해 배경색칠하기



4. 각 프레그먼트 동작 구현
- OneFragment
(...)
// 프레그먼트가 구성하는 화면
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Inflate the layout for this fragment
val binding = FragmentOneBinding.inflate(inflater,container, false)
// activity는 layoutInflater매개변수를 이용하지만, 프레그먼트는 inflate를 괄호 안에 쓴다.
//return inflater.inflate(R.layout.fragment_one, container, false)
return binding.root
}
(...)
4. 리사이클러 뷰 - 목록 화묜 구성
1. 리사이클러 뷰
- 동일한 형태의 뷰를 여러개 가지고 있는 것들을 관리하는데, 가장 많이 사용된다.
- 아이템 하나에 대해서 레이아웃을 구성해 줘야 한다.
- ViewHolder(필수):하나의 아이템에 대한 구성을 하게 된다.
- Adapter(필수): 리사이클러 뷰에 있는 것들을 관리, ViewHolder를 가지고 여러개이 아이템으로 구성되는 아답터 구성
- LayoutManager(필수): 배치
- ItemDecoration(옵션): 꾸미기
2. OneFragment - 리사이클러 뷰가 화면전체를 차지하게끔 만들기
// fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/recyclerView"
/>
3. 리사이클러뷰가 각각의 아이템들의 항목을 구성하지 위한 파일 업로드

4. OneFragmetn.kt에 recycleview에 필요한 내용들
// 화면을 구성하는 화면의 바이딩이 필요
class MyVeiwHolder(val binding: ItemRecyclerviewBinding): RecyclerView.ViewHolder(binding.root)
// 리사이클러 뷰관리, 리사이클러 뷰에 사용자가 만든 데이터를 전달하고 그 데이터로 MyViewHolder내용을 리사이클러 뷰에 넣는다.
// 이때 Myadapter은 밑에 세개의 함수가 필수적이다.
class Myadapter(val data: 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")
}
}
// 화면을 구성하는 화면의 바이딩이 필요
class MyVeiwHolder(val binding: ItemRecyclerviewBinding): RecyclerView.ViewHolder(binding.root)
// 리사이클러 뷰관리, 리사이클러 뷰에 사용자가 만든 데이터를 전달하고 그 데이터로 MyViewHolder내용을 리사이클러 뷰에 넣는다.
// 이때 Myadapter은 밑에 세개의 함수가 필수적이다.
class Myadapter(val datas: MutableList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
// 리사이클러뷰 안에 있는 아이템의 개수가 몇개냐 = 데이터의 갯수에 따라 다르다.
override fun getItemCount(): Int {
return datas.size
}
// MyVeiwHolder와 Myadapter연결
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MyVeiwHolder((ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)))
}
// 전달받은 데이터를 뷰홀더에 연결, 하나하나 대응
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val binding = (holder as MyVeiwHolder).binding
binding.itemData.text = datas[position]
}
}
// 프레그먼트가 구성하는 화면
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Inflate the layout for this fragment
val binding = FragmentOneBinding.inflate(inflater,container, false)
// activity는 layoutInflater매개변수를 이용하지만, 프레그먼트는 inflate를 괄호 안에 쓴다.
var datas = mutableListOf<String>()
for (i in 1..10){
datas.add("item $i")
}
//return inflater.inflate(R.layout.fragment_one, container, false)
return binding.root
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Inflate the layout for this fragment
val binding = FragmentOneBinding.inflate(inflater,container, false)
// activity는 layoutInflater매개변수를 이용하지만, 프레그먼트는 inflate를 괄호 안에 쓴다.
var datas = mutableListOf<String>()
for (i in 1..10){
datas.add("item $i")
}
val radapter = Myadapter(datas)
binding.recyclerView.layoutManager = LinearLayoutManager(activity)
binding.recyclerView.adapter = radapter
//return inflater.inflate(R.layout.fragment_one, container, false)
return binding.root
}

5. Item들의 배치를 그리드로 배치하기
// 프레그먼트가 구성하는 화면
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Inflate the layout for this fragment
val binding = FragmentOneBinding.inflate(inflater,container, false)
// activity는 layoutInflater매개변수를 이용하지만, 프레그먼트는 inflate를 괄호 안에 쓴다.
var datas = mutableListOf<String>()
for (i in 1..10){
datas.add("item $i")
}
val radapter = Myadapter(datas)
//binding.recyclerView.layoutManager = LinearLayoutManager(activity)
// 그리드로 표현
binding.recyclerView.layoutManager = GridLayoutManager(activity, 2)
binding.recyclerView.adapter = radapter
//return inflater.inflate(R.layout.fragment_one, container, false)
return binding.root
}

6. 꾸미기
- 데코레이션 만들기
//꾸미기
class MyDecoration(val context: Context): RecyclerView.ItemDecoration(){
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 = 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
)
}
// 아이템들 사이의 간격
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)
else
outRect.set(10, 10, 10, 0)
view.setBackgroundColor(Color.parseColor("#28A0FF"))
ViewCompat.setElevation(view, 20.0f)
}
}
- 데코레이션 반영하기
// 프레그먼트가 구성하는 화면
override fun onCreateView(
(...)
// 데코레이션 반영
binding.recyclerView.addItemDecoration(MyDecoration(activity as Context))
//return inflater.inflate(R.layout.fragment_one, container, false)
return binding.root
}

- 그림 뒤로 보내기
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
// Darw : kbo -> item
//override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
// DrawOver : item => kbo
super.onDraw(c, parent, state)

7. 별도의 파일로 관리하기



8. 아이템 클릭 했을때 이벤트 처리 - MyAdapter.kt 수정
class Myadapter(val datas: MutableList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
(...)
// 전달받은 데이터를 뷰홀더에 연결, 하나하나 대응
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val binding = (holder as MyVeiwHolder).binding
binding.itemData.text = datas[position]
// 이벤트 처리
binding.itemRoot.setOnClickListener {
Toast.makeText(it.context, "${datas[position]}이 선택되었습니다.", Toast.LENGTH_SHORT).show()
AlertDialog.Builder(it.context).run{
setTitle("알림")
setIcon(android.R.drawable.ic_dialog_alert)
setMessage("${datas[position]}이 선택되었습니다.")
setPositiveButton("예", null)
show()
}
}
}
}

'SPECIALIZED > Android' 카테고리의 다른 글
| 12. 머터리얼 라이브러리 (0) | 2025.05.07 |
|---|---|
| 10. 액티비티 컴포넌트 (0) | 2025.04.30 |
| 9. 다이얼로그 이용하기 (0) | 2025.04.13 |
| 8. 안드로이드 프로그래밍 사용자 이벤트 처리하기 (0) | 2025.04.13 |
| 7. 리소스 활용하기 (0) | 2025.04.03 |