<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TomislavNovacicBF</title>
    <description>The latest articles on DEV Community by TomislavNovacicBF (@tomislavnovacicbf).</description>
    <link>https://dev.to/tomislavnovacicbf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F691756%2Fb48b9e43-0365-42bb-9dfd-9f8052d51cb2.png</url>
      <title>DEV Community: TomislavNovacicBF</title>
      <link>https://dev.to/tomislavnovacicbf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomislavnovacicbf"/>
    <language>en</language>
    <item>
      <title>Top 5 essential skills for fresh parents</title>
      <dc:creator>TomislavNovacicBF</dc:creator>
      <pubDate>Mon, 24 Jan 2022 14:06:58 +0000</pubDate>
      <link>https://dev.to/bornfightcompany/top-5-essential-skills-for-fresh-parents-2jne</link>
      <guid>https://dev.to/bornfightcompany/top-5-essential-skills-for-fresh-parents-2jne</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This article is all about work-life "balance" and how to continue progressing in your career after the baby comes. I will write about my conclusions and organisation in this new environment. We will wrap things up with 5 essential skills you can work on to prepare yourself for the arrival of the newcomer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The comfort zone
&lt;/h2&gt;

&lt;p&gt;Things are going great, you are learning new stuff and reading work-related articles on a daily basis. You are as motivated as ever, advancing really quickly and on your way to become a ninja developer (or other equivalent role). Your diet is on check and you are getting your workouts in. Nothing can stop you! And then one day you become a parent...&lt;/p&gt;

&lt;h2&gt;
  
  
  The chaos
&lt;/h2&gt;

&lt;p&gt;At the beginning things are going good, but as the baby grows you start to realise that your free time becomes shorter and shorter, and soon enough you will have practically no free time. You now don’t have time for your partner or working out, let alone for personal development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do now? How to continue working on your goals and be there for your child at the same time?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;People always talk about work-life balance and you always thought it was some nonsense that lazy people say? Yeah, me too. Well there is some truth in that approach, because now you just have to make things work somehow. Save yourself a few months (or a year) of getting the hang on things and start working on this skills immediately if you are expecting kids any time soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top 5 skills ambitious parent needs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Time management&lt;/strong&gt;&lt;br&gt;
Day only has 24 hours so try to get the best out of every minute. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read whenever you have a few minutes to spare.&lt;/strong&gt; Read while your partner is playing with the baby, when you're in the rest room, in the bed after everyone is asleep and you have few atoms of energy left.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get up early&lt;/strong&gt;. Wake up first and start your day in a complete peace (and use that time wisely).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work from home&lt;/strong&gt;. If you have the option to work from home, just take it - you’ll save a bunch of commute and getting-ready hours, and you can use that in a better way. If that is not a possibility for you, then travel to work with public transportation so you can do some work while you are on your way to the office. If you, for some reason, prefer the car over public transportation then you can listen to audio books or podcasts while driving.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't sacrifice sleep&lt;/strong&gt;, it's counter productive after some time and it's not worth it in the end. Fresh mind = more focus = better productivity and more growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Organization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Try to plan every minute of your day up front&lt;/strong&gt;. This enables you to see how much literature you can consume in a day. According to that you can set realistic goals and don't get frustrated thinking you've done too little that day. When you precisely estimate every action in your day, you unknowingly start to do repetitive tasks even more quickly (eating for example).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Multitasking&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Incorporate learning in any activity that does not require your complete focus&lt;/strong&gt;. Listen to audio books while you are grocery shopping, cleaning, cooking, in the gym or walking a dog. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Focus&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Focus 110% while working&lt;/strong&gt;. That is the only time of the day that is strictly dedicated for your work and personal growth. It’s also the only time when you’ll have complete peace and no distractions. You’ll start to appreciate those hours more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start adjusting to working in non-ideal environments&lt;/strong&gt;. There will be times when you have to work or read and kids are screaming and running around, and you have to get used to it (it gets better over time I promise).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Dedication&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prioritise&lt;/strong&gt;. Not all things you stumble upon on your go-to reading materials are important for your line of work. Read only about stuff that is applicable to your work and future aspirations, skip the rest. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete your social media accounts&lt;/strong&gt; (or leave them but use them responsibly). The same goes for TV and gaming consoles. Time spent on these platforms is valuable and cannot be returned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;P.S. Don't forget to spend quality time with your family, don't be a stranger. This article focuses on the parts of the day you are "free".&lt;/p&gt;

