# Recycler View ItemTouchHelper & onSaveInsrance事件用法- kotlin [延續recyclerView程式碼](https://hackmd.io/JfbNEXvpQ0aruQfy9MXgCA) 1. 在OnePieceAdapter.kt檔案, OnePieceAdapter類別底下新增巢狀類別,[程式碼第66行]inner class ItemDragHelperCallback 2. [程式碼第11行]定義一個attachiToRecyclerView涵式將TouchHelper跟RecyclerView綁在一起 ```kotlin= class OnePieceAdapter() : RecyclerView.Adapter<OnePieceAdapter.ViewHolder>(){ var fruit : MutableList<OnePiece> = mutableListOf() private var itemClickListener : ItemClickListener ?= null private val mItemDragHelperCallback = ItemDragHelperCallback() //触摸辅助类 可以用于主动调用删除 private val mItemTouchHelper = ItemTouchHelper(mItemDragHelperCallback) init { //Log.d("類別初始化", "init執行了") } open fun attachiToRecyclerView(recyclerView :RecyclerView){ mItemTouchHelper.attachToRecyclerView(recyclerView) } fun reload(list: List<OnePiece>){ fruit.clear() fruit.addAll(list) notifyDataSetChanged() } inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view){ //viewHolder定義要顯示在recyclerView的layout元件 val fruitImage = view.findViewById<ImageView>(R.id.fruitImage) val fruitName = view.findViewById<TextView>(R.id.fruitName) fun bindData(data: OnePiece){ fruitImage.setImageResource(data.imageId) fruitName.text = data.name //設定圖片clicker監聽 fruitImage.setOnClickListener(){ itemClickListener?.showClick(data) } } } //期望取得綁定viewHolder的資料 open fun getCheckedItems(): ArrayList<OnePiece> { val checkedItems :ArrayList<OnePiece> = ArrayList<OnePiece>() for (i in 0 until fruit.size) { if (fruit.isNotEmpty()) { //checkedItems.add(fruit[i]) checkedItems.add(OnePiece(fruit[i].name, fruit[i].imageId)) Log.d("取得修改後資料", "${checkedItems[i]}") } } return checkedItems } fun setToshowClickListener(listener : ItemClickListener){ //用以呼叫的 itemClickListener = listener } //set方法,可以供Activity或Fragment呼叫 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { //把要顯示在recyclerView的view inflate出來 //Log.d("onCreateViewHolder", "onCreateViewHolder準備執行") val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false) return ViewHolder(view) } override fun getItemCount(): Int { return fruit.size } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bindData(fruit[position]) } //====================== inner class ItemDragHelperCallback: ItemTouchHelper.Callback() { override fun getMovementFlags( //此處return可以滑動的方向值 recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { var swipe = 0 var move = 0 recyclerView.let{ if(recyclerView.layoutManager is GridLayoutManager){ //如果Grid layout支持上下左右滑動 move = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT } else if (recyclerView.layoutManager is LinearLayoutManager){ //支持上下滑動 move = ItemTouchHelper.UP or ItemTouchHelper.DOWN //支持左右滑動刪除 swipe = ItemTouchHelper.START or ItemTouchHelper.END } } return ItemTouchHelper.Callback.makeMovementFlags(move, swipe) } override fun onMove( //此處return 是否可以拖動 布林值 recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { val fromPos = viewHolder.adapterPosition val toPos = target.adapterPosition fruit.let { val from = fruit[fromPos] fruit.removeAt(fromPos) Log.d("移除選擇要移動的物件", "${fruit}") fruit.add(toPos,from) Log.d("插入移動完成的物件", "${fruit}") notifyItemMoved(fromPos,toPos) return true } } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { //用於滑動刪除 fruit.let { var position : Int = viewHolder.adapterPosition it.removeAt(position) notifyItemRemoved(position) } } override fun isLongPressDragEnabled(): Boolean { return super.isLongPressDragEnabled() } override fun isItemViewSwipeEnabled(): Boolean { return super.isItemViewSwipeEnabled() } //加入滑動刪除的效果圖片 @SuppressLint("ResourceType") override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE){ var RecyclerViewSwipeDecorator = RecyclerViewSwipeDecorator.Builder(c,recyclerView,viewHolder,dX,dY,actionState,isCurrentlyActive) RecyclerViewSwipeDecorator.let { it.addBackgroundColor(ContextCompat.getColor(recyclerView.context,android.R.color.holo_red_dark)) it.addActionIcon(R.drawable.ic_baseline_delete_24) } .create() .decorate() super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) } } override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { //設計拖曳動畫 super.onSelectedChanged(viewHolder, actionState) if(actionState != ItemTouchHelper.ACTION_STATE_IDLE){ viewHolder?.let { if(viewHolder is ViewHolder){ viewHolder.fruitImage!!.visibility = View.VISIBLE } } } } override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { super.clearView(recyclerView, viewHolder) viewHolder?.let { if(viewHolder is ViewHolder){ //viewHolder.fruitImage!!.visibility = View.GONE } } } } } ``` 3. mainActivity.kt, [程式碼第26行]呼叫attachiToRecyclerView綁定recyclerView ,就完成recyclerView拖曳功能了 ##### 接下來說明旋轉螢幕之後,如何保存recyclerView的狀態 1. 首先將data class 加上宣告 ```kotlin= @Parcelize data class OnePiece(val name: String, val imageId: Int) : Parcelable ``` 2. [程式碼第43行]onSaveInstanceState 這邊來儲存我們旋轉螢幕過程把資料保存下來 3. [程式碼第45行]透過linearLayoutManager內建的onSaveInstance()方法保存recyclerView目前滑動到哪個位置 4. [程式碼第48行]透過自己定義的涵式getCheckedItems取得我們拖曳改變List順序後的資料並保存 5. [程式碼第16~24行]判斷是否要重新生成資料或者取用被保留下來的資料 ```kotlin=0 private const val BUNDLE_RECYCLER_LAYOUT = "recycler_layout" private const val BUNDLE_JSON_LIST = "json_list" class MainActivity : AppCompatActivity() { //定義要綁定給Adapter的資料 lateinit var fruitList : ArrayList<OnePiece> lateinit var recyclerView : RecyclerView lateinit var adapter : OnePieceAdapter lateinit var linearLayoutManager: LinearLayoutManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) recyclerView = findViewById(R.id.recyclerView) linearLayoutManager = LinearLayoutManager(this) adapter = OnePieceAdapter() if(savedInstanceState == null){ Log.d("第一次執行onCreate", "savedInstanceState 是null的") fruitList = initFruits() } else { Log.d("第一次執行onCreate", "savedInstanceState 不是null") val savedRecyclerLayoutState = savedInstanceState.getParcelable<Parcelable>(BUNDLE_RECYCLER_LAYOUT)!! fruitList = savedInstanceState.getParcelableArrayList<OnePiece>(BUNDLE_JSON_LIST)!! linearLayoutManager.onRestoreInstanceState(savedRecyclerLayoutState) } //綁itemTouchHelper adapter.attachiToRecyclerView(recyclerView) //adapter.getCheckedItems() adapter.setToshowClickListener(object: ItemClickListener{ override fun showClick(data: OnePiece) { toShwo() } }) //布局Layout Manager & 設定adapter給recyclerView recyclerView.adapter = adapter recyclerView.layoutManager = linearLayoutManager //使用內建的分隔線↓ recyclerView.addItemDecoration(DividerItemDecoration(this@MainActivity,LinearLayoutManager.VERTICAL)) //重新載入資料給adapter adapter.reload(fruitList) } //保存Recyclerview實例 override fun onSaveInstanceState(outState: Bundle) { //↓保存recyclerView位置資訊 outState.putParcelable(BUNDLE_RECYCLER_LAYOUT,linearLayoutManager.onSaveInstanceState()) Log.d("存存存", "${adapter.getCheckedItems()}") //保存recyclerView資料變更後的List outState.putParcelableArrayList(BUNDLE_JSON_LIST,adapter.getCheckedItems()) super.onSaveInstanceState(outState) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) } @SuppressLint("ShowToast") private fun toShwo() { Toast.makeText(this@MainActivity, "hello",Toast.LENGTH_SHORT).show() } private fun initFruits(): ArrayList<OnePiece> { val create_fruitList = arrayListOf<OnePiece>() repeat(2) { create_fruitList.add(OnePiece("魯夫", R.drawable.luffy)) create_fruitList.add(OnePiece("布魯克",R.drawable.brook)) create_fruitList.add(OnePiece("佛朗基",R.drawable.frank)) create_fruitList.add(OnePiece("基拉",R.drawable.killer)) create_fruitList.add(OnePiece("喬巴", R.drawable.chopper)) create_fruitList.add(OnePiece("娜美", R.drawable.nami)) create_fruitList.add(OnePiece("羅賓", R.drawable.robin)) create_fruitList.add(OnePiece("香吉", R.drawable.sanji)) create_fruitList.add(OnePiece("烏索普", R.drawable.sogeking)) create_fruitList.add(OnePiece("德瑞克", R.drawable.xdrike)) create_fruitList.add(OnePiece("拖拉法爾加-羅", R.drawable.trafalg)) create_fruitList.add(OnePiece("儸儸諾亞-索隆", R.drawable.zoro)) } return create_fruitList } } ``` ###### tags: `recycler view` `kotlin` `Android` `ItemTouchHelper`