Search

Showing posts with label Xamarin. Show all posts
Showing posts with label Xamarin. Show all posts

The Repository Pattern with Xamarin Forms and SQLite

Wednesday, 20 April 2016

The official Xamarin Forms site explains how to use SQLite with a cross platform application. In this post we go a step further and use the repository pattern and dependency injection to allow for unit testing. Here's the source code for the sample app.

The App

The example app is a simple screen which lists the months of the year. It looks like this...



...and here's the code...
public class HomePage : ContentPage
{
 public HomePage ()
 {
  // Set up the repository.
  var sqlProvider = DependencyService.Get <isqlprovider> ();
  var repo = new SQLRepository (sqlProvider);
  repo.Init ();

  // Set up the controller factory and controller.
  var controllerFactory = new ControllerFactory (repo);
  var controller = controllerFactory.GetDemoController ();

  // Set up the list.
  var listView = new ListView
  {
   RowHeight = 40
  };

  listView.ItemsSource = controller.GetFirstQuarter ();
 
  Content = new StackLayout {
   VerticalOptions = LayoutOptions.FillAndExpand,
   Children = { listView }
  };
 }
}
This is simple. We use the DependencyService to get a reference to the SQLite provider (which supplies the platform specific SQLite connection). We initialise the repository. The repository is passed to the controller factory which supplies a controller. Finally we setup the page.

Platform Specific Code

Each platform needs to implement it's own ISQLProvider. This sets up and opens a SQLite connection. Here's the Android version...
public class SQLite_Android : ISQLProvider {
 public SQLite_Android () {}
 public SQLite.SQLiteConnection GetConnection () {
  var sqliteFilename = "DemoSQLite.db3";
  string documentsPath = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal); // Documents folder
  var path = Path.Combine(documentsPath, sqliteFilename);

  File.Delete (path);

  // Create the connection
  var conn = new SQLite.SQLiteConnection(path);
  // Return the database connection
  return conn;
 }}

The Core Project and Dependency Injection

The core project is where the code to control the app lives. It's separated out from the UI layer which means we can unit test it. The key classes here are...
{
 private StandardKernel kernel;

 public ControllerFactory (IRepository repository)
 {
  kernel = new StandardKernel ();
  kernel.Bind<irepository> ().ToConstant (repository);
 }

 public DemoController GetDemoController()
 {
  return kernel.Get<democontroller> ();
 }
}
...the ControllerFactory - Responsible for building the controllers for the app. In the demo we are using dependency injection with Ninject to supply the repository to the controller. 
public class SQLRepository : IRepository
{
 SQLiteConnection connection;

 public SQLRepository (ISQLProvider provider)
 {
  this.connection = provider.GetConnection();
 }

 public void Init()
 {
  this.connection.CreateTable<month> ();
  this.connection.Insert(new Month(12, "December"));
  this.connection.Insert(new Month(11, "November"));
  this.connection.Insert(new Month(10, "October"));
  this.connection.Insert(new Month(9, "September"));
  this.connection.Insert(new Month(8, "August"));
  this.connection.Insert(new Month(7, "July"));
  this.connection.Insert(new Month(6, "June"));
  this.connection.Insert(new Month(5, "May"));
  this.connection.Insert(new Month(4, "April"));
  this.connection.Insert(new Month(3, "March"));
  this.connection.Insert(new Month(2, "February"));
  this.connection.Insert(new Month(1, "January"));
 }

 public List<month> GetMonths ()
 {
  return this.connection.Table<month> ().ToList ();
 }
}
...the SQLRepository - This is a simple demo so all the repository does is provide a list of months. Months are set up when the repository initialises.
public class DemoController
{
 private IRepository repo;

 public DemoController (IRepository repo)
 {
  this.repo = repo;
 }

 public List<month> GetFirstQuarter()
 {
  return this.repo.GetMonths ().Where (x =&gt; x.Number &lt;= 3).ToList ();
 }
}
...the DemoController - Again this is very simple in the demo app. It sorts the list from the repository and passes it back.

The Unit Test Project

The unit test project doesn't use SQLite. Instead it has its own repository which uses an in memory list. We just have one sample test here.
public class DemoController
[TestFixture ()]
public class Test
{
 [Test ()]
 public void TestCase ()
 {
  var repository = new TestRepository ();
  repository.Months.Add (new Month (12, "December"));
  repository.Months.Add (new Month (11, "November"));
  repository.Months.Add (new Month (10, "October"));
  repository.Months.Add (new Month (9, "September"));
  repository.Months.Add (new Month (8, "August"));
  repository.Months.Add (new Month (7, "July"));
  repository.Months.Add (new Month (6, "June"));
  repository.Months.Add (new Month (5, "May"));
  repository.Months.Add (new Month (4, "April"));
  repository.Months.Add (new Month (3, "March"));
  repository.Months.Add (new Month (2, "February"));
  repository.Months.Add (new Month (1, "January"));

  var controllerFactory = new ControllerFactory (repository);
  var controller = controllerFactory.GetDemoController ();
  var months = controller.GetFirstQuarter ();

  Assert.AreEqual ("February", months [1].Name);
 }
}

