Xamarin.Tip – BottomNavigationView in Xamarin.Android

I previously talked about adding a BottomNavigationView to your native Android apps using Java (Android.Basics – Adding a Bottom Navigation View), but I couldn’t leave my Xamarin buddies out! Consider this phase 1 in moving your tabs to the bottom of your Xamarin apps! In this post, we’ll look at a basic implementation of the new Material Design BottomNavigationView in Xamarin.Android, and in a later post, we’ll implement it in Xamarin.Forms with a custom TabbedRenderer.

Resources

Create a Menu Resource

The BottomNavigationView uses a menu to create the items in the navigation view, so you’ll need to create an xml resource under Resource/menu.

bottom_bar_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/all_puppies"
        android:title="@string/action_all"
        android:icon="@drawable/ic_home_white_24dp" />

    <item android:id="@+id/big_puppies"
        android:title="@string/action_big"
        android:icon="@drawable/ic_dog_white_24dp" />

    <item android:id="@+id/small_puppies"
        android:title="@string/action_small"
        android:icon="@drawable/ic_small_dog_white_24dp" />

    <item android:id="@+id/trained_puppies"
        android:title="@string/action_trained"
        android:icon="@drawable/ic_trained_white_24dp" />

    <item android:id="@+id/active_puppies"
        android:title="@string/action_active"
        android:icon="@drawable/ic_active_white_24dp" />
</menu>


Note that the images I use are from my previous post, and are included in the source code in GitHub linked above.

Update the Layout

Add an android.support.design.widget.BottomNavigationView to your layout, or you can now add it easily in Visual Studio (for Mac) in the design view by selecting it on the right.

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="All"
        android:textAlignment="center"
        android:gravity="center" />
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:minWidth="25px"
        android:minHeight="25px"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_alignParentBottom="true"
        android:background="@android:color/white"
        app:elevation="6dp"
        app:menu="@menu/bottom_bar_menu" />
</RelativeLayout>

or….

Screen Shot 2017-07-25 at 3.48.02 PM

Just make sure you properly set the layout_width and layout_height to meet the standards and also add elevation to give it the shadow and solid background.

Add Listeners

Xamarin did a great job of wrapping the Java listener with C# events so we can add the event handlers we know and love to handle changes when an item is selected in the BottomNavigationView.

MainActivity.cs

    public class MainActivity : Activity
    {

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            var textView = FindViewById<TextView>(Resource.Id.textView);
            var bottomBar = FindViewById<BottomNavigationView>(Resource.Id.bottomNavigationView);
            textView.Text = "All";
            bottomBar.NavigationItemSelected += (s,a) => {
                textView.Text = a.Item.TitleFormatted.ToString();
            };
        }
    }

Things to Remember

Remember that this is part of the AppCompat packages from Google, and is only in version 25+ of the Android Support Design Library. You can install the nuget package for it here: https://www.nuget.org/packages/Xamarin.Android.Support.Design/25.4.0-rc1

Because of this, you also need to set the theme of your Activity to something that is a sub-theme of Theme.AppCompat.

Results

Check it out!

Xamarin_Bottom_Bar

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

Xamarin.Tip – Fixing the VS for Mac Xamarin.Forms Template Android Issues

Here’s another quick resolution to one of the most annoying issues I’ve faced in working with Visual Studio for Mac…

The Problem

When creating a brand new, out of the box, Xamarin.Forms project, it didn’t work. This has been a common trend for Xamarin/Microsoft over the last year or so as they roll out Xamarin + Visual Studio updates. The latest culprit was that the Android project was never able to actually install the Xamarin.Forms package from nuget. This was not obvious during the actual build, since the project DID load successfully, and the build errors were along the lines of not being able to find Android Support library resources in the styles.xml. This also caused an issue where source files were not loading with intellisense or highlighting, so the error was even less obvious (see Xamarin.Tip – Fixing the Highlighting Drop In Your Xamarin.Android Projects).

