Xamarin.Control – Xamarin.Forms MaterialEntry

Back by popular demand, bringing more Material Design controls to you Xamarin.Forms app! This time we will look at implementing the standards in Material Design’s text fields by building a MaterialEntry control. You can find the source code and example app here: https://github.com/SuavePirate/MaterialEntry but you can build your own by following this post.

Let’s build our Xamarin.Forms control to work the same on not only Android where Material Design is baked in, but also to run on iOS AND UWP. In the end, we should be able to use our floating label, set an accent color that expands on the label and underline when focused while being able to bind these properties through MVVM.

Simulator Screen Shot Jul 14, 2017, 3.42.03 PM

The first thing we need to do is create a BorderlessEntry that removes the border from our entry on all 3 platforms. I’ve done this in a previous blog post here: Xamarin.Forms Borderless Entry, so we won’t be implementing it here. This code is also in the GitHub link above.

With our BorderlessEntry we can now create our custom control WITHOUT ANY MORE CUSTOM RENDERERS!

Let’s set up the layout structure in our XAML file, and then wire up the animation logic in our code behind.

MaterialEntry.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"               xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:local="clr-namespace:SuaveControls.MaterialEntry"              x:Class="SuaveControls.MaterialEntry.MaterialEntry">
  <ContentView.Content>
        <Grid ColumnSpacing="16" Margin="0,8">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="1"/>
            </Grid.RowDefinitions>
            <Label x:Name="HiddenLabel" FontSize="10" IsVisible="False" Margin="0"/>
            <local:BorderlessEntry x:Name="EntryField" Text="{Binding Text, Mode=TwoWay}" Margin="0,12,0,0"/>
            <BoxView x:Name="BottomBorder" BackgroundColor="Gray"  Grid.Row="1" HeightRequest="1" Margin="0" HorizontalOptions="FillAndExpand"/>
            <BoxView x:Name="HiddenBottomBorder" BackgroundColor="Gray" Grid.Row="1" HeightRequest="1" Margin="0" WidthRequest="0" HorizontalOptions="Center"/>
        </Grid>
    </ContentView.Content>
</ContentView>

We set up our BorderlessEntry that will act as our formal point for entering text. We also add a label that is initially hidden and laid out on top of the BorderlessEntry. This is the label we will be using to animate the floating action that Material Design uses based while we fade out the placeholder text. The last bit is two BoxViews that act as the bottom line below the Entry. One is the unfocused which has a standard gray color, while the other has a width of 0 and will have a background color of our selected AccentColor. This will have an animated width expansion when the BorderlessEntry is focused.

Now let’s look at the animation and bindings in the code behind:

MaterialEntry.xaml.cs

