
RecyclerView 마지막 포스팅입니다.
왼쪽에서 오른쪽으로 스와이프를 하면 통화를,
반대로 오른쪽에서 왼쪽으로 스와이프를 하면 메세지 전송이 되도록 해보겠습니다.
필요한 과정
1. item_phonebook.xml 수정
2. ItemTouchHelper.callback 구현
ㄱ. 필수 메서드 구현
ㄴ. 보조 메서드 구현
3. Adapter에 ItemTouchHelper.callback 부착
구현
1. item_phonebook.xml 수정
이제 두가지의 레이아웃이 필요합니다.
스와이프 시킬 레이아웃은 이름과 전화번호부가 적혀있는 화면이구요,
스와이프 되면 이제 양 사이드에 문자 아이콘과 전화 아이콘이 있어야겠죠?

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#ffffff">
<!--behind layout-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_call_phone_book_list_item"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:src="@drawable/ic_call_100"/>
<View
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="0dp"/>
<ImageView
android:id="@+id/iv_message_phone_book_list_item"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:src="@drawable/ic_sms_100"/>
</LinearLayout>
<!--swipe layout-->
<LinearLayout
android:id="@+id/swipe_item_phone_book_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_person_phone_book_list_item"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center"
android:src="@drawable/ic_person"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical">
<TextView
android:layout_weight="1"
android:id="@+id/tv_name_phone_book_list_item"
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center|left"
android:paddingTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="이름"
android:textColor="@color/black"
android:textSize="20dp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_phone_number_phone_book_list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center|left"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="전화번호"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
아이템 배치는 해주었고, 이것도 리스트 아이템이니 리스트 아이템 하나하나 마다 존재해야 합니다.
그러므로 어댑터의 뷰 홀더에도 선언을 해주고 초기화를 해 주어야 합니다.
class PhoneBookListAdapter(var persons: ArrayList<Person>, var con: Context) :
RecyclerView.Adapter<PhoneBookListAdapter.ViewHolder>(),
PhoneBookListItemSwipeListener, Filterable {
var TAG = "PhoneBookListAdapter"
//...
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
//swipe layout
var swipe_item_phone_book_list: LinearLayout
var iv_person_phone_book_list_item: ImageView
var tv_name_phone_book_list_item: TextView
var tv_phone_number_phone_book_list_item: TextView
//behind layout
var iv_call_phone_book_list_item: ImageView
var iv_message_phone_book_list_item: ImageView
init {
//swipe layout
swipe_item_phone_book_list = itemView.findViewById(R.id.swipe_item_phone_book_list)
iv_person_phone_book_list_item = itemView.findViewById(R.id.iv_person_phone_book_list_item)
tv_name_phone_book_list_item = itemView.findViewById(R.id.tv_name_phone_book_list_item)
tv_phone_number_phone_book_list_item = itemView.findViewById(R.id.tv_phone_number_phone_book_list_item)
//behind layout
iv_call_phone_book_list_item = itemView.findViewById(R.id.iv_call_phone_book_list_item)
iv_message_phone_book_list_item = itemView.findViewById(R.id.iv_message_phone_book_list_item)
}
//...
}
//...
}
PhoneBookListItemSwipeListener는 3번항목에서 설명하도록 하겠습니다.
2. ItemTouchHelper.callback 구현
ㄱ. 필수메서드 구현
ItemTouchHelper.callback을 구현할 클래스, PhoneBookListItemHelper.kt를 작성하겠습니다.
ItemTouchHelper.callback을 구현하려면 반드시 아래 세 메소드를 작성해야 합니다.
class PhoneBookListItemHelper(val con: Context):
ItemTouchHelper.Callback() {
var TAG = "PhoneBookListItemHelper"
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
TODO("Not yet implemented")
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
TODO("Not yet implemented")
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
}
세가지 메서드는 다음과 같은 작업을 관장합니다.
- getMovementFlags : makeMovementFlags(dragFlags: Int, swipeFlags: Int)를 반환해야 합니다.
드래그 및 스와이프 방법을 makeMovementFlags의 매개변수로 입력해 반환합니다.
Flags는 드래그 또는 스와이프를 할 수 있는 방향을 뜻하는데, 상 하 좌 우 네가지를 선택할 수 잇습니다.
드래그를 통해 array의 순서변경도 가능하나, 전화번호부는 정렬된 목록이므로 사용하지 않습니다.
override fun getMovementFlags( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { return makeMovementFlags(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) }
- onMove : 드래그시에 호출되는 함수로, 이번엔 사용하지 않습니다.
드래그 이벤트가 진행된 RecyclerView와 드래그 될 ViewHolder, 그 위에 위치한 ViewHolder를
드래그 이벤트가 발생했을때 매개변수로 받습니다.
override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { return false }
- onSwiped : 스와이프 이벤트가 발생됬을때 호출됩니다.
스와이프 된 아이템의 ViewHolder와 스와이프 방향을 매개변수로 받습니다.
즉, 어떤 아이템이 스와이프 됬는지를 알 수 있게 되므로, 여기에 전화를 걸거나 문자를 보내는 intent를 작성하면 됩니다.
왜냐하면, ViewHolder는 onCreate로 뷰가 생성되고, onBindViewHolder로 값이 매핑되어서 출력되므로
스와이프 한 시점에서는 이미 TextView에 position에 알맞은 값이 세팅되어 있기 떄문입니다.
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val personViewHolder = viewHolder as PhoneBookListAdapter.ViewHolder
val name = personViewHolder.tv_name_phone_book_list_item.text
val phoneNumber = personViewHolder.tv_phone_number_phone_book_list_item.text
Log.d("###", "swipe item's name : $name, phoneNumber : $phoneNumber")
if(direction == ItemTouchHelper.LEFT){
val smsUriSwipedPerson = Uri.parse("sms:$phoneNumber")
val smsIntent = Intent(Intent.ACTION_SENDTO, smsUriSwipedPerson)
con.startActivity(smsIntent)
} else {
val callUriSwipedPerson = Uri.parse("tel:$phoneNumber")
val callIntent = Intent(Intent.ACTION_CALL, callUriSwipedPerson)
con.startActivity(callIntent)
}
}
ㄱ. 보조 메서드 구현
이렇게 필수적인 세가지 메서드는 작성을 완료 했습니다.
그리고 추가적으로 다음과 같은 메서드를 구현해야 합니다.
스와이프를 사용하고 있고, 저는 롱클릭 이벤트를 사용하지 않음으로 다음의 메서드를 추가해줍니다.
override fun isItemViewSwipeEnabled(): Boolean {
return true
}
override fun isLongPressDragEnabled(): Boolean {
return false
}
여기까지 하면 스와이프까지 구현이 됩니다.