When going into the nuget package manager and trying to manually add the Xamarin.Forms package, I was hit with this error:

Could not install package 'Xamarin.Android.Support.v4 23.3.0'. You are trying to install this package into a project that targets 'MonoAndroid,Version=v2.3'
...

The Solution

My first instinct was to check the Android target version in the project options to make sure that they were within the supported SDK range, and they were:
Screen Shot 2017-06-21 at 4.05.17 PM

Unlike Visual Studio 2017 (or any Windows VS with Xamarin), there was no “Compile using Android Version” picker. THIS field is what is really determines the projects target framework level, but VS for Mac doesn’t have it like it should (hope you’re reading this Microsoft!).

Let’s fix it manually:

  1. Unload your Android project by right clicking it in the Solution explorer and clicking “Unload”
  2. Open the Android project .csproj in the editor by right clicking > Tools > Edit.
  3. For each build configuration, add an explicit TargetFrameworkVersion that’s compatible. In my case that was v4.4
  4. Your .csproj should look like this:
    <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    ... 
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
        <ProjectGuid>{2BC9AAD5-D311-499E-8B06-A63CCB65C633}</ProjectGuid>
        <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
        <OutputType>Library</OutputType>
        <RootNamespace>AppName.Droid</RootNamespace>
        <AssemblyName>AppName.Droid</AssemblyName>
        <AndroidApplication>True</AndroidApplication>
        <AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
        <AndroidResgenClass>Resource</AndroidResgenClass>
        <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
        <MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
        <MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
        <AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion> <!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>bin\Debug</OutputPath>
        <DefineConstants>DEBUG;</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <AndroidLinkMode>None</AndroidLinkMode>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion><!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <OutputPath>bin\Release</OutputPath>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <AndroidManagedSymbols>true</AndroidManagedSymbols>
        <AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion><!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
    
    ...
    </Project>
    
  5. Close the .csproj editor
  6. Reload the project by right clicking in the Solution Explorer and selecting Reload
  7. Retry adding the Xamarin.Forms nuget package to the project

Now you should be good to go. Did this not work for you? Leave a comment below and I’d be happy to help you fix this annoying issue as well!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tips – Overriding Android Button Shadows/Elevation

Since Material Design’s implementation in the Android OS, some controls that ship with either the new styles, or with the App Compat packages place some under-the-cover restrictions on what you can do with the control by default. In this example, we will look at updating the App Compat Button Shadows and Elevation that ship with the control.

According to Material Design’s standards, “raised buttons” (versus flat buttons and floating action buttons) should have a resting elevation of 2dp, and an pressed/hover elevation of 8dp.

whatismaterial_3d_elevation_component02

This principle is also implemented in the App Compat Button. However, if you try to update the Elevation of your Button, you’ll notice that it won’t stay that way on the redraw, but will go right back to the 4dp it is by default.


supportButton.Elevation = 9; // set it directly
ViewCompat.SetElevation(supportButton, 9); // set using app compat method

...

Console.WriteLine(supportButton.Elevation); // will return 4...

So why is this? And how is Android creating the pressed animation automatically to increase the elevation? It certainly isn’t any code we’ve written. The answer is in the StateListAnimator property of the Button. The StateListAnimator is responsible for setting properties of the Button during certain states such as Enabled, Disabled, Focused, Pressed, etc. and is what is overriding the manual set of Button.Elevation.

You can override this in a few different ways to claim back full control. First, if you want to handle your different different states manually in your code, you can set the StateListAnimator to a new instance, or null, then set the Elevation to what you want.

In Code

supportButton.StateListAnimator = new StateListAnimator();
ViewCompat.SetElevation(supportButton, 9);

...

Console.WriteLine(supportButton.Elevation); // 9!

The most reusable way to do this is to subclass Button and set the StateListAnimator in the constructor:

CustomElevatingButton.cs