&lt;p&gt;Did I forget anything? Does any of this sound familiar to you? Feel free to react and comment down below.&lt;/p&gt;

</description>
      <category>engineeringmonday</category>
      <category>productivity</category>
      <category>learning</category>
      <category>motivation</category>
    </item>
    <item>
      <title>How to build animated lists
with MotionLayout and
ViewPager2</title>
      <dc:creator>TomislavNovacicBF</dc:creator>
      <pubDate>Mon, 23 Aug 2021 11:36:33 +0000</pubDate>
      <link>https://dev.to/bornfightcompany/how-to-build-animated-lists-with-motionlayout-and-viewpager2-5di</link>
      <guid>https://dev.to/bornfightcompany/how-to-build-animated-lists-with-motionlayout-and-viewpager2-5di</guid>
      <description>&lt;p&gt;Hello! This is my first post on this platform, I hope you’ll like it. Please leave a comment or react to the post to show me support. P.S. Many more posts are on their way!&lt;/p&gt;

&lt;p&gt;We are all seeing beautiful design concepts made by creative designers every day on various websites and blogs, but most of us never had a chance to try to build them. Have you ever wondered what it’s like to build such layouts in practice? Is it super easy with modern frameworks or is it too much hassle? Well the purpose of this blog is to solve that mystery and find out how building one of those complex designs looks like first hand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Animating widgets like ImageViews, AppBars and DrawerLayouts is easy thanks to MotionLayout (which became stable with the release of ConstrainLayout 2.0), but what about list items? There are a bunch of blogs about animating widgets, but very few are about animation list items. For my sample project, I chose our Filmdom app and tried to implement its landing screen. Below, you will find out how it went and what are the pros and cons of building such a layout.&lt;/p&gt;

&lt;p&gt;Final version of the layout is shown in the video below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnr27pscfki22ixt9pimj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnr27pscfki22ixt9pimj.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: It is considered that you know the basics of MotionLayout and ViewPager2, as we will not go into details of how each of these components work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The purpose of this blog is list animation, so we will exclude everything else for the point of brevity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Top picks for you
&lt;/h3&gt;

&lt;p&gt;To showcase recommended movies, I used ViewPager2 which extends RecyclerView. The only attributes from the XML layout worth mentioning are &lt;strong&gt;clipToPadding&lt;/strong&gt; and &lt;strong&gt;clipChildren&lt;/strong&gt;, we need to set both of those attributes to false. Attribute &lt;strong&gt;clipChildren&lt;/strong&gt; determines whether each child view is allowed to draw outside its own bounds within the parent, and &lt;strong&gt;clipToPadding&lt;/strong&gt; attribute determines whether child view is allowed to draw outside of the parent itself. Without these attributes set to false, our ViewPager would clip two upcoming pages and we would only see the currently selected page which is not what we want. In this case, attribute &lt;strong&gt;paddingEnd&lt;/strong&gt; narrows our selected page, so we have space to show two more pages on the screen.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;androidx.viewpager2.widget.ViewPager2&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;”wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:clipChildren=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
    &lt;span class="na"&gt;android:clipToPadding=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
    &lt;span class="na"&gt;android:paddingEnd=&lt;/span&gt;&lt;span class="s"&gt;"120dp"&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
