StickyScrollView for Xamarin Android

ScrollViews are pretty useful when there is more content than space to display it. But through scrolling chances are high that the user loses track of context or title of the section, so it’s nice to have a sticky header/title that always stays at the top of the ScrollView.

Emil Sjölander created such a control (https://github.com/emilsjolander/StickyScrollViewItems/) for Java Android projects so I decided to port the control to C#. https://github.com/maltegoetz/StickyScrollViewXamarinAndroid

sticky22

The repository contains an example project with a simple StickyScrollView that has a LinearLayout with a few colored FrameLayouts children. One on the FrameLayouts is sticky.

Usage

Usage is pretty easy, you just need to copy the StickyScrollView.cs file to your project and the Attrs.xml file to the values folder of your project.

Replace the ScrollView with a StickyScrollview. From this:

<ScrollView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_height="match_parent"
  android:layout_width="match_parent">
    <!-- scroll view child goes here -->
</ScrollView>

to

<namespacelowercase.StickyScrollView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_height="match_parent"
  android:layout_width="match_parent">
    <!-- scroll view child goes here -->
</namespacelowercase.StickyScrollView>

The StickyScrollView inherits from ScrollView so you can only add one child but the child can have some more children. In this example I used a LinearLayout with a few views. One or more children can have an android:tag attribute with the value sticky, these will stick at the top of the ScrollView.

<namespacelowercase.StickyScrollView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/sticky_scroll"
  android:layout_height="match_parent"
  android:layout_width="match_parent">
    <LinearLayout
      android:layout_height="match_parent"
      android:layout_width="match_parent"
      android:orientation="horizontal">
        <!-- other children -->
        <View
          android:layout_height="300dp"
          android:layout_width="match_parent"
          android:tag="sticky"/>
        <!-- other children -->
    </LinearLayout>
</namespacelowercase.StickyScrollView>

There are also two additional flags that can be set on views that were added to optimize performance for the most usual cases. If the view you want to stick either has transparency or does not have a constant representation than you must supply one or both of the following flags. -hastransparancy for views that have transparancy and -nonconstant for views that will change appearance during there sticky time (examples are buttons with pressed states as well as progress spinners).

So this ends up with 4 different ways to tag a view as sticky resulting is slightly different behaviour android:tag=“sticky“android:tag=“sticky-hastransparancy“ android:tag=“sticky-nonconstant“ and android:tag=“sticky-hastransparancy-nonconstant“.

Advertisements
StickyScrollView for Xamarin Android

Building an Mvvm cross Platform app from scratch with Xamarin

In this post, I am going to show the simplest possible implementation of a cross platform app, that yet has the many of those typical architecture perks in it and shows all the steps of our design process.

This is part one and covers the way up to the implementation of models, services, service-access and unit tests. The complete solution is available on GitHub.

As a simple example, I’ll take an app that displays the Github-Event stream, which looks like this:

overview

The first thing we usually design is the application domain (yes, I am one of those DDD guys). So we write down all the objects we work with, which are in this case: Events (someone commited), Repositories, Commits and of course the Users. They are placed in a portable library that is usually also called something with domain, so we’ll go with GitExplorer.Domain.

public class GitHubEvent
{
    public string Id { get; set; }
    public User Actor { get; set; }
    public GitHubRepository Repository { get; set; }
    public List Commits { get; set; }
}

public class GitHubRepository
{
    public string FullName { get; set; }
}

public class User
{
    public string Name { get; set; }
    public string ProfileImageUrl { get; set; }
}

public class GitHubRepository
{
    public string FullName { get; set; }
}

Yes, this are mostly dump objects and for mobile apps that just display data from a service, that’s as much as they learn in their life… Sad thing, but you will hopefully have spouse with higher ambitions. :)

Now we have our models, we need some services that keep track of them. As services are usually very tightly coupled to models, we usually keep them in the model project too. Services wrap operations on models like getting them from some online service, putting them into a repository or checking into live updates and everything else.

To keep things simple, our GitHubEventService will only obtain the events from the service and keep them in memory.

public class GitHubEventService
{
    private readonly IGitHubClient _client;

    private List<GitHubEvent> _events = new List<GitHubEvent>();

    public GitHubEventService(IGitHubClient client)
    {
        _client = client;
    }

    public async Task<List<GitHubEvent>> GetEventsAsync()
    {
        var items = await _client.DownloadEventsAsync();
        _events = items;
        return _events;
    }

    public List<GitHubEvent>  GetEventsFromMemory()
    {
        return _events;
    }
}

As you can see, the service requires a IGitHubClient to run properly, which looks like this:

public interface IGitHubClient
{
    Task<List<GitHubEvent>> DownloadEventsAsync();
}

Access to services is usually subject to change, as Apis get newer versions, other security measures, ore are completely exchanged against something else.
To be able to change them without reworking the complete application, we move it’s implementation to a new portable library called GitExplorer.Domain.Api.

A part of the Api projects are the DTOs, generated from the Services json response. There’s a neat Function in Visual Studio for this (Edit -> Paste Special -> Paste JSON as classes) or if you are using Xamarin Studio, you can use json2csharp.com to do the same. Usually, we strip away a lot of these auto generated properties, because we’ll never use them. Just makes things more readable.

(Yes, this leads to an exact copy of our model classes, just with some different names, and yes, we could just use the models and add some attributes, but if we need to change anything for the Api access, it doesn’t impact our models. This is usually worth the extra lines for converting between the types.)

In this case, it is a simple HttpClient implementation utilizing Json.Net.

public class GitHubGlobalEventClient : IGitHubClient
{
    public async Task&lt;List&gt; DownloadEventsAsync()
    {
        try
        {
            //var handler = new ModernHttpClient.NativeMessageHandler();
            var client = new HttpClient();

            client.DefaultRequestHeaders.Add("User-Agent", "Awesome 1.0");
            client.DefaultRequestHeaders.Add("Authorization", "Basic YOUR API KEY HERE");

            var result = await client.GetStringAsync("https://api.github.com/events");
            var data = JsonConvert.DeserializeObject&lt;List&gt;(result);

            return data
                .Where(e =&gt; e.type == "PushEvent")
                .Select(eventdto =&gt; new GitHubEvent()
                {
                    Id = eventdto.id,
                    Actor = new User()
                    {
                        Name = eventdto.actor.login,
                        ProfileImageUrl = eventdto.actor.avatar_url
                    },
                    Commits = eventdto.payload.commits?.Select(commitdto =&gt; new Commit()
                    {
                        Sha = commitdto.sha,
                        Comment = commitdto.message
                    }).ToList(),
                    Repository = new GitHubRepository()
                    {
                        FullName = eventdto.repo.name
                    }
                }).ToList();
        }
        catch(Exception e)
        {
            throw new Exception("Something went wrong");
        }
    }
}

Now comes the developers moment of delight: This is actually runnable code.

As there is no app yet, we usually place some tests now, to see if they run correctly.

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public async Task TestApiClientDownload()
    {
        var client = new GitHubOrganisationEventClient();
        var data = await client.DownloadEventsAsync();

        Assert.IsTrue(data.Count > 0);
    }

    [TestMethod]
    public async Task TestService()
    {
        var client = new GitHubOrganisationEventClient();
        var service = new GitHubEventService(client);
        var data = await service.GetEventsAsync();

        Assert.IsTrue(data.Count > 0);
    }
}

Stay tuned for part 2, where we add ViewModels, Android Apps, etc., or check out the complete solution on GitHub.

Building an Mvvm cross Platform app from scratch with Xamarin