public class CustomElevatingButton : Android.Support.V7.Widget.AppCompatButton
{
    public CustomElevatingButton(Context context): base(context)
    {
        StateListAnimator = new StateListAnimator();
    }
}

Using Styles

Alternatively, you can set it using styles for your Button:

styles.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
    </style>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:buttonStyle">@style/NoShadowButton</item>
    </style>
    <style name="NoShadowButton" parent="android:style/Widget.Button">
        <item name="android:stateListAnimator">@null</item>
    </style>
</resources>

You can also do it per-button:

 styles.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
    </style>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        ...
    </style>
    <style name="NoShadowButton" parent="android:style/Widget.Button">
        <item name="android:stateListAnimator">@null</item>
    </style>
</resources>

some_layout.axml

...
<Button style="@style/NoShadowButton" ... />
...

In Xamarin.Forms

We can do the same thing in Xamarin.Forms with either a custom renderer or a custom Effect. In this example, we will create a universal Xamarin.Forms.Button custom renderer to set an explicit height:

ElevatedButtonRenderer

public class ElevatedButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
{
    public override void OnElementChanged(ElementChangedEventArgs<Button> e)
    {
        StateListAnimator = null; // clear the state list animator
        Elevation = 9; // set the elevation
    }
}

Creating Your Own StateListAnimator

Of course, instead of clearing the StateListAnimator and handling your elevation manually, you could create your own to handle the states and animations however you want. Google has documentation included in the discussion about animations here. Here’s an example of creating and applying your own:

anim/reverse_state_list_animator.xml

<!-- animate the elevation property of a view when pressed -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_pressed="true">
    <set>
      <objectAnimator android:propertyName="elevation"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueTo="0dp"
        android:valueType="floatType"/>
        <!-- you could have other objectAnimator elements
             here for "x" and "y", or other properties -->
    </set>
  </item>
  <item android:state_enabled="true"
    android:state_pressed="false"
    android:state_focused="true">
    <set>
      <objectAnimator android:propertyName="elevation"
        android:duration="100"
        android:valueTo="2dp"
        android:valueType="floatType"/>
    </set>
  </item>
</selector>

This animation will do the reverse of the Material Design Standard, and will take the Button elevation from 2dp to 0dp when pressed.

Now we just need to apply this animation resource to our Button style either universally or on a specific button:

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
    </style>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:buttonStyle">@style/NoShadowButton</item>
    </style>
    <style name="NoShadowButton" parent="android:style/Widget.Button">
        <item name="android:stateListAnimator">@anim/reverse_state_list_animator</item>
    </style>
</resources>

Now pressing any button within the AppTheme will reverse the elevation property and go more “into” the view rather than elevating.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Controls – Android ArcLayout

Here’s a quick and fun new control to play with. It’s called the ArcLayout, and if the name isn’t descriptive enough, it allows you to layout your Views in the layout based on arcs, degrees, radii, etc. rather than using existing LinearLayouts, RelativeLayouts, or FrameLayouts.

The control is originally from OgacleJapan and you can find his native Android repository here: https://github.com/ogaclejapan/ArcLayout.

I’ve simply provided a Xamarin Android Binding Project to wrap it which you can find here: https://github.com/SuavePirate/ArcLayout. I haven’t created a nuget package for it yet, since the binding project could use some clean up first. However, here is an easy way to get started using it right away.

  1. Clone the Xamarin Binding Library (it includes the native project .jar/.aar in it, so you can clone and start right away).
  2. Copy the project to your solution, and add it as a reference in your Xamarin Android project.
  3. Start adding it to your layouts!
<com.ogaclejapan.arclayout.ArcLayout         
         android:id="@id/arc_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="bottom"
         app:arc_color="#4D000000"
         app:arc_radius="168dp"
         app:arc_axisRadius="120dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF" 
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

</com.ogaclejapan.arclayout.ArcLayout>

Now we can dissect the new attributes and properties we get from ArcLayout:
attrs

Properties of the ArcLayout

