Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Tab layout in MVVM architecture with the data binding library

I am developing an app that has a tab layout as the image.

enter image description here

I’d like to use MVVM architecture with data binding library but I am new with this framework.

I can do this without using MVVM by normally setup tab layout using ViewPager as this sample.

Normal tab layout without MVVM and data binding:

activity_main.xml:

    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabGravity="fill"/>
    </android.support.design.widget.AppBarLayout>

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

MainActivity.java:

    public class MainActivity extends AppCompatActivity {

    private Toolbar toolbar;
    private TabLayout tabLayout;
    private ViewPager viewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        setupViewPager(viewPager);

        tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(viewPager);
    }

    private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        adapter.addFragment(new OneFragment(), "ONE");
        adapter.addFragment(new TwoFragment(), "TWO");
        adapter.addFragment(new ThreeFragment(), "THREE");
        viewPager.setAdapter(adapter);
    }

    class ViewPagerAdapter extends FragmentPagerAdapter {
        private final List<Fragment> mFragmentList = new ArrayList<>();
        private final List<String> mFragmentTitleList = 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 addFragment(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }

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

Tab layout in MVVM:

When using MVVM with data binding library, we will have to use a view model for the tab layout view.

And I don’t know how to setup tab layout in the XML and also in the view model. How to handle events such as “tap on one tab of the layout” using data binding library

Is there any sample of using Tab layout in MVVM with data binding library?

Thanks for your help.

like image 452
Jimy25 Avatar asked Jan 18 '17 11:01

Jimy25


People also ask

What is data binding in MVVM?

Data binding in Android (and many platforms as well) allow you to add objects in your XML layout and bind them together. It simplifies user interface management as changes in the underlying object is automatically reflected to views that are bound to it.

How do I convert layout to data binding layout?

To convert your XML layouts into the Data Binding layout, follow the below steps: Declare a <layout> tag, which will wrap your existing layout file at the root level. Declare variables under the <data> tag, which will go under the <layout> tag. Declare necessary expressions to bind data inside the view elements.


2 Answers

MainActivity -

    public class MainActivity extends Activity 
    {
        @Override
        protected void onCreate(@Nullable final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            App.get(this).component().inject(this);
            ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.setHandler(this);
            binding.setManager(getSupportFragmentManager());
        }
        
        @BindingAdapter({"bind:handler"})
        public static void bindViewPagerAdapter(final ViewPager view, final MainActivity activity)
        {
            final MainActionsAdapter adapter = new MainActionsAdapter(view.getContext(), activity.getSupportFragmentManager());
            view.setAdapter(adapter);
        }
        
        @BindingAdapter({"bind:pager"})
        public static void bindViewPagerTabs(final TabLayout view, final ViewPager pagerView)
        {
            view.setupWithViewPager(pagerView, true);
        }
    
    }

xml -

    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:fresco="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">
    
        <data>
    
            <import type="android.view.View" />
    
            <variable
                name="handler"
                type="com.ui.main.MainActivity" />
    
            <variable
                name="manager"
                type="android.support.v4.app.FragmentManager" />
        </data>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true"
                app:title="@string/app_name"
                app:titleMarginStart="8dp" />
    
            <android.support.design.widget.TabLayout
                android:id="@+id/tab_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:pager="@{(pager)}">
            </android.support.design.widget.TabLayout>
    
            <android.support.v4.view.ViewPager
                android:id="@+id/pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:handler="@{handler}" />
    
        </LinearLayout>
    
    </layout>

Adapter -

    public class MainSectionsAdapter extends FragmentPagerAdapter
    {
        private static final int CONTACTS = 0;
        private static final int CALLS = 1;
        private static final int CHATS = 2;
        
        private static final int[] TABS = new int[]{CONTACTS, CALLS, CHATS};
        
        private Context mContext;
        
        public MainSectionsAdapter(final Context context, final FragmentManager fm)
        {
            super(fm);
            mContext = context.getApplicationContext();
        }
        
        @Override
        public Fragment getItem(int position)
        {
            switch (TABS[position])
            {
                case CONTACTS:
                    return ContactsFragment.newInstance();
                case CALLS:
                    return CallsFragment.newInstance();
                case CHATS:
                    return ChatsFragment.newInstance();
            }
            return null;
        }
        
        @Override
        public int getCount()
        {
            return TABS.length;
        }
        
        @Override
        public CharSequence getPageTitle(int position)
        {
            switch (TABS[position])
            {
                case CONTACTS:
                    return mContext.getResources().getString(R.string.contacts);
                case CALLS:
                    return mContext.getResources().getString(R.string.calls);
                case CHATS:
                    return mContext.getResources().getString(R.string.chats);
            }
            return null;
        }
    }
like image 67
Dmitrijs Avatar answered Oct 02 '22 06:10

Dmitrijs


I'm not sure if this is newly introduced recently but with Android Support version 27.1.1, you don't even even need a custom data binding adapter, you can simply use:

<android.support.design.widget.TabLayout
                android:id="@+id/tl_1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:setupWithViewPager="@{some_fragment_viewpager}"
                app:tabSelectedTextColor="@android:color/white"
                app:tabTextColor="@color/v5_grey_55"
                />


        <android.support.v4.view.ViewPager
            android:id="@+id/some_fragment_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:addOnPageChangeListener="@{vm.pageChangeListener}"
            app:setAdapter="@{vm.pageAdapter}"
            app:setCurrentItem="@{vm.pageChangeListener.currentPosition}"
            />

Take note that the viewPager variable in app:setupWithViewPager="@{some_fragment_viewpager}" points to android:id="@+id/some_fragment_viewpager". That's how the reference to the ViewPager is done (like magic I know)!

ViewModel

public class SomeViewModel {
  public ViewPager.OnPageChangeListener pageChangeListener;
  public SomeFragmentPagerAdapter pagerAdapter;
  // ...
}

FragmentPagerAdapter

public classs SomeFragmentPagerAdapter extends FragmentPagerAdapter {
  public Boolean currentPosition;
}
like image 26
ericn Avatar answered Oct 02 '22 05:10

ericn