[Android/Kotlin 07] 연락처앱 만들기(전화걸기, 즐겨찾기)

이다을·2023년 8월 30일
2

RecyclerView를 사용하여 리스트를 표시하고, 각 아이템의 클릭 기능을 구현했습니다. (별 아이콘 클릭 시 즐겨찾기 토글, 전화번호 클릭 시 해당 번호로 전화발신) RecyclerView는 많은 아이템을 효율적으로 관리하고 표시할 수 있으며, ViewBinding을 활용하여 UI 요소에도 접근할 수 있습니다.

🔗 연락처앱 github

✔️ 조건

  • 메인 화면에는 RecyclerView 하나만 표시
  • 연락처 아이템 레이아웃에는 사진, 이름, 전화번호를 표시하는 뷰
  • 연락처 클래스: 사진, 이름, 전화번호, 즐겨찾는여부 필드
  • 연락처 더미 데이터를 생성
  • 어댑터 클래스: RecyclerView.Adapter를 확장
  • 연락처 데이터를 바인딩하는 로직이 포함 → ViewBinding 활용
  • 데이터모델의 즐겨찾기 여부에 따라 viewType을 나눠 표시
  • 메인 활동에서 RecyclerView를 어댑터와 연결하고, 더미 데이터를 로드하여 표시

⚡연락처 UI

프로필 사진옆에 이름과 전화번호를 배치, 즐겨찾기 버튼은 전체의 세로 가운데 정렬로 배치했습니다.

⚡ViewBinding을 사용하여 레이아웃 인플레이트

각 뷰 요소에 접근하기 위한 방법을 제공합니다.

// ActivityMainBinding 클래스를 사용하여 레이아웃 인플레이트
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

⚡데이터 리스트 생성 및 어댑터 초기화

RecyclerView에 표시할 데이터 리스트를 생성하고, 해당 리스트를 어댑터에 연결합니다.

val dataList = mutableListOf<MyItem>()
// dataList에 MyItem 객체들을 추가
adapter = MyAdapter(dataList)

⚡어댑터와 레이아웃 매니저 설정

아이템들을 어떻게 배치할지 결정합니다.

binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)

⚡데코레이션 추가

RecyclerView의 아이템 간의 간격, 선 등을 데코레이션을 통해 구현할 수 있습니다.

val decoration = MyAdapter.AddressAdapterDecoration()
binding.recyclerView.addItemDecoration(decoration)

⚡아이템 클릭 기능 구현

어댑터 내부에서 아이템의 버튼을 클릭했을 때의 동작을 구현합니다.

adapter.numberClick = object : MyAdapter.NumberClick {
    override fun onNumberClick(view: View, position: Int) {
        // 클릭한 아이템의 정보를 사용하여 전화 걸기 기능 구현
        // Intent를 사용하여 전화 앱을 실행하도록 설정
    }
}

adapter.favoriteClick = object : MyAdapter.FavoriteClick {
    override fun onFavoriteClick(view: View, position: Int) {
        // 클릭한 아이템의 즐겨찾기 상태를 변경하고 어댑터에 변경 사항을 알림
    }
}

⚡data class

데이터를 저장하기 위한 목적으로 사용되며, 상태를 관리하고 내부에 속성과 기능을 추가할 수 있습니다. toggleFavorite() 함수는 즐겨찾기 상태를 토글하는 기능을 수행합니다. 함수 내부에서 isFavorite 값을 반전시킴으로써 즐겨찾기 상태를 변경합니다.

data class MyItem(val profile: Int, val name: String, val number: String, var isFavorite: Boolean) {
    fun toggleFavorite() {
        isFavorite = !isFavorite
    }
}

⚡adapter class

VIEW_TYPE 상수는 아이템 뷰의 다양한 형태를 구분하기 위해 사용됩니다. 아래 오브젝트는 두 가지 다른 뷰 형태를 사용하는 방법입니다.

companion object {
    private const val VIEW_TYPE_DEFAULT = 0
    private const val VIEW_TYPE_ANOTHER = 1
}

⚡뷰 홀더 생성

onCreateViewHolder 메서드에서는 뷰 홀더를 생성하고 반환합니다. viewType에 따라 다른 뷰 홀더를 생성하도록 설정되어 있습니다.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return when (viewType) {
        VIEW_TYPE_DEFAULT -> {
            val binding = ItemRvBinding.inflate(inflater, parent, false)
            DefaultViewHolder(binding)
        }
        VIEW_TYPE_ANOTHER -> {
            val binding = ItemRvBinding.inflate(inflater, parent, false)
            AnotherViewHolder(binding)
        }
        else -> throw IllegalArgumentException("Invalid view type")
    }
}

⚡뷰 홀더 바인딩

onBindViewHolder 메서드에서는 생성한 뷰 홀더에 데이터를 바인딩합니다.각 아이템 뷰의 버튼에 클릭 리스너를 설정하여 클릭 시 동작을 수행할 수 있도록 합니다.

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val item = items[position]
    when (holder) {
        is DefaultViewHolder -> holder.bindDefault(item)
        is AnotherViewHolder -> holder.bindAnother(item)
    }
    holder.itemView.findViewById<ImageView>(R.id.favorite).setOnClickListener {
        favoriteClick?.onFavoriteClick(it, position)
    }
    holder.itemView.findViewById<TextView>(R.id.number).setOnClickListener {
        numberClick?.onNumberClick(it, position)
    }
}

⚡아이템 뷰 타입 설정

getItemViewType 메서드는 각 아이템의 뷰 타입을 결정합니다. 이를 통해 다른 뷰 홀더를 사용하여 아이템을 표시할 수 있습니다.

override fun getItemViewType(position: Int): Int {
    return if (items[position].isFavorite) {
        VIEW_TYPE_DEFAULT
    } else {
        VIEW_TYPE_ANOTHER
    }
}

⚡아이템 뷰 홀더 클래스

DefaultViewHolder와 AnotherViewHolder 클래스는 아이템 뷰의 구성 요소들을 바인딩하고 데이터를 설정하는 역할을 수행합니다. 이를 통해 각 아이템 뷰의 모양과 데이터를 조정할 수 있습니다.

inner class DefaultViewHolder(private val binding: ItemRvBinding) : RecyclerView.ViewHolder(binding.root) {
    // ...
    fun bindDefault(item: MyItem) {
    // ...
    }
}

inner class AnotherViewHolder(private val binding: ItemRvBinding) : RecyclerView.ViewHolder(binding.root) {
    // ...
    fun bindAnother(item: MyItem) {
    // ...
    }
}

profile
나도 개발 할래

6개의 댓글

comment-user-thumbnail
2023년 8월 30일

오늘 장사 쉬나요?

2개의 답글
comment-user-thumbnail
2023년 8월 30일

알기 쉽게 설명해주시네요 ㅎㅎ
그런데 이 캐릭터는 파인가요??

1개의 답글
comment-user-thumbnail
2023년 9월 1일

잘봣습니다 ~,~

답글 달기