h6ah4i / android-advancedrecyclerview

RecyclerView extension library which provides advanced features. (ex. Google's Inbox app like swiping, Play Music app like drag and drop sorting)
https://advancedrecyclerview.h6ah4i.com/
Apache License 2.0
5.32k stars 860 forks source link

draggable staggered grid layout notifydatasetchanged not work #486

Open hadighasemian opened 5 years ago

hadighasemian commented 5 years ago

i am using realm database and create a data provider. after delete a task in taskactivity and return to task fragment, fragment return error object was deleted in other thread. i have to notifydatasetchanged() on onresume() fragment. but how?

class TaskFragmentDataProvider  {
       private var mData: MutableList<ConcreteData>
       private var mLastRemovedData: ConcreteData? = null
       private var mLastRemovedPosition = -1
       var realm = RealmManager.getNewInstance()

val count: Int
    get() = mData.size

init {
    mData = RefreshTaskData()
    notifyDataSetChange()
}
fun notifyDataSetChange(){
    var objects = TaskManager.findAllAsync(true)
    objects!!.addChangeListener { _,_->
        RefreshTaskData()
    }
}
fun RefreshTaskData(): MutableList<ConcreteData> {
     mData =  LinkedList()
    val objects = TaskManager.findAll(true)
    var i=0
    objects!!.forEach {
        val id = it.id!!
        val viewType =i++
        val swipeReaction =
            RecyclerViewSwipeManager.REACTION_CAN_SWIPE_UP or RecyclerViewSwipeManager.REACTION_CAN_SWIPE_DOWN
        mData.add(ConcreteData(id,viewType, swipeReaction,it))
    }
    return mData
}

fun getItem(index: Int): ConcreteData {
    if (index < 0 || index >= count) {
        throw IndexOutOfBoundsException("index = $index")
    }

    return mData[index]
}
fun getTaskItems(position: Int): RealmResults<Entry>? {
    return getItem(position).task.entries.where().findAll()
        .sort(arrayOf("sortId","id"), arrayOf(Sort.DESCENDING, Sort.ASCENDING))
}

fun undoLastRemoval(): Int {
    if (mLastRemovedData != null) {
        val insertedPosition: Int = if (mLastRemovedPosition >= 0 && mLastRemovedPosition < mData.size) {
            mLastRemovedPosition
        } else {
            mData.size
        }

        mData.add(insertedPosition, mLastRemovedData!!)

        mLastRemovedData = null
        mLastRemovedPosition = -1

        return insertedPosition
    } else {
        return -1
    }
}

fun moveItem(fromPosition: Int, toPosition: Int) {
    if (fromPosition == toPosition) {
        return
    }

    val item = mData.removeAt(fromPosition)

    mData.add(toPosition, item)
    mLastRemovedPosition = -1
    realm.executeTransaction {
        mData.forEach {
            it.task.sortId = mData.indexOf(it).toLong()
        }
    }

}

fun swapItem(fromPosition: Int, toPosition: Int) {
    if (fromPosition == toPosition) {
        return
    }
    Collections.swap(mData, toPosition, fromPosition)
    mLastRemovedPosition = -1
}

fun removeItem(position: Int) {

    val removedItem = mData.removeAt(position)

    mLastRemovedData = removedItem
    mLastRemovedPosition = position
}
class ConcreteData internal constructor(val id: Long, val viewType: Int, swipeReaction: Int, var task: Task)

} `

`

internal class TaskAdapter(private val mProvider: TaskFragmentDataProvider, var context: Context?) :
    RecyclerView.Adapter<TaskAdapter.BaseViewHolder>(),
    DraggableItemAdapter<TaskAdapter.BaseViewHolder> {

open class BaseViewHolder(v: View) : AbstractDraggableItemViewHolder(v)

class HeaderItemViewHolder(v: View) : BaseViewHolder(v)

class NormalItemViewHolder(v: View) : BaseViewHolder(v) {
    var mContainer: ViewGroup = v.findViewById(R.id.root)
    var title: TextView = v.findViewById(R.id.title)
    var items: RecyclerView = v.findViewById(R.id.items)
    var pin: ImageView = v.findViewById(R.id.pin)
    var percent: PercentView = v.findViewById(R.id.percent)
}

init {

    // DraggableItemAdapter requires stable ID, and also
    // have to implement the getItemId() method appropriately.
    setHasStableIds(true)

}

override fun getItemId(position: Int): Long {
    return if (isHeader(position)) {
        RecyclerView.NO_ID
    } else {
        mProvider.getItem(toNormalItemPosition(position)).id
    }
}

override fun getItemViewType(position: Int): Int {
    return if (isHeader(position)) {
        ITEM_VIEW_TYPE_HEADER
    } else {
        ITEM_VIEW_TYPE_NORMAL_ITEM_OFFSET + mProvider.getItem(toNormalItemPosition(position)).viewType
    }
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    when (viewType) {
        ITEM_VIEW_TYPE_HEADER -> {
            // NOTE:
            // This dummy header item is required to workaround the
            // weired animation when occurs on moving the item 0
            //
            // Related issue
            //   Issue 99047:   Inconsistent behavior produced by mAdapter.notifyItemMoved(indexA,indexB);
            //   https://code.google.com/p/android/issues/detail?id=99047&q=notifyItemMoved&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened&
            val v = inflater.inflate(R.layout.dummy_header_item, parent, false)
            return HeaderItemViewHolder(v)
        }
        else -> {
            val v = inflater.inflate(R.layout.row_grid_task, parent, false)
            return NormalItemViewHolder(v)
        }
    }
}

override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
    if (isHeader(position)) {
        onBindHeaderViewHolder(holder as HeaderItemViewHolder, position)
    } else {
        onItemViewHolder(holder as NormalItemViewHolder, toNormalItemPosition(position))
    }
}

private fun onBindHeaderViewHolder(holder: HeaderItemViewHolder, position: Int) {
    val lp = holder.itemView.layoutParams as StaggeredGridLayoutManager.LayoutParams
    lp.isFullSpan = true
}

private fun onItemViewHolder(holder: NormalItemViewHolder, position: Int) {

    val obj = mProvider.getItem(position)

    holder.title.text = obj.task.title
    if (obj.task.pin)holder.pin.visibility = View.VISIBLE

    val lyt = LinearLayoutManager(context)
    lyt.orientation = RecyclerView.VERTICAL
    holder.items.layoutManager = lyt
    holder.items.adapter = TaskRowEntriesAdapter(context!!, mProvider.getTaskItems(position) as List<Entry>)

    percentPercent(holder.percent,obj.task.donePercent())

    holder.mContainer.setOnClickListener {
        val intent = Intent(context, TaskActivity::class.java)
        intent.putExtra("task_id",obj.task.id)
        intent.putExtra("edit",true)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            holder.mContainer.transitionName = obj.task.id.toString()
            holder.title.transitionName = obj.task.title
            val pair1 = androidx.core.util.Pair(holder.mContainer as View, holder.mContainer.transitionName)
            val optionsCompat =
                        ActivityOptionsCompat.makeSceneTransitionAnimation(context as Activity, pair1)
                    context!!.startActivity(intent, optionsCompat.toBundle())
        }else{
            context!!.startActivity(intent)
        }
    }
    holder.items.setOnClickListener {
        holder.mContainer.callOnClick()
    }
    // set item view height
    val context = holder.itemView.context
    val itemHeight = calcItemHeight(context, obj.task)
    val lp = holder.items.layoutParams
    if (lp.height != itemHeight) {
        lp.height = itemHeight
        holder.items.layoutParams = lp
    }

    // set background resource (target view ID: container)
    val dragState = holder.dragState

    if (dragState.isUpdated) {
        val bgResId: Int = when {
            dragState.isActive -> R.drawable.bg_item_dragging_active_state
            dragState.isDragging -> R.drawable.bg_item_dragging_state
            else -> R.drawable.selector_radius_card_white
        }
        holder.mContainer.setBackgroundResource(bgResId)
    }
}

