Kotlin Parallax Tabs (KPT)


KPT:   SOLD       




Hi dev, this time I want to discuss about parallax implementation for tabview header. What do you think is parallax? In my opinion, parallax is the art of transition between two different pages using animation.

Actually, this parallax technique has long been popular on the web platform, but recently many Android developers have been inspired by it and want to apply it to their mobile applications, so there's nothing wrong if I share an alternative solution without using third-party libraries.

You will soon find out that all of these needs are  TabLayout. But hold your horses! I just entered  TabLayout and this is what happened.

Unlike the usual TabView Material Design, here the Tab is transparent. So clearly, the plug and play solution does not work in this case.

It looks like it needs some tweaking. So before I paste the entire code, let's take a look at the layout framework.

<android.support.design.widget.CoordinatorLayout> 
   <android.support.design.widget.AppBarLayout>
      <android.support.design.widget.CollapsingToolbarLayout>
         <ImageView />
         <android.support.v7.widget.Toolbar />
         <android.support.design.widget.TabLayout />
      </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

CollapsingToolbarLayout  is like  FrameLayout. So pay attention to the order of the elements inside it. Whichever element you put last, is the one that appears on top. This positioning is important for parallax scrolling to work.

Also, it is the ImageView that enables parallax scrolling. We can tell it  ImageView to have parallax scrolling with a single attribute. We will discuss this further later.

So now that you have an idea of ​​the layout structure, take a look at this XML layout code.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/htab_maincontent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/htab_appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/htab_collapse_toolbar"
            android:layout_width="match_parent"
            android:layout_height="256dp"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
            app:titleEnabled="false">

            <ImageView
                android:id="@+id/htab_header"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/header"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax"/>

            <View
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:alpha="0.3"
                android:background="@android:color/black"
                android:fitsSystemWindows="true"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/htab_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:layout_gravity="top"
                android:layout_marginBottom="48dp"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

            <android.support.design.widget.TabLayout
                android:id="@+id/htab_tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                app:tabIndicatorColor="@android:color/white"
                app:tabSelectedTextColor="@android:color/white"
                app:tabTextColor="@color/white_70"/>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/htab_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</android.support.design.widget.CoordinatorLayout>

I know the code is very long. But it takes double the effort to try the parallax effect using Java. So, if you have recovered from the shock, let's see what happens in the code above.

Explanation

These are some of the attributes that make parallax scrolling work. Let's discuss them one by one.

  • 256dp is a good number for a collapsible header view.
  • This is not a Flexible Space with Image pattern. We don't want the Toolbar title to collapse. We want it fixed at the top. So we disable the title  collapsible.
  • Flag that triggers the parallax effect when scrolling the page.
  • Scrim that makes Tab text easier to read with a solid title background.
  • Remember that  CollapsingToolbarLayout is an extension of  FrameLayout. This attribute ensures that  Toolbar it stays on top.
  • TabLayout defaults to a height of 48dp. Telling our Toolbar to have the same bottom margin, avoiding the overlapping issues we saw above.
  • Make sure  Toolbar it's consistently pinned to the top. Otherwise, when you start scrolling,  Toolbar it will scroll off-screen.
  • Make sure  TabLayout it sticks to the bottom of the header.

If you are still confused by the explanation points above, hopefully the visualization below can help provide a little enlightenment.

Setup ViewPager

In case you forgot, Tab requires  ViewPager it to be used, so let's handle that in Activity.java.

Start by creating a dummy  Fragment to display some content,  Fragment just display it to start with  RecyclerView.

Additionally, the background color  Fragment will change depending on its position in  ViewPager.

public static class DummyFragment extends Fragment {
int color;

        public DummyFragment() {
        }

        @SuppressLint("ValidFragment")
        public DummyFragment(int color) {
            this.color = color;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.dummy_fragment, container, false);

