시간
오후 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에서 수정해야하는 것이었는데, 이 부분에서도 시간을 많이 사용했다.
인터페이스와 콜백함수를 사용해서 해결할 수 있었다.
interface OnItemDrop {
fun onDrop(fromIdx: Int, toIdx: Int)
}
class MusicActivity : AppCompatActivity(), OnItemDrop {
// 어댑터에서 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는 인터페이스를 구현한 객체를 의미한다.
class MusicListAdapter(context: Context, private val musicArrayList: ArrayList<MusicData>,
private val dropCallback: OnItemDrop): BaseAdapter() {
// 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를 통해 사용할 수 있다.
// 시스템이 보내는 드래그 이벤트를 수신하는 리스너
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...