private fun percentPercent(percent: PercentView, donePercent: Int) {
    percent.updatePercentValue(donePercent)
    var backPercentColor = when {
        donePercent<25 -> ContextCompat.getColor(context!!,R.color.prim_red)
        donePercent<50 -> ContextCompat.getColor(context!!,R.color.prim_orange3)
        donePercent<75 -> ContextCompat.getColor(context!!,R.color.prim_yellow)
        else -> ContextCompat.getColor(context!!,R.color.prim_green)
    }
    percent.setPercentBackground(Shape.creatShape(
        backPercentColor,
        Screen.dp2screen(context!!,1),
        ContextCompat.getColor(context!!,R.color.header1),
        20f,20f,20f,20f))
}

override fun getItemCount(): Int {
    val headerCount = headerItemCount
    val count = mProvider.count
    return headerCount + count
}

override fun onMoveItem(fromPosition: Int, toPosition: Int) {
    var fromPosition = fromPosition
    var toPosition = toPosition

    fromPosition = toNormalItemPosition(fromPosition)
    toPosition = toNormalItemPosition(toPosition)

    mProvider.moveItem(fromPosition, toPosition)
}

override fun onCheckCanStartDrag(holder: BaseViewHolder, position: Int, x: Int, y: Int): Boolean {
    return !isHeader(position)
}

override fun onGetItemDraggableRange(holder: BaseViewHolder, position: Int): ItemDraggableRange? {
    val headerCount = headerItemCount
    return ItemDraggableRange(headerCount, itemCount - 1)
}

override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
    return  (mProvider.getItem(draggingPosition).task.pin
            ==
            mProvider.getItem(dropPosition).task.pin)

}

override fun onItemDragStarted(position: Int) {
    notifyDataSetChanged()
}

override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {

    notifyDataSetChanged()
}

companion object {
    private val TAG = "MyDraggableItemAdapter"
    private val ITEM_VIEW_TYPE_HEADER = 0
    private val ITEM_VIEW_TYPE_NORMAL_ITEM_OFFSET = 1

    private val USE_DUMMY_HEADER = true
    private val RANDOMIZE_ITEM_SIZE = true

    val headerItemCount: Int
        get() = if (USE_DUMMY_HEADER) 1 else 0

    fun isHeader(position: Int): Boolean {
        return position < headerItemCount
    }

    fun toNormalItemPosition(position: Int): Int {
        return position - headerItemCount
    }

    fun calcItemHeight(context: Context, item: Task): Int {
        var tl = 0
        item.entries.forEach {
            when(it.type){
                Is_Item->{
                    tl++
                }
                Is_Text->{
                    tl += StringHelper.countLine(it.text!!.text,20)
                }
                Is_Task->{
                    tl++
                }
            }
        }
        return (if(tl>10){
            10
        }else{
            tl
        })* Screen.dp2screen(context,20)
    }

    fun swapBit(x: Int, pos1: Int, pos2: Int): Int {
        val m1 = 1 shl pos1
        val m2 = 1 shl pos2
        var y = x and (m1 or m2).inv()

        if (x and m1 != 0) {
            y = y or m2
        }
        if (x and m2 != 0) {
            y = y or m1
        }

        return y
    }
}

}

