Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Translate Floating Action Button on ViewPager scroll

I have a screen with 3 tabs, a ViewPager below with 3 Fragments and a Floating Action Button in bottom-right corner.

When 1st tab is current tab then I want to show the FAB in bottom-right corner. When 2nd tab is current tab then I want to show the FAB in bottom-centre. When 3rd tab is current tab then I want to hide the FAB.

The translate animation from bottom-right to bottom-centre or vice versa should happen as the view pager scrolls i.e. on the basis of scroll amount.

Below is what I have tried.

mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            ViewCompat.animate(mFAB)
                    .translationX(-translate)
                    .withLayer()
                    .setDuration(0)
                    .setInterpolator(new LinearInterpolator())
                    .start();
        }

        @Override
        public void onPageSelected(int position) {
            mTabLayout.getTabAt(position).select();
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

Is this the best way to do this? If yes what should be the value of translate variable?

Please help me in implementing this.

For reference you can checkout Android Lollipop Call application. The FAB to show the dialer animates in similar manner. For 1st tab its in bottom-right then for middle tab it comes in the centre. This is exactly what I want.

like image 279
Nitesh Kumar Avatar asked Aug 24 '15 18:08

Nitesh Kumar


1 Answers

The easiest way to do that would be for example like this:

mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        final int width = mViewPager.getWidth();
        if (position == 0) { // represents transition from page 0 to page 1 (horizontal shift)
            int translationX = (int) ((-(width - mFab.getWidth()) / 2f) * positionOffset);
            mFab.setTranslationX(translationX);
            mFab.setTranslationY(0);
        } else if (position == 1) { // represents transition from page 1 to page 2 (vertical shift)
            int translationY = (int) (mFab.getHeight() * positionOffset);
            mFab.setTranslationY(translationY);
            mFab.setTranslationX(-(width - mFab.getWidth()) / 2f);
        }
    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
});

Bare in mind that width parameter should represent the width of the screen (I assumed that the ViewPager fills the width of the screen (so I assigned width = mViewPager.getWidth()).

You also didnt exactly say in what way you want your FAB to be hidden. So I just translated it vertically. You could also use setScaleX(...) and setScaleY(...).

Edit:

The (step) value of the changed parameter should be calculated using the following method (Let's call it - for future references - formula 1):

// formula 1
value = targetValue - ((targetValue - initialValue) * (1.0f - offset));

where offset represents a parameter that changes (within range [0f; 1f]). So in your case offset is positionOffset argument of the onPageScrolled method.

Let's analyze the transition between pages 0 and 1 (of your case; position==0):

  1. You want to move your FAB horizontally, so you will be modifying the translationX paramenter.
  2. FAB is anchored at the bottom-right corner (e.g. with layout_gravity="bottom|right")
  3. The initial position of the FAB is the proper position of your animation for NOT scrolled page 0. Therefore the the initialValue of the translationX for your animation is 0

initialValue = 0;

  1. You want to move your FAB to the center of the screen. Given the screen width as a variable width you can calculate the proper targetValue for the FAB shift to the center of the screen. It's equal to half of the screen width subtracted by half of the FAB width. And since you're starting from the right corner, this value be negative (``)

targetValue = -(width - mFab.getWidth()) / 2f;

  1. Due to the fact that the initialValue parameter is 0, you can simplify the formula 1 to (will be referenced as formula 2):

// formula 2
value = targetValue * offset;

  1. Taking the calculated targetValue you end up with this (formula 2 with calculated targetValue and positionOffset instead of offset):

value = (-(width - mFab.getWidth()) / 2f) * positionOffset;

Which is exactly what I used to move FAB from the initial position (bottom|right) to the center of the screen (horizontally). I went through a similar process in calculating the desired step value for the translationY in the transition between page 1 and 2.

What you need to do is do the same thing for any future steps. Think about initialValue and targetValue of the animated parameters, insert calculated values into the formula 1 and see if it works as desired.

like image 124
Bartek Lipinski Avatar answered Oct 17 '22 21:10

Bartek Lipinski