Displaying Dynamic Navigation Information in Android Apps

Monday Jun 19th 2017 by Uma Narayanan
Share:

Solve the problem of dynamic navigation on your Android device with Xamarin Forms.

Introduction

When developing applications, we come across requirements, for example, to display a count of new notifications or the number of items in the cart on the toolbar. In this article, we will solve this problem in Xamarin Forms with some elements at the platform level. This article is focuses on Android implementation.

Description

To begin with, let's start with creating a "Multiplatform" Xamarin Forms application named "CustomNotificationToolbar." It creates two projects, as shown in Figure 1:

Creating the forms application
Figure 1: Creating the forms application

Xamarin Form Level Changes

In the "CustomNotificationToolbar" project, add a 'Forms Content Page XAML' file with code behind named 'CustomNotificationToolbarPage.xaml,' as shown in Figure 2:

Adding a 'Forms Content Page Xaml' file
Figure 2: Adding a 'Forms Content Page XAML' file

In the CustomNotificationToolbarPage.Xaml file, add a button to increase the count of the number of notifications displayed on the toolbar.

<Button Command="{Binding AddToolbarItemCommand}"
   Text="Click Me" />

In the code behind file, declare an event that will be defined in the page renderer at the platform level.

1. public event EventHandler<string>
      ToolbarItemChangedEvent;

In the App.Xaml.cs resolve the page model, as shown below:

2. var landingPage = new FreshNavigationContainer(
      FreshPageModelResolver.ResolvePageModel
      <CustomNotificationToolbarPageModel>());

There are few more changes required in the code behind; however, let's first look at the page model implementation. For this example, we are using FreshMVVM. Add the FreshMvvm package to the project. Now, add a new class file named "CustomToolbarIndicatorPageModel.cs;" this will be view model file for the Xaml file created previously. Inherit the class file from 'FreshBasePageModel.' In the page model, we define a Command to handle the click event.

3. public ICommand AddToolbarItemCommand {get; set;}

In the constructor, we define the Command implementation, as in the following code line:

4. public CustomNotificationIndicatorPageModel()
   {
      AddToolbarItemCommand  = new Command(async ()
         => await AddToolbarItemValue());
   }

Define a static variable of type int that stores the count of the number of times the button was tapped. Also, declare an event object that is triggered whenever the count is changed.

5. static int NotifyCount;
6. public event EventHandler<string> ItemValueChangedEvent;
7. public ICommand NotificationClickCommand { get; set; }

The definition of "AddToolbarItemValue" (referred to in Line 4), is as below:

8. private async Task AddToolbarItemValue()
   {
      NotifyCount++;

      if (ItemValueChangedEvent!= null)
         ItemValueChangedEvent.Invoke(this,
            NotifyCount.ToString());
   }

In this method, the static variable defined in Step 5 is incremented by 1 every time user taps the button. In addition, it also invokes the 'ItemValueChangedEvent' defined in the XAML code behind.

Code Behind File Changes

The code in the CustomNotificationToolbarPage.xaml.cs is as follows:

9. protected override async void OnBindingContextChanged()
   {
      base.OnBindingContextChanged();

      var pm = (CustomNotificationToolbarPageModel)
         this.BindingContext;

      if (pm != null)
      {
         pm.ItemValueChangedEvent +=
            Pm_ToolbarItemChangedEvent;

         if (this.ToolbarItems.Count == 0)
         {
            this.ToolbarItems.Clear();

               this.ToolbarItems.Add(new ToolbarItem()
               {
                  Order = ToolbarItemOrder.Default,
                  Command = pm.NotificationClickCommand
               });
         }
      }
   }

In this method, we first retrieve the binding context—the page model. Assign an implementation for the "ItemValueChangedEvent" and add a toolbaritem with a command associated with it. The implementation method for the ItemValueChangedEvent is as below:

10. void Pm_ToolbarItemChangedEvent(object sender,
       string e)
    {
       if (ToolbarItemChangedEvent != null)
          ToolbarItemChangedEvent.Invoke(this, e);
    }

This method, in turn, invokes the ToolbarItemChangedEvent event defined in the platform specific page renderer.

Let's summarize all the actions done so far. We have a page model named 'CustomNotificationToolbarPageModel.cs' that has an event named 'ToolbarItemChangedEvent,' which is implemented in the code behind file 'CustomNotificationToolbarPage.xaml.cs.' The "AddToolbarItemValue" method is defined in the view model; this method increments the count and notifies the code behind page about the change. The changed value is passed as an event argument. The code behind page declares an event named 'ItemValueChangedEvent;' the implementation for this event resides in the PageRenderer—at the platform specific level.

Platform Level Changes