attr description
arc_origin Center of the arc on layout. All of patterns that can be specified, see the demo app.
arc_color Arc Shaped color
arc_radius Radius of the layout
arc_axisRadius Radius the axis of the child views
arc_freeAngle If set to true, each child view can set the free angle, default false
arc_reverseAngle If set to true, reverse the order of the child, default false. Note: If arc_freeAngle set to true does not work

 

Properties of the ArcLayout Child Views

arc_origin Set the origin point of arc_axisRadius as well as layout_gravity, default center
arc_angle If arc_freeAngle set to true, layout the specified angle

Here’s a good example to get you started including a full circle layout and two semicircles:

arc_layout_full_example.axml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                     xmlns:app="http://schemas.android.com/apk/res-auto"
                 android:id="@+id/MainLayout"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
  <com.ogaclejapan.arclayout.ArcLayout
         android:id="@+id/arc_layout_top"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="top"
         app:arc_color="#321321"
         app:arc_radius="168dp"
         app:arc_axisRadius="120dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF"
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

</com.ogaclejapan.arclayout.ArcLayout>
<com.ogaclejapan.arclayout.ArcLayout
         android:id="@+id/arc_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="bottom"
         app:arc_color="#03a9f4"
         app:arc_radius="168dp"
         app:arc_axisRadius="120dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF"
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

</com.ogaclejapan.arclayout.ArcLayout>
<com.ogaclejapan.arclayout.ArcLayout
         android:id="@+id/arc_layout_center"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="center"
         app:arc_color="#4D123123"
         app:arc_radius="50dp"
         app:arc_axisRadius="50dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF"
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="D"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>
</com.ogaclejapan.arclayout.ArcLayout>
</RelativeLayout>

arclayout

Feel free to contribute to the Xamarin Android Bindings Library repository mentioned above, I’m happy to field pull requests that can help clean it up for a real release.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tips – Android Bar Background Images in Xamarin.Forms

In a previous post, we talked about the iOS side of setting background images for both NavigationBars and TabBars. Beyond that, we looked at setting them so that we can simulate having transparent bars that show a background image behind the entire Page. That looked like this:

Simulator Screen Shot Apr 7, 2017, 11.23.33 AM

So now let’s do the same thing in Android!

Android

So Android handles images very differently compared to iOS, and also gives us a few easy ways to do what we want here. Rather than having to create custom renderers, we need to create Drawables for our images, and apply them in our Android Layouts for our Toolbar and TabBar. This approach is going to be more similar to Morgan Skinner’s approach for iOS here: http://www.morganskinner.com/2015/01/xamarin-formsusing-background-images-on.html

So we will need to crop our image where our Toolbar and Tabbar will be. If you are planning to also support landscape views, be sure to also crop the image separately for landscape and include those images in your appropriate drawables folders such as drawables-land-hdpi.

Another thing to consider is that, unlike in iOS, Android tabs are placed below the toolbar rather than at the bottom of the entire view. Because of this, you made need to use a different background for your pages that have a TabBar versus the pages that don’t. Here are some examples of how you might need to crop your images:

Portrait with no tabs

crop_portrait

Portrait with tabs

crop_portrait_tabs

Landscape with no tabs

crop_landscape

Landscape with tabs

crop_landscape_tabs

Be sure that you name the individual images the same, but place them in the appropriate resource folder as explained before. In this example we will call them:

  • toolbar_background.png
  • tabbar_background.png
  • page_background.png
  • tab_page_background.png

Unlike our iOS implementation, we do NOT need any custom renderers. Instead, we will set the background drawable of our layout files for our bars, and then in our xaml page, add an image to be the background.

Toolbar.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:minHeight="?attr/actionBarSize"
    android:background="@drawable/toolbar_background"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:layout_scrollFlags="scroll|enterAlways" /> 