public partial class MaterialEntry : ContentView
    {
        public static void Init() { }
        public static BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(MaterialEntry), defaultBindingMode: BindingMode.TwoWay);
        public static BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(MaterialEntry), defaultBindingMode: BindingMode.TwoWay, propertyChanged: (bindable, oldVal, newval) =>
        {
            var matEntry = (MaterialEntry)bindable;
            matEntry.EntryField.Placeholder = (string)newval;
            matEntry.HiddenLabel.Text = (string)newval;
        });

        public static BindableProperty IsPasswordProperty = BindableProperty.Create(nameof(IsPassword), typeof(bool), typeof(MaterialEntry), defaultValue: false, propertyChanged: (bindable, oldVal, newVal) =>
        {
            var matEntry = (MaterialEntry)bindable;
            matEntry.EntryField.IsPassword = (bool)newVal;
        });
        public static BindableProperty KeyboardProperty = BindableProperty.Create(nameof(Keyboard), typeof(Keyboard), typeof(MaterialEntry), defaultValue: Keyboard.Default, propertyChanged: (bindable, oldVal, newVal) =>
        {
            var matEntry = (MaterialEntry)bindable;
            matEntry.EntryField.Keyboard = (Keyboard)newVal;
        });
        public static BindableProperty AccentColorProperty = BindableProperty.Create(nameof(AccentColor), typeof(Color), typeof(MaterialEntry), defaultValue: Color.Accent);
        public Color AccentColor
        {
            get
            {
                return (Color)GetValue(AccentColorProperty);
            }
            set
            {
                SetValue(AccentColorProperty, value);
            }
        }
        public Keyboard Keyboard
        {
            get
            {
                return (Keyboard)GetValue(KeyboardProperty);
            }
            set
            {
                SetValue(KeyboardProperty, value);
            }
        }

        public bool IsPassword
        {
            get
            {
                return (bool)GetValue(IsPasswordProperty);
            }
            set
            {
                SetValue(IsPasswordProperty, value);
            }
        }

        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }
        public string Placeholder
        {
            get
            {
                return (string)GetValue(PlaceholderProperty);
            }
            set
            {
                SetValue(PlaceholderProperty, value);
            }
        }
        public MaterialEntry()
        {
            InitializeComponent();
            EntryField.BindingContext = this;
            EntryField.Focused += async (s, a) =>
            {
                HiddenBottomBorder.BackgroundColor = AccentColor;
                HiddenLabel.TextColor = AccentColor;
                HiddenLabel.IsVisible = true;
                if (string.IsNullOrEmpty(EntryField.Text))
                {
                    // animate both at the same time
                    await Task.WhenAll(
                        HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, BottomBorder.Width, BottomBorder.Height), 200),
                        HiddenLabel.FadeTo(1, 60),
                        HiddenLabel.TranslateTo(HiddenLabel.TranslationX, EntryField.Y - EntryField.Height + 4, 200, Easing.BounceIn)
                     );
                    EntryField.Placeholder = null;
                }
                else
                {
                    await HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, BottomBorder.Width, BottomBorder.Height), 200);
                }
            };
            EntryField.Unfocused += async (s, a) =>
            {
                HiddenLabel.TextColor = Color.Gray;
                if (string.IsNullOrEmpty(EntryField.Text))
                {
                    // animate both at the same time
                    await Task.WhenAll(
                        HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, 0, BottomBorder.Height), 200),
                        HiddenLabel.FadeTo(0, 180),
                        HiddenLabel.TranslateTo(HiddenLabel.TranslationX, EntryField.Y, 200, Easing.BounceIn)
                     );
                    EntryField.Placeholder = Placeholder;
                }
                else
                {
                    await HiddenBottomBorder.LayoutTo(new Rectangle(BottomBorder.X, BottomBorder.Y, 0, BottomBorder.Height), 200);
                }
            };
        }
    }

We first set up the BindableProperties and public properties to enable the binding of the AccentColor, Text, Placeholder, and Keyboard. These BindableProperties also handle their own PropertyChanged events to update the view elements dynamically.

After that, we handle our constructor and wire up our Focused and Unfocused events on our BorderlessEntry. In the Focused event, we set the colors of the hidden bar, and the floating label to the accent color. We then start the animations of expanding the hidden bar, and the fade in and float up of the floating label.

On the Unfocused event, we do the inverse of setting the floating label color back to the unfocused color, check if there is text, if there is not – float the label back down, and then animate the collapse of the colored bar.

With all these things together, we get a nicely animated text field that has a floating label and expanding bottom bar with a given accent color!

ios_Material_Entry2

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.Basics – Ad Hoc iOS Builds, Part 2a: Publishing in HockeyApp

Before Getting Started

Before learning how to upload your iOS builds to HockeyApp, be sure to check out Part 1 of this series: Xamarin.Basics – Ad Hoc iOS Builds, Part1: Certificates and Profiles to learn about the requirements for producing Ad Hoc builds that will be distributed in HockeyApp.

Quick Intro

Up to this point, we should have everything we need to build our iOS application in Release/Ad Hoc mode to produce an .ipa file. This output .ipa is what we will be uploading to HockeyApp and other distribution channels such as Mobile Center. It’s important to note that HockeyApp is actually on its way out the door and being booted by the new Visual Studio Mobile Center produced by Microsoft after acquiring HockeyApp and their team. At this point, Mobile Center is still in Preview, so if you’re looking for a production ready solution for the time being, stay here. If not, follow my next blog post (to be posted here) on doing this in Mobile Center.

