In this blog post, we will be discussing the new ViewPager2 widget introduced by Google. The first version of this component was released on February 7, 2019. At the writing of this post, this widget is on version beta05.
Most of the developers might be aware of View Pager which we have been using for the Onboarding screen of our application where this view group can hold multiple fragments/views with its swipe support. However, we still always looked at third-party libraries to make it customizable for our needs.
Now comes the good news, considering all the issues reported by developers towards the earlier version of ViewPager like RTL(Right to left) support, Vertical support, among many others, Google decided to write and introduce a new View Pager i.e ViewPager2 which offers a lot of new exciting features since at its core lies the Recyclerview, thus bringing all the powerful features of a Recyclerview inside your ViewPager2 now!
So, let me share a simple use case where I saw the powerful usage of ViewPager2. During my development time, I came across a scenario where I need to show animation(Lottie animation) every time the page is visible to the user and stop that animation when the page gets hidden. I had to Implement a simple PagerAdapter as layout remains the same across different pages.
Following were my observation:
- we could not control animation as there is no callback method that tells when a view gets hidden or is visible to the user(Using addOnPageChangeListener also doesn’t solve the issue due to caching)
- If we set the Lottie animation type to LottieDrawable.INFINITE/RESTART, and want to cancel/restart the animation when the view is not hidden/visible, we again flopped!
Now, with ViewPager 2, I had a sigh of relief as I could use the power of Recyclerview which provides method like :
@Override public void onViewAttachedToWindow(@NonNull final WhatsNewPageViewHolder holder) { super.onViewAttachedToWindow(holder); } @Override public void onViewDetachedFromWindow(@NonNull WhatsNewPageViewHolder holder) { super.onViewDetachedFromWindow(holder); }
override fun onViewAttachedToWindow(holder: WelcomeViewHolder) { super.onViewAttachedToWindow(holder) } override fun onViewDetachedFromWindow(holder: WelcomeViewHolder) { super.onViewDetachedFromWindow(holder) }
In order to use ViewPager 2 in your project, you need to add the following dependency in your app level build. gradle file under dependencies node:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" ......... ......... implementation "androidx.viewpager2:viewpager2:1.0.0-beta05" }
We can now use ViewPager 2 in our layout file like this:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:indicator="http://schemas.android.com/apk/res-auto" android:id="@+id/parentContainer" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginBottom="20dp" android:importantForAccessibility="no" indicator:layout_constraintBottom_toTopOf="@+id/pageIndicator" indicator:layout_constraintStart_toStartOf="parent" indicator:layout_constraintTop_toTopOf="parent" /> ................ ................ ................ </androidx.constraintlayout.widget.ConstraintLayout>
In the Adapter class, which is similar to a Recyclerview Adapter class, all that was left was to play the animation when the view becomes visible on the screen or gets hidden with the following methods:
class WelcomeAdapter(@NonNull val welcomeScreenItemResponseList: List<WelcomeScreensItem>) : Adapter<WelcomeViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WelcomeViewHolder { val inflater = LayoutInflater.from(parent.context) return WelcomeViewHolder(inflater, parent) } override fun getItemCount(): Int { return welcomeScreenItemResponseList.size } override fun onBindViewHolder(holder: WelcomeViewHolder, position: Int) { val welcomeScreensItem: WelcomeScreensItem = welcomeScreenItemResponseList[holder.adapterPosition] holder.setAnimation(welcomeScreensItem) } override fun onViewAttachedToWindow(holder: WelcomeViewHolder) { super.onViewAttachedToWindow(holder) holder.showAnimation(true, LottieDrawable.INFINITE) } }
In the above class, all we have done is received the data to be shown on the UI and set animation(Lottie animation from drawable) to the view.
Corresponding ViewHolder class for adapter looks like this:
class WelcomeViewHolder(inflater: LayoutInflater, private val parent: ViewGroup) : RecyclerView.ViewHolder(inflater.inflate(R.layout.layout_whats_news_pages, parent, false)) { private var lottieView: CustomLottieAnimationView = itemView.findViewById(R.id.newPageSnapShot) fun setAnimation(welcomeScreenItem: WelcomeScreensItem) { val res: Resources = parent.context.resources @RawRes val animationID = res.getIdentifier(welcomeScreenItem.imageName, "raw", parent.context.packageName) lottieView.prepareAnimation(animationID) } fun showAnimation(showAnimation: Boolean, animationType: Int) { lottieView.showAnimation(showAnimation, animationType) } }
In this class, we have provided helper methods to set the animation on the UI.
class CustomLottieAnimationView : LottieAnimationView { constructor(context: Context) : super(context) {} constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {} constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {} fun prepareAnimation(animation: Int) { setAnimation(animation) } fun showAnimation(@NonNull playAnimation: Boolean, repeatCount: Int) { cancelAnimation() setRepeatCount(repeatCount) if (playAnimation) { playAnimation() } } }
In the above class, we have extended the LottieAnimationView class to customize the animation as per our needs.
To add the Lottie Animation, you need to import the following library into your app-level build. gradle file:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" ............ implementation "com.airbnb.android:lottie:2.8.0" ............. }
Let’s have a look at the fragment class as well:
class WelcomeFragment : Fragment() { private var mWelcomeModel: WelcomeModel? = null companion object { fun newInstance(bundle: Bundle?): WelcomeFragment { val whatNewFragment = WelcomeFragment() whatNewFragment.arguments = bundle return whatNewFragment } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.layout_what_new, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mWelcomeModel = requireArguments().getParcelable("domainModel") mWelcomeModel?.let { whatNewPages.orientation = ViewPager2.ORIENTATION_HORIZONTAL whatNewPages.adapter = WelcomeAdapter(it.mNewFeatures) } } }
As you can see with ViewPager2, we can use ViewPager Orientation as horizontal or vertical which was not present earlier with the older version of the ViewPager. Not only this, but the new view pager has also provided support for RTL, Right To Left support which has been asked by many developers before.
However, the current version of the ViewPager is in Beta change and may have few more API changes before the final rollout.
You can find the complete source code here. Happy Coding !!
Thanks ……explained in a simple yet effective way….please keep posting great stuff.