반응형
Greensky0026
삽질러의 대환장 공사판
Greensky0026
전체 방문자
오늘
어제
  • 분류 전체보기 (241)
    • Language (56)
      • JAVA (13)
      • Swift (38)
      • Kotlin (4)
      • Dart (0)
      • PHP (0)
      • JavaScript (1)
    • IDE & Framework (92)
      • Spring (10)
      • Android (48)
      • iOS (8)
      • NodeJS (1)
      • CodeIgniter (3)
      • Flutter (1)
      • 분류중 (18)
    • Infra (8)
    • Database (12)
    • VCS (4)
    • Debug Log (34)
      • java (7)
      • swift (3)
      • Android (9)
      • Web (7)
      • 삽질기 (7)
    • Others (19)
      • 코딩테스트 풀이 (0)
      • IT 이야기 (18)
    • 쉼터 (2)
    • 개발공부 (14)
      • Network (1)
      • 자료구조와 알고리즘 (2)
      • Code design (8)
      • ETC (1)
      • 아카이브 (1)

블로그 메뉴

    공지사항

    • 프로그래머스 코딩 테스트 관련글 비공개 처리

    인기 글

    태그

    • 예제
    • 프로그래머스
    • 공부
    • ios
    • 프로그래밍
    • IT
    • 코딩테스트
    • kotlin
    • reduce
    • 타입
    • xcode
    • level1
    • swift
    • Java
    • map
    • Android
    • 구축
    • 개발
    • JSP
    • swfit

    최근 댓글

    최근 글

    티스토리

    hELLO · Designed By 정상우.
    Greensky0026
    IDE & Framework/Android

    [Kotlin] RecyclerView (3) - ItemTouchHelper로 Swipe event 구현

    [Kotlin] RecyclerView (3) - ItemTouchHelper로 Swipe event 구현
    IDE & Framework/Android

    [Kotlin] RecyclerView (3) - ItemTouchHelper로 Swipe event 구현

    2022. 2. 8. 20:20
    반응형

     RecyclerView 마지막 포스팅입니다.

    왼쪽에서 오른쪽으로 스와이프를 하면 통화를,

    반대로 오른쪽에서 왼쪽으로 스와이프를 하면 메세지 전송이 되도록 해보겠습니다.

     

    필요한 과정

    1. item_phonebook.xml 수정

    2. ItemTouchHelper.callback 구현

        ㄱ. 필수 메서드 구현

        ㄴ. 보조 메서드 구현

    3. Adapter에 ItemTouchHelper.callback 부착

    구현

    1. item_phonebook.xml 수정

      이제 두가지의 레이아웃이 필요합니다.

    스와이프 시킬 레이아웃은 이름과 전화번호부가 적혀있는 화면이구요,

    스와이프 되면 이제 양 사이드에 문자 아이콘과 전화 아이콘이 있어야겠죠?

    behind layout

    <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
    • 필요한 과정
    • 구현
    • 1. item_phonebook.xml 수정
    • 2. ItemTouchHelper.callback 구현
    • ㄱ. 필수메서드 구현
    • ㄱ. 보조 메서드 구현
    • 3. Adapter에 ItemTouchHelper.callback 부착
    • 예제 코드 전체 보기
    • RecyclerView 시리즈 다른글 보러가기
    •  
    'IDE & Framework/Android' 카테고리의 다른 글
    • Android Proguard
    • [Kotlin] PhoneStateListener deprecated, TelephonyCallback로 대체하기
    • [Kotlin] RecyclerView (2) - Filter와 SerachView로 검색기능 구현
    • [Kotlin] ViewPager2 사용하기
    Greensky0026
    Greensky0026
    점이 모여 선이 된다. 내 삽질도 언젠간 거대한 지하 도시가 되겠지!

    티스토리툴바

    개인정보

    • 티스토리 홈
    • 포럼
    • 로그인

    단축키

    내 블로그

    내 블로그 - 관리자 홈 전환
    Q
    Q
    새 글 쓰기
    W
    W

    블로그 게시글

    글 수정 (권한 있는 경우)
    E
    E
    댓글 영역으로 이동
    C
    C

    모든 영역

    이 페이지의 URL 복사
    S
    S
    맨 위로 이동
    T
    T
    티스토리 홈 이동
    H
    H
    단축키 안내
    Shift + /
    ⇧ + /

    * 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.