Building

Now that we have our Distribution Certificate and Ad Hoc Provisioning Profile created in the Apple Developer Portal and downloaded on our Mac, we can move into Visual Studio and build our Application for Ad Hoc Release. The screenshots you’ll see below will be from Visual Studio 2017 on Windows, but the same principles apply to Visual Studio for Mac.

Ensure your Bundle Identifiers Match

In the Apple Developer portal, we created an App ID and in this definition, we set a Bundle Identifier. This ID needs to match the ID of our actual application. You can set the Bundle Identifier in the Info.plist file of your iOS project in Visual Studio.

Screen Shot 2017-07-12 at 1.54.00 PM

Set Build Configuration to Release – iPhone

Screen Shot 2017-07-12 at 2.07.06 PM

In order to build for iOS Ad Hoc distribution, you’ll need to build against an actual iOS device. Building for simulators does some things differently behind the scenes that makes it faster to install in a simulator. Since our app needs to run against devices, we need to build against a real device. We also need to build in Release mode since we shouldn’t be debugging an Ad Hoc build.

Double Check Your Bundle Signing

Before building, double check that your app is going to use the proper Signing Identity and Provisioning Profile. You can do this by opening the Properties (“Options” if your in VS for Mac) and go to the Bundle Signing tab. Here you can explicitly set a Profile and Certificate to use or let it automatically choose the profile and hope it works out.

Screen Shot 2017-07-12 at 2.12.12 PM.png

Build and Deploy

Now simply build and deploy your app by right clicking the project in the Solution Explorer and clicking the Deploy option.

Screen Shot 2017-07-12 at 2.21.49 PM

This will now create the .ipa file in your bin. Locate your .ipa at YOUR_IOS_PROJECT\bin\Ad-Hoc\YOUR_BUNDLE.ipa. This .ipa is what will be uploaded to HockeyApp and distributed.

Using HockeyApp

HockeyApp has some great getting started documentation for creating your app and also viewing crash reports etc. You can find that here: https://support.hockeyapp.net/kb.

We will focus on uploading a new build using the .ipa file we created. In order to do this, you will need to have created an account with HockeyApp and created an iOS Alpha or Beta application slot.

From here you can click on the Add Version button which will then prompt you to upload your .ipa file.

Screen Shot 2017-07-12 at 2.39.05 PMScreen Shot 2017-07-12 at 2.39.12 PM

Now you’ll be able to add some release notes to your version that is being uploaded. Then enable the people who are allowed to install your app and send them notifications.

Once this is done, your users whose devices are registered by their UDID to the Apple Developer Portal and the Provisioning Profile used will be able to download and install it through the HockeyApp mobile website!

With your users having their hands on the app, you’ll be able to view crash reports, respond to feedback and more through the HockeyApp portal!

Next Steps

Now that we’ve been able to manually upload builds to HockeyApp, we’ll be able to look at using Continuous Integration and Deployment to distribute our new versions as we commit our changes in the source code. James Montemagno has a great blog post in the official Xamarin blog here: https://blog.xamarin.com/continuous-integration-for-ios-apps-with-visual-studio-team-services/.

In a following post, we will look at the future of deploying our iOS applications internally by using the new Visual Studio Mobile Center.

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.

Android – Comparing Models in Kotlin, Java, and C# for Xamarin

Models are an obvious important part of our applications we develop. It’s how we define our data and because of that, we can grow our models to be quite large in number. This post will look at comparing the definition of models between Java, C# (for Xamarin), and Kotlin.

Our example model will be a simple Person that has a Name and Description along with an ID. Let’s look at how we can define our Person model with these fields as well as the ability to access these fields publicly.

Using Java

Java is going to be our largest and most annoying to develop. We need to define our class, private fields, and then public functions/methods to get and set the value of each of the fields. We also need to be able to instantiate a Person with all these properties set in the constructor.

Person.java

public class Person{
    private int id;
    private String name;
    private String description;

    public Person(int id, string name, string description){
        this.id = id;
        this.name = name;
        this.description = description;
    }