```
Margins are not supported by **ViewPager.LayoutParams**, so I had to wrap my fragment’s layout into an additional **FrameLayout** for our margin to be applied. I couldn’t use padding on my layout because it has a background and if I had applied margin on the **ViewPager** widget directly, then we wouldn’t see the selected page leaving the screen. Applying padding on the **ViewPager** widget is also not an option because we have set our **clipToPadding** and **clipChildren** attributes, and the result would be that the previous page is visible – which is not what we want.

To achieve a faded effect on the unselected cards like in the design, I had to set the background of the inner **FrameLayout** to black. It is important that the background follows the shape of the loaded image so we don’t get excess black area. The reason I didn’t use some different approach to achieve this is that I needed to create a fade-in/fade-out effect, and the only way I found to do that is by changing the opacity of the view. We will go in further detail about this in the next section. Another thing I had to do to get three pages on the screen at the same time was to use the **setOffscreenPageLimit** method and set the limit value to “3” on our **ViewPager**.

```xml
&lt;span class="nt"&gt;&amp;lt;FrameLayout&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;FrameLayout&lt;/span&gt;
        &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
        &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
        &lt;span class="na"&gt;android:layout_marginStart=&lt;/span&gt;&lt;span class="s"&gt;"24dp"&lt;/span&gt;
        &lt;span class="na"&gt;android:background=&lt;/span&gt;&lt;span class="s"&gt;"@drawable/bg_rounded_circle_drawable"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;ImageView&lt;/span&gt;
               &lt;span class="err"&gt;…&lt;/span&gt;
         &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/FrameLayout&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/FrameLayout&amp;gt;&lt;/span&gt;
```

For the purpose of scaling and fade-in/fade-out animations, I implemented **PageTransformer** on our **ViewPager**. In the overridden **transformPage** method, we first have to set the elevation on each card depending on its position. Line **ViewCompat.setElevation(page, -abs(position))** sets the highest elevation on the first card in our stack of three and a proportionally smaller elevation on each consecutive card. This gives us the desired visual effect of cards coming towards us. Next thing we have to do is calculate our scale factor, so we can apply the correct values on our movie cards.

Our **when** function is scaling our cards if they are visible on the screen, otherwise they have default values. The transition on the X axis is proportional to the scale factor, so we can have smooth animations. The conditions afterwards determine the visibility of movie posters inside our card. Selected card in the center position has visibility at 100% and its visibility drops as it moves away from the center of the **ViewPager**. We can change the card’s movie poster visibility through **setAlpha** method. This **PageTransformer** setup requires a little bit of trial and error to achieve the desired result.

```kotlin
class SliderTransformer(private val offscreenPageLimit: Int) : ViewPager2.PageTransformer {

    companion object {
        private const val DEFAULT_TRANSLATION_X = .0f
        private const val DEFAULT_TRANSLATION_FACTOR = 1.46f
        private const val SCALE_FACTOR = .14f
        private const val DEFAULT_SCALE = 1f
    }

    override fun transformPage(page: View, position: Float) {

        page.apply {
            ViewCompat.setElevation(page, -abs(position))
            val scaleFactor = -SCALE_FACTOR * position + DEFAULT_SCALE
            when (position)  {
                    in 0f..offscreenPageLimit - 1f -&amp;gt; {
                         scaleX = scaleFactor
                         scaleY = scaleFactor
                         translationX = -(width / DEFAULT_TRANSLATION_FACTOR) * position
                    }
                    else -&amp;gt; {
                         translationX = DEFAULT_TRANSLATION_X
                         scaleX = DEFAULT_SCALE
                         scaleY = DEFAULT_SCALE
                   }
            }

            val recommendedMovieIV: ImageView = findViewById(R.id.recommendedMovieIV)
            if (position &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;= -1.0f || position &amp;gt;= 1.0f) {
                recommendedMovieIV.alpha = 0.5f
            } else if (position == 0.5f) {
                recommendedMovieIV.alpha = 1.0f
            } else if (position &lt;span class="err"&gt;&amp;lt;&lt;/span&gt; 0.5f) {
                recommendedMovieIV.alpha = 1.0f - abs(position)
            }
        }
    }
}
```

We can conclude that MotionLayout is not designed to work with a single ViewPager2 page. For animating pages, we have to use **PageTransformer** and if we really want to use MotionLayout on a single page we can do so by putting its implementation logic inside the transformPage method. The issue with that approach is that we then have to find a way to track MotionLayout’s progress from the value of the position argument and that’s not an easy job to do. There is a possibility to animate all items in the **ViewPager2/RecyclerView** pretty easily, but that is not what we are looking for here.

###Coming soon

For the upcoming movie list, I also used **ViewPager2**. Our page’s layout is wrapped inside MotionLayout as we use it here to achieve the tilting animation on swipe gestures. Each swipe animates the whole ViewPager and all of its pages.

```xml
&lt;span class="nt"&gt;&amp;lt;androidx.constraintlayout.motion.widget.MotionLayout&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;
    &lt;span class="na"&gt;xmlns:app=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res-auto"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:minHeight=&lt;/span&gt;&lt;span class="s"&gt;"240dp"&lt;/span&gt;
    &lt;span class="na"&gt;app:layoutDescription=&lt;/span&gt;&lt;span class="s"&gt;"@xml/tilt_scene"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;LinearLayout&lt;/span&gt;
            &lt;span class="err"&gt;…&lt;/span&gt;
     &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/androidx.constraintlayout.motion.widget.MotionLayout&amp;gt;&lt;/span&gt;