Tabbar.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.TabLayout         
    xmlns:android="http://schemas.android.com/apk/res/android"              
    xmlns:app="http://schemas.android.com/apk/res-auto"              
    android:id="@+id/sliding_tabs"     
    android:layout_width="match_parent"        
    android:layout_height="wrap_content"          
    android:background="@drawable/tabbar_background"          
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"          
    app:tabIndicatorColor="@android:color/white"
    app:tabGravity="fill"      
    app:tabMode="fixed" />

Then be sure to set the resources before starting Xamarin.Forms in your MainActivity:

MainActivity.cs

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());

        }
    }

With all those things set, you can now use your background images on pages with or without tabs:

ContentPageWithTabs.xaml

<ContentPage ...>
    <Grid ...>
        <Image Source="tab_page_background.png" .../>
        <!-- The rest of your content on top of the image -->
    </Grid>
</ContentPage>

ContentPageWithNoTabs.xaml

<ContentPage ...>
    <Grid ...>
        <Image Source="page_background.png" .../>
        <!-- The rest of your content on top of the image -->
    </Grid>
</ContentPage>

 

You can see an example result here (with a rushed crop job):

Screenshot_2017-04-18-14-20-08

 

 

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tips – Sending Authorized SignalR Requests

In my last post, I talked about creating a CookieProvider for handling OAuth Bearer Tokens from an HTTP Cookie. Now that we have this capability, we can make a secure connection to SignalR using a Cookie store rather than adding our tokens to the Authorization HTTP Header.

By using cookies instead of the HTTP Header, we can now use a full Websocket connection rather than being forced into long polling. Using websockets increases speed of messages received and messages sent.

So here’s the quick bit of code for making a secure SignalR Websocket connection:

SignalRConnectionService.cs

    public class SignalRConnectionService
    {
        private HubConnection _connection;
        public IHubProxy AppHubProxy { get; set; }

        public SignalRConnectionService()
        {
            _connection = new HubConnection(YOUR_SIGNALR_URL);
            AppHubProxy = _connection.CreateHubProxy("AppHub");
        }

        public async Task StartAsync()
        {
            try
            {
                if(_connection.State != ConnectionState.Disconnected)
                {
                    _connection.Stop();
                }
                _connection.CookieContainer = new CookieContainer();
                _connection.CookieContainer.Add(new Uri(THE_BASE_URL_OF_YOUR_SERVER), new Cookie("BearerToken", YOUR_ACCESS_TOKEN));
                await _connection.Start();
               
        }
        public void Stop()
        {
            _connection.Stop();
        }
    }

Now we have the full speed of websockets enabled in our Xamarin, Mac, and Windows applications!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Xamarin.Tips – Restrict the Length of Your Entry Text

Here’s a quick one on how to restrict the number of characters a user can enter in an Entry. Basically, we are going to create a custom Behavior and then apply it to our Entry.

EntryLengthValidatorBehavior.cs

 /// <summary>
    /// Behavior that restricts the length of an entry
    /// </summary>
    public class EntryLengthValidatorBehavior : Behavior<Entry>
    {
        public int MaxLength { get; set; }

        protected override void OnAttachedTo(Entry bindable)
        {
            base.OnAttachedTo(bindable);
            bindable.TextChanged += OnEntryTextChanged;
        }

        protected override void OnDetachingFrom(Entry bindable)
        {
            base.OnDetachingFrom(bindable);
            bindable.TextChanged -= OnEntryTextChanged;
        }

        void OnEntryTextChanged(object sender, TextChangedEventArgs e)
        {
            var entry = (Entry)sender;

            if (entry.Text.Length > this.MaxLength)
            {
                string entryText = entry.Text;
                entry.TextChanged -= OnEntryTextChanged;
                entry.Text = e.OldTextValue;
                entry.TextChanged += OnEntryTextChanged;
            }
        }
    }

Now we can apply it in our Xaml:

<Entry x:Name="Pin1" TextColor="White">
    <Entry.Behaviors>
        <behaviors:EntryLengthValidatorBehavior MaxLength="4"/>
    </Entry.Behaviors>
</Entry>

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!