Xamarin.Android – NotifyMe Part II

Hello. This is a second part about my second project. Today with a lot of code!
If you don’t know what is going on previous post is here.
As you know I completed application for Get Noticed 2017 contest, but I want to rewrite it again in native approach.
If you are interested about it, repository is here: https://github.com/bradlak/NotifyMe-native

To meet good development practises I should use MVVM pattern so I decided to use MvvmCross which is quite big and recommended framework.

I achieved tabs in main view by using Android TabLayout with ViewPager filled with fragments.

Here is my MainView:

<?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/main_content" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent">
    <android.support.design.widget.AppBarLayout 
     android:id="@+id/appbar" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <include layout="@layout/toolbar" />
        <android.support.design.widget.TabLayout 
          android:id="@+id/tabs" 
          android:layout_width="match_parent" 
          android:layout_height="80dp" 
          android:layout_gravity="center_horizontal" 
          app:tabMode="fixed" 
          app:tabBackground="@color/lighterBackground" 
          app:tabTextColor="@color/white" 
          app:tabIndicatorHeight="2dp" 
          app:tabIndicatorColor="@color/white" 
          app:tabGravity="fill" 
          android:elevation="0dp" />
    </android.support.design.widget.AppBarLayout>
    <android.support.v4.view.ViewPager 
     android:id="@+id/viewpager" 
     android:layout_width="match_parent" 
     android:layout_height="fill_parent" 
     app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

with one of fragments:

    [Register("notifyMe.droid.views.HistoryFragment")]
    public class HistoryFragment : MvxFragment<HistoryViewModel>
    {
        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            var ignored = base.OnCreateView(inflater, container, savedInstanceState);
            this.EnsureBindingContextIsSet(savedInstanceState);
            return this.BindingInflate(Resource.Layout.HistoryFragmentView, container, false);
        }
    }

and code for Activity which sticks it together:

    [Activity]
    public class MainView : BaseAppCompatActivity<MainViewModel>
    {
        public MainView() : base(Resource.Layout.MainView)
        {
        }

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SupportActionBar.SetTitle(Resource.String.Logout);

            var viewPager = FindViewById<ViewPager>(Resource.Id.viewpager);
            if (viewPager != null)
            {
                var fragments = new List<FragmentInfo>
                {
                    new FragmentInfo() {ViewModel = ViewModel.FriendsViewModel, Fragment = new FriendsFragment()},
                    new FragmentInfo() {ViewModel = ViewModel.HistoryViewModel, Fragment = new HistoryFragment()}
                };

                viewPager.Adapter = new MvxFragmenPagerAdapter(SupportFragmentManager, fragments);
            }

            var tabLayout = FindViewById<TabLayout>(Resource.Id.tabs);
            tabLayout.SetupWithViewPager(viewPager);

            for (int i = 0; i < tabLayout.TabCount; i++)
            {
                var view = PrepareCustomView(i);
                var tab = tabLayout.GetTabAt(i).SetCustomView(view);
            }
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            var result = base.OnOptionsItemSelected(item);
            if (ViewModel != null)
            {
                if (item.ItemId == Resource.Id.home || item.ItemId == Android.Resource.Id.Home)
                {
                    ViewModel.ExitCommand.Execute(null);
                    result = true;
                }
            }

            return result;
        }

        private View PrepareCustomView(int i)
        {
            var view = LayoutInflater.Inflate(Resource.Layout.tabView, null);

            var image = view.FindViewById<ImageView>(Resource.Id.tabIcon);
            var text = view.FindViewById<TextView>(Resource.Id.tabText);
            if (i == 0)
            {
                image.SetImageDrawable(Resources.GetDrawable(Resource.Drawable.people));
                text.Text = "FRIENDS";
            }
            else
            {
                image.SetImageDrawable(Resources.GetDrawable(Resource.Drawable.history));
                text.Text = "HISTORY";
            }

            view.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent,
                                                               ViewGroup.LayoutParams.MatchParent);

            return view;
        }
    }

Tabs have their own custom view presented below:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
 xmlns:android="http://schemas.android.com/apk/res/android" 
 android:background="@drawable/tabRipple" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent">
    <ImageView 
     android:src="@android:drawable/ic_menu_gallery" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:id="@+id/tabIcon" 
     android:paddingTop="10dp" 
     android:paddingRight="5dp" 
     android:paddingBottom="5dp" 
     android:paddingLeft="5dp" />
    <TextView 
     android:text="Text" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:id="@+id/tabText" 
     android:padding="5dp" 
     android:gravity="center" 
     android:textColor="@color/gray" 
     android:textSize="15dp" 
     android:layout_below="@+id/tabIcon" />
</RelativeLayout>

But this is not enough to get the same behaviour like in Xamarin.Forms app. I also had to add ripple effect to each tab.

<?xml version="1.0" encoding="UTF-8" ?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/ripple2">
	 <item>
    <selector>
        <item android:state_pressed="true">
            <color android:color="@color/ripple" />
        </item>
        <item>
            <color android:color="@color/lighterBackground" />
        </item>
    </selector>
    </item>
</ripple

Both tab views look same now.

Xamarin.Android:

Xamarin.Forms for Android:

Every fragment has his own ViewModel so I met the conditions of MVVM pattern.

May the Xamarin be with you! ( New gift from my fiancee 🙂 )

Leave a Reply

Your email address will not be published. Required fields are marked *