Project Setup & Package References

To clarify the project structure and required references we have...

The Xamarin Forms project - This provides the UI. It has to reference the sqlite.net pcl so it can pass the connection from the platform specific project to the core project.

The platform specific projects - these need to reference Ninject and sqlite.net. The platform specific versions are provided by the package dependency.

The core project - again this needs to reference Ninject and sqlite.net but this time it will use the cross compatible versions.

The unit test project - This one just needs the Ninject reference as it doesn't use sqlite.net.

The key thing to remember with all these package references is that the platform specific version will be provided; A reference to sqlite-net-pcl in an IOS project will provide the IOS version, in Android it will provide the Android version.

If you are missing the reference to Ninject in the platform specific project you will get a 'System.NotImplemented' exception.

If you are missing the SQLite reference in the platform specific project you will get the error 'Something went wrong in the build configuration.  This is the bait assembly, which is for referencing by portable libraries, and should never end up part of the app.  Reference the appropriate platform assembly instead.'

If you are getting either error with the packages in place try cleaning the solution or uninstalling the app from your device.

Xamarin Android - ViewPager & PageTranformers

Monday, 27 October 2014

This post looks at how to use a ViewPager to let users swipe between fragments and how to make some cool animations during the swipe. The source code for these examples is available here.

ViewPager Basics

To set up a basic view pager we need:
  • A Layout with a ViewPager widget.
  • Some fragments to use as pages.
  • A PagerAdapter which managers the fragments.
  • An Activity to tie everything together.
The layout for the ViewPager is really simple - it just has a ViewPager in it:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
In this example the page fragments are simple as well, the layout is contains a TextView and the fragment class just picks up the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:minWidth="25px"
    android:minHeight="25px"
    android:background="@drawable/Fragment">
    <TextView
        android:text="textView1"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView1"
        android:textColor="#ff000000" />
</LinearLayout>
public class PagerFragment : Fragment
{
    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.Inflate(Resource.Layout.DemoFragment, container, false);
        return view;
    }
}
We need a PagerAdapter to manage the fragments, this is simple in the demo but would be more complicated in real life.
internal class DemoPagerAdapter : Android.Support.V13.App.FragmentStatePagerAdapter
{
    private const int PageCount = 5;

    public DemoPagerAdapter(FragmentManager fm) : base(fm)
    {
    }

    public override Fragment GetItem(int position)
    {
        return new PagerFragment();
    }

    public override int Count
    {
        get { return PageCount; }
    }
}
Finally we need an activity to tie it all together. The activity uses the view pager layout and ties it up to the pager adapter.
[Activity(Label = "PagerDemo", Icon = "@drawable/icon")]
public class PagerActivity : Activity
{
    private ViewPager viewPager;
    private PagerAdapter pagerAdapter;

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

        this.SetContentView(Resource.Layout.PagerActivity);
        
        pagerAdapter = new DemoPagerAdapter(this.FragmentManager);

        viewPager = this.FindViewById(Resource.Id.pager);
        viewPager.Adapter = pagerAdapter;
    }
}
After putting it all together you get an activity which looks like this. This is great but now we'll look at making the transitions between the pagers much cooler.


Animations with Page Transformers

To spice up the animation between the fragments we need a class that implements ViewPager.IPageTransformer. This will let us customise the animation as the pages change in pretty much any way we like. We’ll also need to set the page transformer against the view pager using SetPageTransformer. The interface has a single method called TransformPage which accepts the view from the pages fragment and a float called position. The position variable will change from -1 to 1 as the page changes. -1 is off screen to the right, 1 is off screen to the left, and 0 means that the page is fully in the centre of the screen.

First of all we’ll fade the fragments out as they go off screen, this is what the FadeTransformer class looks like when it’s running:



Here’s the code for the transformer. You can see it calculates the alpha value and then apply it to the view. We also need to set the alpha value to 0 when the view is fully of screen (positions 1 and -1).
/// <summary>
/// This FadeTransformer fades fragments in and out as they are swiped.
/// </summary>
public class FadeTransformer : Java.Lang.Object, ViewPager.IPageTransformer
{
 private float MinAlpha = 0.3f; // Minimum alpha value.