근데 뭔가 이상합니다. 스와이프가 되긴하는데 레이아웃에 만들어둔 통화 이미지와 문자 이미지가 안보입니다.
이유는 뷰홀더가 '통째로' 스와이프가 되고있기 때문입니다.
onChildDraw 함수를 이용해서 뷰 홀더 전체가 아니라 아이템을 특정시켜서 스와이프 할 수 있습니다.
깔린 레이아웃과 스와이프될 레이아웃 두종류가 있으므로, 스와이프 될 레이아웃만 스와이프 되도록 구현합니다.
override fun onChildDraw(
c: Canvas, recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean
) {
Log.d(TAG, "Orign position : $dX,$dY")
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
val view = (viewHolder as PhoneBookListAdapter.ViewHolder).swipe_item_phone_book_list
getDefaultUIUtil().onDraw(c, recyclerView, view, dX, dY, actionState, isCurrentlyActive)
Log.d(TAG, "moved position : $dX,$dY")
}
}
이제 바깥쪽의 정보가 적힌 레이아웃만 스와이프가 됩니다.
문자 전화도 정상적으로 작동하구요. 그런데 어라? 다시 앱으로 돌아와보니..

스와이프가 되서 날아간 채로 있네요.
이때, 상호작용이 종료되고 애니메이션이 종료 된 후에 호출되는 clearVIew 함수를 재정의해서 사용하시면 됩니다.
override fun clearView(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
) {
getDefaultUIUtil().clearView((viewHolder as PhoneBookListAdapter.ViewHolder).swipe_item_phone_book_list)
}
3. Adapter에 ItemTouchHelper.callback 부착
fun setAdapter(){
//리사이클러뷰에 리사이클러뷰 어댑터 부착
rv_phone_book.layoutManager = LinearLayoutManager(this)
phoneBookListAdapter = PhoneBookListAdapter(persons)
rv_phone_book.adapter = phoneBookListAdapter
//스와이프 이벤트 부착
itemTouchHelper = ItemTouchHelper(PhoneBookListItemHelper(this))
itemTouchHelper.attachToRecyclerView(rv_phone_book)
}
이렇게 부착을 해주면 구현 완료입니다. 고생하셨습니다.
예제 코드 전체 보기
https://github.com/gr2nsky/RecyclerViewPractice/tree/SwipeEvent
GitHub - gr2nsky/RecyclerViewPractice
Contribute to gr2nsky/RecyclerViewPractice development by creating an account on GitHub.
github.com
RecyclerView 시리즈 다른글 보러가기
2022.01.30 - [IDE & Framework/Android] - [Kotlin] RecyclerView (1) - 기본 예제
[Kotlin] RecyclerView (1) - 기본 예제
새 시리즈 포스팅입니다. 기본 에제 - 좌/우 스와이프로 삭제/통화 걸기 구현 - 검색 구현 - AAC MVVM적용 이렇게 총 네편으로 포스팅 할 예정입니다. RecyclerView란? ViewHolder 패턴을 사용해 아이템
greensky0026.tistory.com
2022.02.05 - [IDE & Framework/Android] - [Kotlin] RecyclerView (2) - Filter와 SerachView로 검색기능 구현
[Kotlin] RecyclerView (2) - Filter와 SerachView로 검색기능 구현
RecyclerView 시리즈의 두번째 포스팅입니다. 이번엔 SerachView, Filterable 위젯을 사용해 검색기능을 구현해 보겠습니다. SerachView는 커스텀하기가 쉽지 않아 EditText로 직접 구현하는 경우도 굉장히 많
greensky0026.tistory.com
'IDE & Framework > Android' 카테고리의 다른 글
Android Proguard (0) | 2022.02.20 |
---|---|
[Kotlin] PhoneStateListener deprecated, TelephonyCallback로 대체하기 (0) | 2022.02.13 |
[Kotlin] RecyclerView (2) - Filter와 SerachView로 검색기능 구현 (2) | 2022.02.05 |
[Kotlin] ViewPager2 사용하기 (0) | 2022.01.31 |
[Kotlin] RecyclerView (1) - 기본 예제 (0) | 2022.01.30 |