```
MotionScene below is self-explanatory, so I won’t be getting into details about it. Pages are tilted to the right or to the left depending on the gesture direction.

```xml
&lt;span class="nt"&gt;&amp;lt;MotionScene&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;
    &lt;span class="na"&gt;xmlns:app=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Transition&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/rightToLeft"&lt;/span&gt;
        &lt;span class="na"&gt;app:constraintSetEnd=&lt;/span&gt;&lt;span class="s"&gt;"@id/end"&lt;/span&gt;
        &lt;span class="na"&gt;app:constraintSetStart=&lt;/span&gt;&lt;span class="s"&gt;"@id/start"&lt;/span&gt;
        &lt;span class="na"&gt;app:duration=&lt;/span&gt;&lt;span class="s"&gt;"1000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;OnSwipe&lt;/span&gt;
            &lt;span class="na"&gt;app:dragDirection=&lt;/span&gt;&lt;span class="s"&gt;"dragLeft"&lt;/span&gt;
            &lt;span class="na"&gt;app:touchAnchorId=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;KeyFrameSet&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"-15"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"25"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"-30"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"-15"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"75"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/KeyFrameSet&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Transition&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Transition&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/leftToRight"&lt;/span&gt;
        &lt;span class="na"&gt;app:constraintSetEnd=&lt;/span&gt;&lt;span class="s"&gt;"@id/end"&lt;/span&gt;
        &lt;span class="na"&gt;app:constraintSetStart=&lt;/span&gt;&lt;span class="s"&gt;"@id/start"&lt;/span&gt;
        &lt;span class="na"&gt;app:duration=&lt;/span&gt;&lt;span class="s"&gt;"1000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;OnSwipe&lt;/span&gt;
            &lt;span class="na"&gt;app:dragDirection=&lt;/span&gt;&lt;span class="s"&gt;"dragRight"&lt;/span&gt;
            &lt;span class="na"&gt;app:touchAnchorId=&lt;/span&gt;&lt;span class="s"&gt;"@+id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;KeyFrameSet&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"25"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"75"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;KeyAttribute&lt;/span&gt;
                &lt;span class="na"&gt;android:rotationY=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
                &lt;span class="na"&gt;app:framePosition=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;
                &lt;span class="na"&gt;app:motionTarget=&lt;/span&gt;&lt;span class="s"&gt;"@id/motionContainer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/KeyFrameSet&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Transition&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ConstraintSet&lt;/span&gt; &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/start"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ConstraintSet&lt;/span&gt; &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/end"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/MotionScene&amp;gt;&lt;/span&gt;
```
To show 5 pages on the screen at the same time, I again had to use the **setOffscreenPageLimit** method on our **ViewPager** to set the limit to “3”. I applied a custom **OnPageChangeCallback** on our **ViewPager**. This callback is determining the direction of the gesture by comparing the current offset to the previous one. With this information, we can calculate the **realCurrentPosition**, **nextPosition** and **realOffset**.

With the **realCurrentPosition** and **nextPosition** properties, we are fetching the first two visible pages from ViewPager. On swipe left, we are scaling down the currently selected page and scaling up the following page. For the swipe to the right, it’s vice-versa. This callback is also responsible for determining the strength of the gesture, so that we can tilt the pages accordingly.

```kotlin
class UpcomingMovieChangedCallback(private val binding: ActivityMainBinding, private val upcomingMoviesAdapter: GenericMoviesAdapter) : ViewPager2.OnPageChangeCallback() {

