RP2 week3 - ListView) 2021.01.14

Star·2021년 1월 15일
0

📝Daily Report

시간

오후 9시 30분 ~ 새벽 2시
시간이 모자른데 너무 늦게 시작했다.

Daily Schedule

1)Drag & Drop - 어제 못다했던 드래그 & 드롭으로 리스트 순서 변경을 완료한다. 리스트 어댑터의 getView()에서 setOnTouchListener와 setOnDragListener를 각 View에 달아주면 될 것 같다.
--> 예상 시간) 2시간

2) RecyclerView로 변경 - ListView에 충분히 익숙해졌으니 지금껏 만든 것을 RecyclerView로 변경한다.
--> 예상 시간) 1시간 30분

3) DB(Room) 구축 - 이후 검색이나 장르별 정렬 등 추가 구현하기 위함
--> 예상 시간) 오늘 여기까진 시작 못할 것 같다

결과

>>Drag & Drop) 5시간
https://developer.android.com/guide/topics/ui/drag-drop
먼저 위 공식문서 페이지를 정독했다. 로직이랑 코드를 최대한 이해해보려고 했는데, 시간이 많이 걸렸다.

중간에 막혔던 건 드래그 드롭 이벤트는 Adapter에서 처리를 하는데, 리스트 데이터의 원본은 MusicActiviy에서 수정해야하는 것이었는데, 이 부분에서도 시간을 많이 사용했다.
인터페이스와 콜백함수를 사용해서 해결할 수 있었다.

  1. Adapter에서 반응한 이벤트를 MusicActivity에서 처리하기 위해,
  • 인터페이스를 따로 정의하고,
interface OnItemDrop {
    fun onDrop(fromIdx: Int, toIdx: Int)
}
  • 이벤트를 처리할 MusicActivity에 인터페이스를 구현하고,
class MusicActivity : AppCompatActivity(), OnItemDrop {
  • 리스트를 업데이트할 콜백함수를 override한다.
// 어댑터에서 Drag & Drop 발생시 인덱스를 받아서 리스트를 수정하고 반영하기 위한 콜백함수
override fun onDrop(fromIdx: Int, toIdx: Int) {
    if(fromIdx > toIdx) {                           // 아래에서 위로 드래그 하면,
        musicList.add(toIdx, musicList[fromIdx])    // toIdx(드롭) 위치로 옮기고,
        musicList.removeAt(fromIdx+1)         // 변경 전의 toIdx 위치를 포함해서 그 아래에 나머지는 한칸씩 내린다
    }
    else {                                                  // 위에서 아래로 드래그 할 때,
        musicList.add(toIdx + 1, musicList[fromIdx])     // toIdx(드롭) 위치로 옮기고,
        musicList.removeAt(fromIdx)                         // 변경 전 toIdx 위치를 포함해서 그 위에 나머지는 위로 옮긴다
    }
    musicListAdapter.notifyDataSetChanged()
}
  • 어댑터를 생성할 때 함께 넘겨준다.
musicListAdapter = MusicListAdapter(this, musicList, this)

마지막의 this는 인터페이스를 구현한 객체를 의미한다.


  1. 어댑터에서 열심히 사용해준다.
class MusicListAdapter(context: Context, private val musicArrayList: ArrayList<MusicData>,
                       private val dropCallback: OnItemDrop): BaseAdapter() {
  • 어댑터의 getView()에서 각 리스트뷰 아이템에 touch listener와 drag listener를 연결해준다.
// list item을 터치했을 때
view.setOnTouchListener { v, motionEvent ->
    if(motionEvent?.action == MotionEvent.ACTION_DOWN){
        val dragShadowBuilder = MyDragShadowBuilder(v)  // 시스템은 위 객체를 통해 드래그 이미지를 그린다.
        val item = ClipData.Item(position.toString())   // 드롭된 View에 전달할 드래그중인 view의 인덱스
        // 전달할 데이터 담아서 인스턴스화
        val dragData = ClipData("position", arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN), item)

        /* 시스템에 드래그가 시작됐음을 알리면(startDrag()), 
        시스템은 그 응답으로 dragShadowBuilder에서 콜백 메소드 호출하여 drag shadow를 그린다.*/
        v?.startDrag(dragData,dragShadowBuilder,v,0)
        true
    }else{
        false
    }
}

view드래그된 list item의 포지션 값은 ClipData를 사용하여 startDrag로 시스템에 전달하고, Drop이 발생했을 때, Drop된 View에서 DragEvent를 통해 사용할 수 있다.

  • drop이 되면, 순서를 바꿀 위치와 함께 콜백함수를 호출한다.
// 시스템이 보내는 드래그 이벤트를 수신하는 리스너
view.setOnDragListener { v, event ->
	when (event.action) {
    	// 드래그 시작 이벤트에서 true를 반환한 리스너만 현재 드래그가 끝날 때까지 드래그 이벤트를 계속 수신한다.
        DragEvent.ACTION_DRAG_STARTED -> true

	// 드래그를 드롭했을 때,
    	DragEvent.ACTION_DROP -> {
        	// setOnTouchListener()에서 담았던 드래그된 아이템의 인덱스
            	val fromPosition = event.clipData.getItemAt(0).text.toString().toInt()
                val toPosition = position   // 드롭된 위치의 position

		// MusicActivity에 드래그 & 드롭을 알리는 콜백 함수
    		// List View의 배열을 업데이트해서 드래그 & 드롭 결과를 반영한다.
            	dropCallback.onDrop(fromPosition, toPosition)
                true
         }
         else -> false
 }
        

시간이 오래 걸렸는데, 왠지 이렇게 하면 될 것만 같아서 끝까지 위와 같은 방식으로 했다. 물론 이렇게 오래 걸릴줄은 몰랐지..ㅋ

After...

  • 클래스간 이벤트, 데이터 전달에 대해 고민하는데 정말 많은 시간을 썼다. 객체지향과 코틀린 문법은 꼭 시간을 내서 조금씩이라도 공부를 해야할 것 같다.
profile
To be Developer

0개의 댓글