    public void setID(int id){
        this.id = id;
    }
    public int getID(){
        return this.id;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
    public void setDescription(String description){
        this.description = description;
    }
    public String getDescription(){
        return this.description;
    }
}

That’s exhausting…

Now we can instantiate it and update properties like this:

...
Person bob = new Person(1, "Bob", "He writes code and stuff");
bob.setDescription("He doesn't actually write code");
...

Using C# for Xamarin Applications

C# makes our lives a lot easier with the get and set mechanism built into properties.

Person.cs

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }

    public Person(int id, string name, string description)
    {
        ID = id;
        Name = name;
        Description = description;
    }
}

Nice and neat!

Now we can instantiate it and update properties like this:

...
var bob = new Person(1, "Bob", "He writes code and stuff");
bob.Description = "He doesn't actually write code";
...

Using Kotlin

Kotlin has some cool tricks that allow us to define and set properties directly in our constructor without having to define and set them separately. This gives us the quickest way to create simple POCO definitions and speed up that development time.

Person.kt

class Person(var id: Int, var name: String, var description: String);

One line.

Now we can instantiate it and update properties like this:

...
val bob = Person(1, "Bob", "He writes code and stuff");
bob.description = "He doesn't actually write code";
...

Conclusion

Each language has their nuances, but I think we can all agree that defining models in Java is just a headache that other languages have solved with better solutions.

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.

Android.Kotlin – Create a TabLayout

We’ll once again take a break from the cross-platform Xamarin content and look at an example of using the latest Kotlin language from Jetbrains with our native Android applications. In this post, we’ll look at an implementation of a TabLayout with a ViewPager using Kotlin!

I also apologize for the lack of useful highlighting of the Kotlin code in this post. Since it is a new language, WordPress doesn’t support it as well for code snippets…

The source code for this example can be found on my GitHub here:
https://github.com/SuavePirate/KotlinPuppies.

The Layout

This example will use a RecyclerView for the content of each Fragment. So we need to define layouts for our Puppy, Fragment, and our entire Activity that houses the TabLayout and Fragments.

Our puppy item will contain a CardView that has an image and text to contain a picture of the puppy and the pup’s name!

puppy_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content">

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp"
        app:cardElevation="8dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="16dp"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/puppyImageView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:srcCompat="@mipmap/ic_launcher" />

            <TextView
                android:id="@+id/puppyTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAlignment="center"
                android:text="Puppy Name" />
        </LinearLayout>

    </android.support.v7.widget.CardView>
</LinearLayout>

Now let’s look at our Fragment layout that will contain a RecylerView that houses each collection of puppies.

puppy_fragment.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.suavepirate.kotlinpuppies.MainActivity$PlaceholderFragment">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/puppyRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        tools:listitem="@layout/puppy_item" />
</RelativeLayout>

Now let’s wrap it all together with our main layout:

activity_main.xml

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.suavepirate.kotlinpuppies.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/appbar_padding_top"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay">

        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />


</android.support.design.widget.CoordinatorLayout>

Now that we have our layouts, let’s create our Fragment, Adapters, and then wrap it all together in our MainActivity.

Building the Recycler Adapter

Let’s first define our RecyclerView adapter and ViewHolder to contain our collections of puppies.

PuppyHolder.kt

class PuppyHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val puppyImage: ImageView = itemView.findViewById<ImageView>(R.id.puppyImageView)
    private val puppyName: TextView = itemView.findViewById(R.id.puppyTextView)

    fun updateWithPuppy(puppy: Puppy) {
        puppyImage.setImageDrawable(puppy.imageFile)
        puppyName.text = puppy.name
    }
}

This code defines a class that inherits the RecyclerView.ViewHolder with a default constructor that requires a View parameter that is also passed into the base class constructor. It then defines the two subviews we need to populate – the TextView and ImageView of a single puppy. Lastly, we create our updateWithPuppy function that will be called by our Adapter to instantiate the content with the given puppy’s information.

Now that we have our ViewHolder, we can create our Adapter:

PuppyAdapter.kt