    var goingLeft: Boolean by Delegates.notNull()
    private var lastOffset = 0f
    var progress: Float by Delegates.notNull()

    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        val realCurrentPosition: Int
        val nextPosition: Int
        val realOffset: Float
        goingLeft = lastOffset &amp;gt; positionOffset
        if (goingLeft) {
            realCurrentPosition = position + 1
            nextPosition = position
            realOffset = 1 - positionOffset
        } else {
            nextPosition = position + 1
            realCurrentPosition = position
            realOffset = positionOffset
        }

        val currentCard = (binding.upcomingMoviesVP[0] as RecyclerView).layoutManager?.findViewByPosition(realCurrentPosition)
        currentCard?.scaleX = (1 + 0.4 * (1 - realOffset)).toFloat()
        currentCard?.scaleY = (1 + 0.4 * (1 - realOffset)).toFloat()
        currentCard?.pivotY = 0f

        val nextCard = (binding.upcomingMoviesVP[0] as RecyclerView).layoutManager?.findViewByPosition(nextPosition)
        nextCard?.scaleX = (1 + 0.4 * realOffset).toFloat()
        nextCard?.scaleY = (1 + 0.4 * realOffset).toFloat()
        nextCard?.pivotY = 0f

        lastOffset = positionOffset
        progress = when (position) {
            position -&amp;gt; positionOffset
            position + 1 -&amp;gt; 1 - positionOffset
            position - 1 -&amp;gt; 1 - positionOffset
            else -&amp;gt; 0f
        }
    }
}
```
**PageTransformer** for upcoming movies is applying translation on the X axis that is equivalent to the page position in the list and negative value of summed page margins. We are using values calculated in our custom **OnPageChangeCallback** to determine correct transition animation and progress of the transition on our **ViewPager**. There are also a bunch of page decorations that are not mentioned here, but you can check them out in a repository that is linked at the bottom.

```kotlin
val nextItemVisiblePx = resources.getDimension(R.dimen.viewpager_next_item_visible)  //50dp
val currentItemHorizontalMarginPx = resources.getDimension(R.dimen.viewpager_current_item_horizontal_margin_right)  //230dp
val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
val pageTransformer = PageTransformer { page: View, position: Float -&amp;gt;
    page.translationX = -pageTranslationX * position

    if (upcomingMovieChangedCallback.goingLeft) {
        ((page as ViewGroup).getChildAt(0) as MotionLayout).setTransition(R.id.leftToRight)
    } else {
        ((page as ViewGroup).getChildAt(0) as MotionLayout).setTransition(R.id.rightToLeft)
    }
    (page.getChildAt(0) as MotionLayout).progress = upcomingMovieChangedCallback.progress
}
binding.upcomingMoviesVP.setPageTransformer(pageTransformer)
```

###Conclusion

Building complex layouts that include animations is still not an easy job to do – even with **MotionLayout** and **PageTransformer** classes. Despite the fact that a bunch of popular apps from years ago have designs with multiple pages on the screen with only one page highlighted, there still isn’t any standardised way of dealing with this issue. This is an easy job to do if we have three pages on the screen, but if we have five or more pages visible on the screen, you will get a massive headache before getting things to behave as you desire (no matter what widget you use for displaying a list of items).

**MotionLayout** and **ViewPager2** are both great tools, but they are not meant to resolve the above mentioned issue. I think we should have a library dedicated to this kind of list presentation – there is no sense in writing a bunch of complex calculations just to get a commonly wanted behaviour.

###Looking to learn more?

You can find this entire project on [GitLab](https://gitlab.com/bornfight-mobile/android-public/cinema), or you can hop over to our projects page to check out the full [Filmdom case study](https://www.bornfight.com/work/filmdom/).

If you know an easier way of solving this issue, or have some other complex layouts in mind that you would like to see come to life, feel free to leave a comment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>engineeringmonday</category>
      <category>android</category>
      <category>design</category>
      <category>animation</category>
    </item>
  </channel>
</rss>
