Translation animation, moving in from top, anim/move_in_from_top.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="-100%" android:toYDelta="0%" />
</set>Translation animation, moving in from bottom, anim/move_in_from_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="100%" android:toYDelta="0%" />
</set>Translation animation, moving out to the bottom, anim/move_out_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="100%" />
</set>Translation animation, moving out to the top, anim/move_out_top.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="-100%" />
</set>The layout file for the list item, list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/lightblue"
android:focusable="true">
<View android:id="@+id/view_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:visibility="invisible"/>
<TextView android:id="@+id/topic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ffffff"
android:gravity="center"
tools:text="Education"/>
</RelativeLayout>The layout file for the main activity with the RecyclerView, activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="200dp"
android:layout_height="match_parent"
android:scrollbars="none"
android:descendantFocusability="afterDescendants"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>The RecylerView adapter, this class does all the animation jobs. ItemListAdapter.kt
import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.TextView
import android.widget.Toast
import kotlinx.android.synthetic.main.list_item.view.*
class ItemListAdapter(private val itemList: List<String>): RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {
private var previousFocusedViewHolder: ViewHolder? = null
private var previouslyFocusedPos = 0
private var currentlyFocusedPos = 0
private lateinit var recyclerView: RecyclerView
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return ViewHolder(layoutView)
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
Log.d("ItemListAdapter", "onAttachedToRecyclerView ")
this.recyclerView = recyclerView
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Log.d("ItemListAdapter", "onBindViewHolder position: $position")
holder.itemView.setOnFocusChangeListener { focusedView, hasFocus ->
Log.d("ItemListAdapter", "onBindViewHolder OnFocusChangeListener position: $position")
updateFocusPositions(holder, hasFocus, position)
startFocusAnimation(holder, hasFocus)
}
holder.tvTopic.text = itemList[position]
}
override fun getItemCount() = itemList.size
private fun startFocusAnimation(holder: ViewHolder, hasFocus: Boolean) {
Log.d("ItemListAdapter", "startFocusAnimation hasFocus: $hasFocus, currentlyFocusedPos: $currentlyFocusedPos, previouslyFocusedPos: $previouslyFocusedPos")
if (hasFocus) {
previousFocusedViewHolder?.let {
val moveOutAnimSet = if (currentlyFocusedPos > previouslyFocusedPos) R.anim.move_out_down else R.anim.move_out_up
val moveOutAnim = AnimationUtils.loadAnimation(it.itemBg.context, moveOutAnimSet)
moveOutAnim.fillAfter = true
moveOutAnim.duration = 250
it.itemBg.startAnimation(moveOutAnim)
Log.d("ItemListAdapter", "startFocusAnimation, moving out from previousViewHolder: $it")
}
val moveInAnimSet = if (currentlyFocusedPos > previouslyFocusedPos) R.anim.move_in_from_top else R.anim.move_in_from_bottom
val moveInAnim = AnimationUtils.loadAnimation(holder.itemBg.context, moveInAnimSet)
moveInAnim.fillAfter = true
moveInAnim.duration = 400
holder.itemBg.startAnimation(moveInAnim)
Log.d("ItemListAdapter", "startFocusAnimation, moving into the currentViewHolder: $holder")
}
}
private fun updateFocusPositions(viewHolder: ViewHolder, hasFocus: Boolean, position: Int) {
if (hasFocus) {
previouslyFocusedPos = currentlyFocusedPos
currentlyFocusedPos = position
} else {
previousFocusedViewHolder = viewHolder
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
var itemBg: View
var tvTopic: TextView
init {
itemView.setOnClickListener(this)
tvTopic = itemView.topic
itemBg = itemView.view_bg
}
override fun onClick(view: View) {
Toast.makeText(view.context, "Clicked Position = " + adapterPosition, Toast.LENGTH_SHORT).show()
}
}
}The MainActivity.kt
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private lateinit var linearLayoutManager: LinearLayoutManager
private lateinit var listAdapter: ItemListAdapter
val topics = listOf("Education","Finance","Government","Entertainment","Technology","Math","Biology","Physics","Chemistry","Space","Sports","Music","Animal","Countries","Weather","Politics","Traffic","Poverty","Social Media","Internet","Housing")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
linearLayoutManager = LinearLayoutManager(this)
listAdapter = ItemListAdapter(topics)
recycler_view.setHasFixedSize(true)
recycler_view.layoutManager = linearLayoutManager
recycler_view.adapter = listAdapter
}
}