class PuppyAdapter(private val puppies: ArrayList<Puppy>) : RecyclerView.Adapter<PuppyHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PuppyHolder {
        val puppyItem = LayoutInflater.from(parent.context).inflate(R.layout.puppy_item, parent, false) as LinearLayout
        return PuppyHolder(puppyItem)
    }

    override fun onBindViewHolder(holder: PuppyHolder, position: Int) {
        holder.updateWithPuppy(puppies[position])
    }

    override fun getItemCount(): Int {
        return puppies.toArray().count();
    }

}

This adapter uses another cool feature of Kotlin – Defining a private field in the constructor while also auto-setting it. The class declaration and default constructor of PuppyAdapter(private val puppies: ArrayList) is the equivalent to something like this in Java:

public class PuppyAdapter{
    private final ArrayList<Puppy> puppies;
    public PuppyAdapter(ArrayList<Puppy> puppies){
        this.puppies = puppies;
    }
}

That’s pretty sweet! The rest of the wire up for the Adapter is pretty standard. It sets the ViewHolder using the PuppyHolder we created above and updates it with the puppy by finding it with the given index.

The Puppy Fragment

Now we can create our Fragment that will contain and wire up the RecyclerView for each puppy collection.

PuppyListFragment.kt


class PuppyListFragment(passedContext: Context) : Fragment(){

    val puppyFactory : PuppyFactory = PuppyFactory(passedContext)
    val ARG_LIST_TYPE = "LIST_TYPE"
    val passThroughContext : Context = passedContext


    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val rootView = inflater!!.inflate(R.layout.fragment_main, container, false)
        val recyclerView = rootView.findViewById<RecyclerView>(R.id.puppyRecyclerView) as RecyclerView
        val listType = this.arguments.getSerializable(ARG_LIST_TYPE) as PuppyListType
        var puppies = ArrayList<Puppy>()
        when (listType) {
            PuppyListType.All -> puppies = puppyFactory.puppies
            PuppyListType.Active -> puppies = puppyFactory.activePuppies
            PuppyListType.LeashTrained -> puppies = puppyFactory.leashTrainedPuppies
            PuppyListType.Big -> puppies = puppyFactory.bigPuppies
            PuppyListType.Small -> puppies = puppyFactory.smallPuppies
        }

        recyclerView.adapter = PuppyAdapter(puppies)
        recyclerView.layoutManager = LinearLayoutManager(passThroughContext)
        return rootView
    }

    companion object {
        val ARG_LIST_TYPE = "LIST_TYPE"

        fun newInstance(listType: PuppyListType, context: Context): PuppyListFragment {
            val fragment = PuppyListFragment(context)
            val args = Bundle()
            args.putSerializable(ARG_LIST_TYPE, listType)
            fragment.arguments = args
            return fragment
        }
    }


}

In the onCreateView override, we get our puppies by type from our factory class and then instantiate our PuppyAdapter and LinearLayoutManager that get applied to the RecyclerView that we grab from our layout created earlier. Now we can pass in the PuppyListType that the fragment is responsible for displaying which will then set up our RecyclerView to render those particular puppies.

We also set up what is the equivalent of a static function that can instantiate a new instance of a PuppyListFragment by using a nested companion object.

Adding Page Adapter

Now that we have our Fragment and it’s child RecyclerView for puppies all set up, we can now create an adapter that is responsible for handling the different pages within the TabLayout that we are ultimately setting up.

PageAdapter.kt

class PageAdapter(fm: FragmentManager, private val context: Context) : FragmentPagerAdapter(fm) {

    override fun getItem(position: Int): Fragment {
        when (position) {
            0 -> return PuppyListFragment.newInstance(PuppyListType.All, context)
            1 -> return PuppyListFragment.newInstance(PuppyListType.Big, context)
            2 -> return PuppyListFragment.newInstance(PuppyListType.Small, context)
            3 -> return PuppyListFragment.newInstance(PuppyListType.LeashTrained, context)
            4 -> return PuppyListFragment.newInstance(PuppyListType.Active, context)
        }
        return PuppyListFragment.newInstance(PuppyListType.All, context)
    }

    override fun getCount(): Int {
        // Show 5 total pages.
        return 5
    }