In the CustomNotificationToolbar.Android project navigate to Resources → Layout → Toolbar.axml file. Open the file and add the following:

<android.support.v7.widget.Toolbar xmlns:android=
      "http://schemas.android.com/apk/res/android"
   android:id="@+id/toolbar"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="?attr/colorPrimary"
   android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
   android:popupTheme="@style/ThemeOverlay.AppCompat.Light">
   <RelativeLayout
      android:layout_width="wrap_content"
      android:id="@+id/cartIcon"
      android:gravity="end"
      android:layout_gravity="end"
      android:layout_marginRight="5dp">
      <ImageView
         android:src="@drawable/icon_email"
         android:layout_width="40dp"
         android:layout_height="40dp"
         android:layout_alignParentBottom="true"
         android:id="@+id/notify_image" />
      <TextView
         android:text=""
         android:id="@+id/notify_label"
          android:layout_marginTop="10dp"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textColor="#fff"
          layout_alignParentTop="true"
          android:layout_centerHorizontal="true" />
   </RelativeLayout>
</android.support.v7.widget.Toolbar>

This code creates a toolbar item with an email icon and positions a text view above the icon. The text view displays the number.

Now, add a new class file named 'ToolbarNavigationRenderer.cs' that inherits from NaviagtionPageRenderer.

1. [assembly: ExportRenderer(typeof(NavigationPage),
      typeof(ToolbarNavigationRenderer))]
2. namespace CustomNotificationToolbar.Droid
   {
3.    public class ToolbarNavigationRenderer:
         NavigationPageRenderer
      {
         public ToolbarNavigationRenderer()
         {
         }

4. protected override void OnElementChanged(
      Xamarin.Forms.Platform.Android.ElementChanged
         EventArgs<NavigationPage> e)
      {
         base.OnElementChanged(e);
         Android.Support.V7.Widget.Toolbar toolBar = 
            FindViewById<Android.Support.V7.Widget.Toolbar>
               (Resource.Id.toolbar);
         Android.Widget.RelativeLayout notifyObj =
            (Android.Widget.RelativeLayout)toolBar.FindViewById
               (Resource.Id.msgIcon);
         notifyObj.Clickable = true;
         notifyObj.Click += notifyObj_Click;

         var toolbarPage = (CustomNotificationToolbarPage)
            Element.CurrentPage;
         toolbarPage.ToolbarItemChangedEvent +=
            ToolbarItemChangedEvent_Handler;
      }

5. protected override void OnLayout(bool changed, int l,
      int t, int r, int b)
   {
      base.OnLayout(changed, l, t, r, b);
      var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>
         (Resource.Id.toolbar);
      for (var i = 0; i < toolbar.ChildCount; i++)
      {
         if (toolbar.GetChildAt(i) is
            Android.Support.V7.Widget.ActionMenuView)
         {
            var menuView = (Android.Support.V7.Widget.ActionMenuView)
               toolbar.GetChildAt(i);
            menuView.Visibility = ViewStates.Gone;
         }
      }
   }

6. void notifyObj_Click(object sender, EventArgs e)
   {
      var toobarItems = Element.ToolbarItems;
      var toolbarPage = (CustomNotificationToolbarPage)
         Element.CurrentPage;
      toolbarPage.NavigateTo(sender, e);
   }



7. void ToolbarItemChangedEvent_Handler(object sender, string e)
      {
         Android.Support.V7.Widget.Toolbar toolBar =
            FindViewById<Android.Support.V7.Widget.Toolbar>
               (Resource.Id.toolbar);
         Android.Widget.RelativeLayout layout =
            (Android.Widget.RelativeLayout)toolBar.FindViewById
               (Resource.Id.msgIcon);
         var labelText = (TextView)layout.FindViewById
            (Resource.Id.notify_label);
         labelText.Text = e;
      }
   }
}

Line 1 declares this page as a renderer of type Navigation Page. The class is inherited from NavigationPageRenderer.

Line 4 defines the implementation method for the event 'ToolbarItemChangedEvent,' declared in the code behind page 'CustomNotificationToolbarPage.' It also defines the click handler of the toolbar item to navigate to a different page. Line 5 hides the toolbar item that was created through code. Line 6 defines the method that allows navigation to a different page when the toolbar item is clicked. Line 7 sets the incremented value to the text view defined in the toolbar.axml file.

Now, when the user clicks "Click Me" on the Android application, the incremented value is displayed on the toolbar above the mail icon.

Ready to open the mail client
Figure 3: Ready to open the mail client

Summary

In this article, we saw how to add text over an image, a common scenario to display the number of unread notifications/email or number of items in the cart. It also talks about how to communicate between a page model, code behind, and the renderers.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved