-->

Animate Recycler View grid when number of columns

2020-08-27 03:04发布

问题:

I am using a RecyclerView with GridLayoutManager. Users can toggle the span count between 2 and 4, which would result in an animation that runs the inbuilt translate animation for each cell to it's new position. Code I have been using thus far is:

TransitionManager.beginDelayedTransition(moviesGridRecycler);
gridLayoutManager.setSpanCount(NEW_SPAN_COUNT);
adapter.notifyDataSetChanged();

This has been working fine for me, but now I need to have a different layout for each span count. To support this, I have 2 view types in the RecyclerView and since the view type is changed when moving to a new span count, RecyclerView is unable to see that it's the "same" content, and the default translate animation is not run.

If I enable layout transitions, I get view entry animations and not view position change animations. Cell views for both span count are having a width and height as match_parent and wrap_content.

As an alternative, I tried dropping the new view type all together, and just having one view type. I have a FrameLayout, which holds views for both span counts. In onBindViewHolder(), I simply toggle visibility of child views. This too results in no animations.

I followed this thread and this one, but could not resolve my issue.

Full Code:

    class ItemFragment : Fragment() {
    private var spanCount = 2
    lateinit var recyclerView: RecyclerView
    lateinit var button: Button

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_item_list, container, false)
        recyclerView = view.findViewById(R.id.listView)
        recyclerView.apply {
            adapter = MyItemRecyclerViewAdapter(DummyContent.ITEMS, spanCount)
            layoutManager = GridLayoutManager(context, spanCount)
            addItemDecoration(RecyclerGridDecoration(context))
        }
        button = view.findViewById(R.id.toggle)
        button.setOnClickListener { onToggle() }

        return view
    }

    fun onToggle() {
        spanCount = if (spanCount == 2) 4 else 2
        TransitionManager.beginDelayedTransition(recyclerView)
        (recyclerView.layoutManager as GridLayoutManager).spanCount = spanCount
        (recyclerView.adapter!! as MyItemRecyclerViewAdapter).apply {
            span = spanCount
            notifyDataSetChanged()
        }
    }

}



class MyItemRecyclerViewAdapter(
        private val mValues: List<DummyItem>,
        var span: Int)
    : RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        if (viewType == 2) {
            val view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.items_two, parent, false)
            return TwoViewHolder(view)
        } else {
            val view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.items_four, parent, false)
            return FourViewHolder(view)
        }

    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = mValues[position]
        if (holder is TwoViewHolder) {
            holder.textTwo.text = item.content
        } else if (holder is FourViewHolder) {
            holder.textFour.text = item.content
        }
    }

    override fun getItemCount(): Int = mValues.size

    abstract inner class ViewHolder(mView: View) : RecyclerView.ViewHolder(mView)

    inner class TwoViewHolder(mView: View) : ViewHolder(mView) {
        val image: ImageView = mView.imageTwo
        val textTwo: TextView = mView.textTwo

    }

    inner class FourViewHolder(mView: View) : ViewHolder(mView) {
        val textFour: TextView = mView.textFour
    }

    override fun getItemViewType(position: Int): Int {
        return span
    }
}

回答1:

I have a solution for your problem change your toggle function to this:

  fun onToggle() {
        spanCount = if (spanCount == 2) 4 else 2
        (recyclerView.adapter!! as MyItemRecyclerViewAdapter).apply {
            span = spanCount
            notifyDataSetChanged()
        }
recyclerView.post(object:Runnable {
  public override fun run() {
     TransitionManager.beginDelayedTransition(recyclerView)
        (recyclerView.layoutManager as GridLayoutManager).spanCount = spanCount
  }
})
   }

In the above code we are first changing the viewType of recyclerView and then we are changing spanCount of GridLayoutManager on RecyclerView UI Handler Queue, so to achieve the two UI operations serially.