    override fun getPageTitle(position: Int): CharSequence? {
        // return null to show no title.
        return null
        
    }

}

This is a pretty standard implementation of a PageAdapter. We override the getItem function and return the appropriate instantiated PuppyListFragment by passing in the PuppyListType we want to use by the grouping.

Set up the Activity

The last bit now is the set up our Activity that will house our TabLayout and ViewPager that will contain multiple instances of the PuppyListFragment to show different collections of puppies by category.

MainActivity.kt



class MainActivity : AppCompatActivity() {
    private var mSectionsPagerAdapter: PageAdapter? = null

    /**
     * The [ViewPager] that will host the section contents.
     */
    private var mViewPager: ViewPager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar = findViewById<View>(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = PageAdapter(supportFragmentManager, this)

        // Set up the ViewPager with the sections adapter.
        mViewPager = findViewById<ViewPager?>(R.id.container)
        mViewPager!!.adapter = mSectionsPagerAdapter

        val tabLayout = findViewById<View>(R.id.tabs) as TabLayout
        tabLayout.setupWithViewPager(mViewPager)

        // set icons
        tabLayout.getTabAt(0)!!.setIcon(R.drawable.ic_home_white_24dp)
        tabLayout.getTabAt(1)!!.setIcon(R.drawable.ic_dog_white_24dp)
        tabLayout.getTabAt(2)!!.setIcon(R.drawable.ic_small_dog_white_24dp)
        tabLayout.getTabAt(3)!!.setIcon(R.drawable.ic_trained_white_24dp)
        tabLayout.getTabAt(4)!!.setIcon(R.drawable.ic_active_white_24dp)

    }
}

Our MainActivity holds a private field for the ViewPager reference, and in the override of onCreate, we set up our view components by finding them in our associated layout file, then wire up the PageAdapter with our TabLayout. Then we set our icons for each given tab after calling the setupWithViewPager on our TabLayout.

View the Results

We can run our application and view our expected results of our tabs and different list of puppy cards!

Screen Shot 2017-07-05 at 11.14.13 AM

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.Tip – PCL Profile Problems

New to Xamarin now that Microsoft is pushing it harder with Visual Studio 2017 and Visual Studio for Mac? Can’t create a new PCL and reference it in your Out of the Box Xamarin.Forms project? This post is for you, and I assure you – you are not alone.

 

The Problem

The core of the problem is this shift within the Microsoft Stack from many different versions of the .NET framework and tools into a “standard” – .NET Standard. However, certain parts of Xamarin aren’t quite there. These are things that worked in Visual Studio 2015, but are now new problems for VS 2017 and VS for Mac, especially if you’re working on a team that uses a mix of these.

There are multiple types of Profiles available for a Portable Class Library. Here’s a quick matrix of the different Profiles and where they compare to .NET Standard versions and supported profiles:

PCL Profile .NET Standard PCL Platforms
Profile7 1.1 .NET Framework 4.5, Windows 8
Profile31 1.0 Windows 8.1, Windows Phone Silverlight 8.1
Profile32 1.2 Windows 8.1, Windows Phone 8.1
Profile44 1.2 .NET Framework 4.5.1, Windows 8.1
Profile49 1.0 .NET Framework 4.5, Windows Phone Silverlight 8
Profile78 1.0 .NET Framework 4.5, Windows 8, Windows Phone Silverlight 8
Profile84 1.0 Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile111 1.1 .NET Framework 4.5, Windows 8, Windows Phone 8.1
Profile151 1.2 .NET Framework 4.5.1, Windows 8.1, Windows Phone 8.1
Profile157 1.0 Windows 8.1, Windows Phone 8.1, Windows Phone Silverlight 8.1
Profile259 1.0 .NET Framework 4.5, Windows 8, Windows Phone 8.1, Windows Phone Silverlight 8

Now we can look at the different .NET Standard versions and their supported platforms:

 

.NET Standard 1.0 1.1 1.2 1.3 1.4 1.5 1.6 2.0
.NET Core 1.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0
.NET Framework (with tooling 1.0) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.2
.NET Framework (with tooling 2.0 preview) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.1 4.6.1 4.6.1
Mono 4.6 4.6 4.6 4.6 4.6 4.6 4.6 vNext
Xamarin.iOS 10.0 10.0 10.0 10.0 10.0 10.0 10.0 vNext
Xamarin.Android 7.0 7.0 7.0 7.0 7.0 7.0 7.0 vNext
Universal Windows Platform 10.0 10.0 10.0 10.0 10.0 vNext vNext vNext
Windows 8.0 8.0 8.1
Windows Phone 8.1 8.1 8.1
Windows Phone Silverlight 8.0

 

Xamarin.Forms is created to support everything from Windows Silverlight 8.0 and up. This means that Xamarin.Forms projects are created using a Profile 259 PCL. This is fine, however, Profile 259 is on it’s way out the door, and therefore can’t be created in Visual Studio 2017! When you create a new PCL in VS 2017, it is created with the latest version of .NET Standard that supports .NET Core, UWP, and Xamarin (as of now this is default to Profile7. However, this is incompatible with Profile 259 and therefore cannot be referenced by your newly created Xamarin.Forms project. There’s no way in the IDE or properties to get it to 259, so if you do still want to support Windows Phone and Windows 8 with your Xamarin Projects in Visual Studio 2017, read below. If you don’t, update your Xamarin.Forms project to remove support for Windows 8 and Windows phone and your profiles will be matching again.

Here’s what the default Xamarin.Forms project targets are:
Screen Shot 2017-07-03 at 11.42.45 AM

And here is what a newly created PCL project targets are with everything selected:

Screen Shot 2017-07-03 at 11.42.58 AM

 

The Solution

We need to get our newly created PCLs to Profile 259 in order to reference them in our Xamarin.Forms project. We can’t do this in the IDE, so we need to dive into the .csproj file itself and make some changes:

NewPCL.csproj

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import ... />
  <PropertyGroup>
    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{4a6a9117-1715-47ea-a6c7-6b0fd5b31bdb}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>NewPCL</RootNamespace>
    <AssemblyName>NewPCL</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

    <!-- THIS IS WHAT NEEDS TO CHANGE -->
    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>


    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
  </PropertyGroup>
...
</Project>

Take that line of Profile7 and update it to Profile259 so that it looks like this all together:

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import ... />
  <PropertyGroup>
    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{4a6a9117-1715-47ea-a6c7-6b0fd5b31bdb}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>NewPCL</RootNamespace>
    <AssemblyName>NewPCL</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

    <!-- THIS IS WHAT HAS CHANGED -->
    <TargetFrameworkProfile>Profile259</TargetFrameworkProfile>


    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
  </PropertyGroup>
...
</Project>

 
Now you’ll be able to add this project as a reference in your Xamarin.Forms PCL project and continue on.

I know this is pretty annoying to do on large projects that contain many different project files. Take a look at my Onion Architecture project as an example…

This is something that is happening because of this weird in-between state we are at with .NET tooling and the move to .NET Standard. Going forward, Xamarin.Forms will be dropping support for Windows Phone Silverlight which will no longer require Profile259 in order to build our applications in Xamarin.Forms. However, for now, this is the fix for new projects!

Still Broken?

Still having issues with your project structures? Leave a comment and I’ll try to help resolve it.

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.

Android.Basics – Adding a Bottom Navigation View

Changing my pace of steady Xamarin content to go to my roots of native mobile development. This time, we’ll look at implementing the latest control provided by Google and Material Design – The BottomNavigationView.

Resources

Aren’t These Just Tabs?

I mean… yeah, but… it’s new and cool! Google finally realized that stretching to the top of the app can be annoying.
Screen Shot 2017-06-27 at 5.56.48 PM.png

This new control is also a little different from the TabLayout we all know in love from Material Design and Android development in that it is limited to 5 maximum tab items and does not support scrolling. It’s meant to act as top level or sibling level navigation as long as all items are of equal importance in the context of the application/current view. It is also nice to give some variety to our applications navigation scheme; larger apps with many tabbed views can become overwhelming, so tossing something new is relieving to our users.

Code

There are 3 major components to setting up a view with a BottomNavigationView.

  1. First, we need to create a menu resource for our navigation items.
  2. Then we need to create, style, and set up our BottomNavigationView in our layout.
  3. Lastly, add listeners for when an item is selected in our BottomNavigationView and make sure it fits the experience expectation defined in Material Design.

Create a Menu

In our example, we will be building an application for viewing adoptable puppies. Each navigation item will be a different set of these puppies by categorizing them. Let’s create a menu for “all”, “big”, “small”, “trained”, and “active” as categories for our puppies:

res/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>

With our menu, we can create our layout.

Updating the Layout

In our example, we are moving from a TabLayout with a ViewPager. However, the Material Design documentation for the BottomNavigationView states that it should NOT be used with side-swiping actions such as a ViewPager. Let’s replace that ViewPager with a FrameLayout that will be used to swap our active Fragment and also remove the TabLayout that is being replaced by the BottomNavigationView:

res/layout/activity_main.xml

<?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"     xmlns:tools="http://schemas.android.com/tools"     android:id="@+id/main_content"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:fitsSystemWindows="true"     tools:context="com.suavepirate.bottomnavigationpuppies.activities.MainActivity">

    <android.support.design.widget.AppBarLayout         android:id="@+id/appbar"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:paddingTop="@dimen/appbar_padding_top"         android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar             android:id="@+id/toolbar"             android:layout_width="match_parent"             android:layout_height="?attr/actionBarSize"             android:background="?attr/colorPrimary"             app:layout_scrollFlags="scroll|enterAlways"             app:popupTheme="@style/AppTheme.PopupOverlay">

        </android.support.v7.widget.Toolbar>
    </android.support.design.widget.AppBarLayout>

<FrameLayout     android:id="@+id/container"     android:layout_width="match_parent"     android:layout_height="match_parent"     app:layout_behavior="@string/appbar_scrolling_view_behavior" ></FrameLayout>
<android.support.design.widget.BottomNavigationView     android:id="@+id/bottombar"     android:layout_width="match_parent"     android:layout_height="56dp"     android:layout_gravity="bottom|fill_horizontal|start"     app:menu="@menu/bottom_bar_menu"     android:background="@android:color/white"     app:elevation="8dp"/>
</android.support.design.widget.CoordinatorLayout>

It’s important to layout the BottomNavigationView at the bottom of the page as well as give it a solid background and elevation. Also, notice how we apply our menu we created to the view by setting app:menu="@menu/bottom_bar_men".

With our layout set, let’s wire up listeners to update the current Fragment based on the selected navigation item.

Setting Up Listeners

In our MainActivity.java we can implement the BottomNavigationView.OnNavigationItemSelectedListener interface and override the onNavigationItemSelected method:

MainActivity.java

// imports

public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
    private PageAdapter mSectionsPagerAdapter;
    private FrameLayout mContainer;
    private BottomNavigationView mBottomBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        // Create the adapter that will return a fragment for each of puppy types
        mSectionsPagerAdapter = new PageAdapter(getSupportFragmentManager(), this);

        // Set up the ViewPager with the sections adapter.
        mContainer = (FrameLayout) findViewById(R.id.container);

        // set up the first Fragment
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.add(R.id.container, mSectionsPagerAdapter.getItem(0), "CURRENT_PAGE");
        ft.commit();

        // set up the bottom bar and listener
        mBottomBar = (BottomNavigationView)findViewById(R.id.bottombar);
        mBottomBar.setOnNavigationItemSelectedListener(this);

    }

    // Handles when an item is selected to update the fragment container
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);

        switch(item.getItemId()){
            case R.id.all_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(0));
                break;
            case R.id.big_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(1));
                break;
            case R.id.small_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(2));
                break;
            case R.id.trained_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(3));
                break;
            case R.id.active_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(4));
                break;
        }
        ft.commit();
        return true;
    }
}

Now with all of this, we are able to switch the current Fragment with a fade in and out animation when the selected navigation item is updated. That means our BottomNavigationView is implemented and ready to go!

Screenshot_1497932698

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.