            final FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.dummyfrag_bg);
            frameLayout.setBackgroundColor(color);

            RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.dummyfrag_scrollableview);

            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity().getBaseContext());
            recyclerView.setLayoutManager(linearLayoutManager);
            recyclerView.setHasFixedSize(true);

            DessertAdapter adapter = new DessertAdapter(getContext());
            recyclerView.setAdapter(adapter);

            return view;
        }
    }

Note that  DummyFragment this is just a placeholder that I used with different background colors for each of my Tabs.  DessertAdapter is  RecyclerView.Adapter what I wrote to show scrollable content that can demonstrate the parallax effect.

On the other hand, you have to specify  differentFragment the appropriate one, for each of your Tabs.

ViewPager Adapter

Minimalist adapter for  ViewPager us (holds 3 Fragments).

private static class ViewPagerAdapter extends FragmentPagerAdapter {
        private final List<Fragment> mmFragmentTitleList FragmentList = new ArrayList<>();
        private final List<String> = new ArrayList<>();

        public ViewPagerAdapter(FragmentManager manager) {
            super(manager);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }

        @Override
        public int getCount() {
            return mFragmentList.size();
        }

        public void addFrag(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentTitleList.get(position);
        }
    }

Connecting ViewPager with TabLayout

Finally we will attach  ViewPager to  TabLayout the in method  onCreate(). We use it  OnTabSelectedListener to handle the Fragment redirection on click.

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());                         
                switch (tab.getPosition()) {
                    case 0:
                        // TODO
                    break;
                }
            }
            @Override
            public void onTabUnselected(TabLayout.Tab tab) { }
            @Override
            public void onTabReselected(TabLayout.Tab tab) {}
});

Don't forget to fill the Adapter  ViewPager with  Fragment you. I added 3 Fragments to the Adapter, each with its own background color.

private void setupViewPager(ViewPager viewPager) {         
    
    ViewPagerAdapter adapter = new ViewPagerAdapter(
                               getSupportFragmentManager());        
    adapter.addFrag(new DummyFragment(                
        ContextCompat.getColor(this, R.color.cyan_50)), "Cyan");        
    adapter.addFrag(new DummyFragment(                
        ContextCompat.getColor(this, R.color.amber_50)), "Amber");        
    adapter.addFrag(new DummyFragment(                
        ContextCompat.getColor(this, R.color.purple_50)), "Purple");        
    viewPager.setAdapter(adapter);    
}

The method  setupViewPager() simply initializes it  Fragment and adds it to  ViewPager.

Dynamic Tab Colors with Palette API

The colors you see are taken by  Toolbar +  TabLayout, taken from the header image. Courtesy of the Palette API. It is dynamic and the colors vary depending on the image.

By using the Palette API, it is easier for us to set the colors  Toolbar,  TabLayout, and  StatusBar.

try {
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.header);
    Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
        @SuppressWarnings("ResourceType")
        @Override
        public void onGenerated(Palette palette) {

            int vibrantColor = palette.getVibrantColor(R.color.primary_500);
            int vibrantDarkColor = palette.getDarkVibrantColor(R.color.primary_700);
            collapsingToolbarLayout.setContentScrimColor(vibrantColor);
            collapsingToolbarLayout.setStatusBarScrimColor(vibrantDarkColor);
        }
    });

} catch (Exception e) {
    // if Bitmap fetch fails, fallback to primary colors
    Log.e(TAG, "onCreate: failed to create bitmap from background", e.fillInStackTrace());
    collapsingToolbarLayout.setContentScrimColor(
            ContextCompat.getColor(this, R.color.primary_500)
    );
    collapsingToolbarLayout.setStatusBarScrimColor(
            ContextCompat.getColor(this, R.color.primary_700)
    );
}

Note that calling the Palette API is actually done in a block  try-catch. This is one of the best practices for handling errors. Here, the Palette API could fail if we couldn’t get a Bitmap. Instead, we use our application’s primary color.

Final Output

With everything in place, run your app by scrolling through it and see how stunning the parallax header effect is.


Post a Comment

Previous Next

نموذج الاتصال