I am developing an app that has a tab layout as the image.
I’d like to use MVVM architecture with data binding library but I am new with this framework.
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;
}
}
Here is my solution for setUpWithViewpager
using databinding
:
public class BindingUtil
{
@BindingAdapter({ "setUpWithViewpager" })
public static void setUpWithViewpager(final TabLayout tabLayout, ViewPager viewPager)
{
viewPager.addOnAdapterChangeListener(new ViewPager.OnAdapterChangeListener()
{
@Override
public void onAdapterChanged(@NonNull ViewPager viewPager, @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter)
{
if (oldAdapter == null && (newAdapter == null || newAdapter.getCount() == 0))
{
// this function will helpful when
// we don't create viewpager immediately
// when view created (this mean we create
// will pager after a period time)
return;
}
tabLayout.setupWithViewPager(viewPager);
}
});
}
}
xml
<android.support.design.widget.TabLayout
...
app:setUpWithViewpager="@{ viewPager }"
/>
<android.support.v4.view.ViewPager
...
android:id="@+id/viewPager"
app:adapter="@{viewModel.pagerAdapter}"
/>
ViewModel
public class MainViewModel extends BaseObservable
{
@Bindable
public PagerAdapter getPagerAdapter()
{
return adapter;
}
private void createViewPagerAdapter()
{
...
notifyPropertyChanged(BR.pagerAdapter);
}
}
full demo project here
Hope it help
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;
}