`

my fragment:

class TaskFragment : Fragment() {
private var listener: MainNavFragmentInteractionListener? = null
private var mRecyclerView: RecyclerView? = null
private var mLayoutManager: StaggeredGridLayoutManager? = null
private var mAdapter: RecyclerView.Adapter<*>? = null
private var mWrappedAdapter: RecyclerView.Adapter<*>? = null
private var mRecyclerViewDragDropManager: RecyclerViewDragDropManager? = null
private var myItemAdapter:TaskAdapter?=null
var realm = RealmManager.getNewInstance()
var taskDataProvider = TaskFragmentDataProvider()
var position:Int=0
var posArr: IntArray = intArrayOf(0,0)
lateinit var objects : RealmResults<Task>

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {

    val layout =  inflater.inflate(R.layout.task_fragment, container, false)
    inite(layout)
    return layout
}
private fun inite(l: View) {
    mRecyclerView = l.list
    initeHeader(l)
    reiniteList(position)
}

private fun reiniteList(position:Int) {
    taskDataProvider = TaskFragmentDataProvider()

    initeList(position)
}
private fun refreshList(position:Int) {
    reiniteList(position)
}

private fun initeHeader(layout: View) {

    layout.ic_header_nav.setOnClickListener {
        listener?.onNavInteraction("change")
    }
    layout.ic_header_help.setOnClickListener {
    }
}

private fun initeList(position: Int) {
    mLayoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
    mRecyclerViewDragDropManager = RecyclerViewDragDropManager()
    mRecyclerViewDragDropManager!!.setDraggingItemShadowDrawable(
        ContextCompat.getDrawable(requireContext(), R.drawable.material_shadow_z3) as NinePatchDrawable?
    )

    mRecyclerViewDragDropManager!!.setInitiateOnLongPress(true)
    mRecyclerViewDragDropManager!!.setInitiateOnMove(false)
    mRecyclerViewDragDropManager!!.setLongPressTimeout(750)

    mRecyclerView!!.setHasFixedSize(true)
    mRecyclerView!!.layoutManager = mLayoutManager

    if (supportsViewElevation()) {
    } else {
        mRecyclerView!!.addItemDecoration(
            ItemShadowDecorator(
                (ContextCompat.getDrawable(
                    requireContext(),
                    R.drawable.material_shadow_z1
                ) as NinePatchDrawable?)!!
            )
        )
    }
    mRecyclerViewDragDropManager!!.attachRecyclerView(mRecyclerView!!)
    myItemAdapter = TaskAdapter(taskDataProvider,context)
    mAdapter = myItemAdapter

    mWrappedAdapter = mRecyclerViewDragDropManager!!.createWrappedAdapter(myItemAdapter!!)      // wrap for dragging
    mRecyclerView!!.adapter = mWrappedAdapter  // requires *wrapped* adapter

}
private fun supportsViewElevation(): Boolean {
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
}

override fun onAttach(context: Context) {
    super.onAttach(context)
    if (context is MainNavFragmentInteractionListener) {
        listener = context
    } else {
        throw RuntimeException("$context must implement homeFragmentInteractionListener")
    }
}

override fun onResume() {
    refreshList(posArr[1])
    super.onResume()

}
override fun onPause() {
    mRecyclerViewDragDropManager!!.cancelDrag()
    posArr = mLayoutManager!!.findFirstCompletelyVisibleItemPositions(posArr)

    posArr.forEach {
        println(it)
    }

    super.onPause()
}
override fun onDestroyView() {
    if (mRecyclerViewDragDropManager != null) {
        mRecyclerViewDragDropManager!!.release()
        mRecyclerViewDragDropManager = null
    }

    if (mRecyclerView != null) {
        mRecyclerView!!.itemAnimator = null
        mRecyclerView!!.adapter = null
        mRecyclerView = null
    }

    if (mWrappedAdapter != null) {
        WrapperAdapterUtils.releaseAll(mWrappedAdapter)
        mWrappedAdapter = null
    }
    mAdapter = null
    mLayoutManager = null

    super.onDestroyView()
}

override fun onDetach() {
    super.onDetach()
    listener = null
}
companion object {
    fun newInstance() = TaskFragment()
}

}