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 🙂 )