 public void TransformPage(View view, float position)
 {
  if (position < -1 || position > 1) 
  {
   view.Alpha = 0; // The view is offscreen.
  } 
  else 
  {
   float scale = 1 - Math.Abs (position); // The scale should be 1 at position 0, and 0 at positions 1 and -1
   float alpha = MinAlpha + (1 - MinAlpha) * scale; // Calculate the alpha value
   view.Alpha = alpha; // Apply the value to the view

   view.FindViewById<TextView> (Resource.Id.textView1).Text = string.Format ("Position: {0}\r\nAlpha: {1}", position, alpha);
  }
 }
}
Here’s another page transformer - this time we’ll shrink the views as they go offscreen:



And here’s the code. Calculating the scale is fairly simple. We also need to set the views TranslateX property, this keeps the edges of the old and new pages nice and close together:
/// <summary>
/// This ScaleTransformer zooms fragments in and out as they are swiped.
/// </summary>
public class ScaleTransformer : Java.Lang.Object, ViewPager.IPageTransformer
{
 private float MinScale = 0.5f; // Minimum scale value.

 public void TransformPage(View view, float position)
 {
  if (position < -1 || position > 1) 
  {
   view.Alpha = 0; // The view is offscreen.
  } 
  else 
  {
   view.Alpha = 1;

   // Scale the view.
   float scale = 1 - Math.Abs (position) * (1 - MinScale);
   view.ScaleX = scale;
   view.ScaleY = scale;

   // Set the X Translation to keep the views close together.
   float xMargin = view.Width * (1 - scale) / 2;

   if (position < 0) 
   {
    view.TranslationX = xMargin  / 2;
   } 
   else 
   {
    view.TranslationX = -xMargin  / 2;
   }

   view.FindViewById<TextView> (Resource.Id.textView1).Text = string.Format ("Position: {0}\r\nScale: {1}", position, scale);
  }
 }
}
In Android you can apply 3d effects to views easily. Here’s a page transformer that has a 3d ‘wheel’ effect.



Here’s the code, we need to set the pivots for the rotation - the y pivot is half way down the view and the x pivot can be on either edge. The x pivots should be on adjacent edges of the old and new views. The rotation values is calculated by multiplying the position by the maximum rotation value.
internal class WheelPageTransformer : Java.Lang.Object, ViewPager.IPageTransformer
{
    private const float MaxAngle = 30F;

    public void TransformPage(View view, float position)
    {
  if (position < -1 || position > 1) 
  {
   view.Alpha = 0; // The view is offscreen.
  } 
  else 
        {
   view.Alpha = 1; 

   view.PivotY = view.Height / 2; // The Y Pivot is halfway down the view.

   // The X pivots need to be on adjacent sides.
            if (position < 0)
            {
                view.PivotX = view.Width;
            }
            else
            {
                view.PivotX = 0;
            }

            view.RotationY = MaxAngle * position; // Rotate the view.

   view.FindViewById<TextView> (Resource.Id.textView1).Text = string.Format ("Position: {0}\r\nPivotX: {1}\r\nRotationY {2}", position, view.PivotX, view.RotationY);
        }
    }
}
Here’s the final transformer, in this one pages from the right of the screen are treated normally but pages on the left sink into the background.



This transformer is slightly different to the other as the pages are drawn in the opposite order so that the z-indexs are correct. We control this in the call to SetPageTransformer. The other difference is that the transformer only really does anything when the pages are to the left of the screen.
internal class SinkAndSlideTransformer : Java.Lang.Object, ViewPager.IPageTransformer
{
    public void TransformPage(View view, float position)
    {
  if (position < -1 || position > 1) 
  {
   view.Alpha = 0; // The view is offscreen.
  } 
  else 
  {
   view.Alpha = 1;

   if (position < 0) 
   {
    // 'Sink' the view if it's to the left.
    // Scale the view.
    view.ScaleX = 1 - Math.Abs (position);
    view.ScaleY = 1 - Math.Abs (position);

    // Set the translationX to keep the view in the middle.
    view.TranslationX = view.Width * Math.Abs (position);
   } 
  }
    }
}

Android–Bluetooth Barcode Scanners & The Soft Keyboard

Wednesday, 22 October 2014

Bluetooth barcode scanners are a great alternative to barcode apps that use the camera. The only downside is that because they work as bluetooth keyboards they can stop the soft keyboard from appearing. This means you have to turn the scanner off if you want to type anything in. Here’s a way to get the scanner and the soft keyboard working together…
  1. Pair the scanner and to the device.
  2. Go to Settings.
  3. Go to Language & Input.
  4. Under ‘Keyboard & Input Methods’ click on ‘Default’.
  5. Turn ‘Hardware/Physical Keyboard’ Off.
Screenshot_2014-10-22-12-01-02
Now the scanner and the soft keyboard will work together. The downside to this method is that you have to do it each item the scanner is paired with the device. I’m investigating solutions to that so watch this space…