Xamarin Forms

Xamarin Forms
Faz todo o sentido escrever uma app numa só linguagem e re-utilizar o máximo de código possível ao porta-la para outras plataformas sendo apenas lógico querer escrever apenas um código base que pudesse ser compilado em apps separadas para as diferentes plataformas e ultimamente nos permitisse atingir o mundo de “Escreve uma vez, Corre em qualquer lado”. Isto parece bem no papel, mas não foi propriamente o caso.

O Problema

O conceito de escrever uma aplicação uma única vez e ser possível corrê-la em várias plataformas não é novo. Os programadores têm tentado conseguir isto por muitos anos com aplicações de servidor e desktop escritas em Java ou C/C++ e compiladas em plataformas específicas. Porque é que as aplicações móveis deveriam ser diferentes? A resposta simples é, não são. Como probramadores de apps móveis, deparamo-nos com os mesmos problemas que outros, no que diz respeito a transversalidade de plataformas, a interface de Utilizador.
Quando se trata de escrever qualquer tipo de software que queremos que possa correr em diferentes plataformas, a parte fácil é a lógica. Com Fácil, quero dizer a parte da aplicação que não é alterada não importando a plataforma alvo. Independentemente da plataforma alvo, iremos precisar ter serviços de data form services, bases de dados, ficheiros, etc. a probabilidade é que isto raramente mudará. A arte que não muda é o UI. Isto é primeiramente devido às diferenças inerentes às várias plataformas. Cada plataforma terá o seu próprio SDK, definindo as capacidades da plataforma bem como os seus componentes e características visuais.
Quando olhamos para a paisagem móvel de hoje, rapidamente percebemos que existem grandes jogadores, iOS, Android e Windows Phone. Este estão em constante competição para tentar superar os outros e ganhar mais fatia de mercado. Mas para um futuro previsível, estas são as plataformas que vamos querer ter como alvo. A razão porque o desenvolvimento Cross-Platform pode ser difícil é devido às diferenças fundamentais em termos de UI. Um botão já não é apenas um botão. Um botão é um UIButton com XML em iOS, um Button com AXML em Android, e um Button com XAML em Windows Phone.

O Difícil

No mundo do C#, esta estratégia irá deixar-nos com pelo menos N+1 projectos dentro da nossa solução. Teremos pelo menos um projecto para cada uma das N plataformas alvo e também teremos de ter pelo menos um projecto que contenha todo o código partilhado do qual a aplicação depende. Dessa forma, poderemos ter os projectos específicos da plataforma simplesmente referenciando o projecto de código partilhado e ter apenas de se preocupar em como visualizar os dados. Graças a algumas funcionalidades nas frameworks .NET e Mono, temos algumas opções.

Todos somos “classy ppl”

As Bibliotecas de Classes portáveis ou PCLs, entraram em cena primeiro. É mais fácil pensar em PCL’s como bibliotecas de classes normais, apenas com funcionalidade limitada. É mais fácil compreender o conceito com um exemplo. Começamos por criar um novo projecto/Solução e nomealo de PCLDemo e seleccionar Class Library (Portable) como template do projecto.

Dependendo das ferramentas que tivermos instaladoas, poderá parecer ligeiramente diferente. Neste ecrã temos a possibilidade de ter como alvo tantas plataformas quanto quisermos. A razão para isto é que quanto mais plataformas quisermos ter como alvo, menos características serão incluídas nesta biblioteca. Para uma lista actualizada das características suportadas pelas diferentes plataformas, visitamos a página Cross Platform Development with the Portable Class Library em MSDN.

Vantagens das Portable Class Libraries
Mesmo existindo algumas coisas que devemos ter em atenção quando usamos PCLs, existem algumas boas vantagens.
1. Codigo especifico-non-platform pode ser isolado num projeto separado.
2. Testes unitários de código partilhado é isolado num projeto separado.
3. Código partilhado na PCL pode ser partilhado com outras aplicações via dll compilado.
Desvantagens das Portable Class Libraries
Quando escolhemos uma nova tecnologia ou plataforma para utilizar, é bom compreender também as desvantagens que precisam ser tidas em conta.
1. Definições de características são limitados com base nas plataformas alvo.
2. PCLs so podem adicionar referencias a outras PCLs que partilhem as mesmas plataformas alvo.
3. PCLs não podem conter qualquer código de plataforma específico.
Se as PCLs parecem muito limitadoras como escolha para código partilhado, existe outra opção.

Entrando nos Xamarin.Forms

Estamos agora cientes das estratégias básicas para partilhar a lógica da nossa app. Está na hora de começara a olhar para o interface de utilizador da nossa app. No lançamento mais recente da versão 3 do Xamarin, uma das características mais entusiasmantes é a introdução da Xamarin.Forms. A Xamarin.Forms é uma biblioteca que contem abstracções de controlos típicos de UI que comprometem os UI’s das nossas apps. Quando estes controlos são utilizados nas plataformas específicas, são mapeados para renderizar controlos nativos de UI para que possamos ter o visual e a sensação de componentes nativos.
A estrutura básica da nossa aplicação cross-platform não muda. Continuaremos a seguir a regra de N+1 no que diz respeito a projectos, mas estaremos agora a mover o código platform-specific da UI dos projectos platform-specific para os Shared Projects. Antes de avançarmos muito, precisamos compreender as necessidades e requisitos não só de correr mas desenvolver as apps Xamarin.Forms.

Criar uma aplicação básica
Para criar uma aplicação básica Xamarin.Forms , abrumos o Visual Studio, selecionamos File > New Project, selecionamos a categoria Mobile Apps, e escolhemos o template apropriado. Escolhi o template Blank App (Xamarin.Forms Shared). Isto irá criar uma aplicação Xamarin.Forms utilizando o template Shared Project.

Depois de clicar o OK, a nossa solução sera criada com quarto projetos. Como falámos antes, teremos três projetos platform-specific e um Shared Project. O Shared Project contém a logica da aplicação bem como todo o codigo Xamarin.Forms que sera encarregado de criar ecrãs (objectos Page) da nossa aplicação.

Se abrirmos o projeto SampleFormsApp, vamos ver uma classe única chamada App.cs. Este é o ficheiro principla do projeto que contem as definições de Page que aparecerão ultimamente nas plataformas alvo. É uma classe simples que apenas contem o seguinte:
public class App
{
public static Page GetMainPage()
{
return new ContentPage
{
Content = new Label {
Text = "Hello, Forms !",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
},
};
}
}

A classe App é relativamente simples pois apenas contem um único metodo estático que devolve um objecto ContentPage .Um objeto ContentPage é o mais básico e sem artifícios Page que podemos criar. É mais facil pensar num ContentPage como uma tela branca com que podemos fazer o que quisermos. Existe muito pouca funcionalidade construída nela.
Iremos aperceber-nos que a maioria dos objetos Page têm uma propriedade Content que nos permite atribuir-lhe um objeto View. Neste caso, é uma visualização de uma label simples que exibe “Hello, Forms !” no ecrã enquanto lhe dá alguma formatação básica para a centrar.
Paara poder carregar esta ContentPage em todas as plataformas alvo, existe algum codigo tipo platform-specific que é necessário incluir em cada objeto das plataformas. Vamos ver primerio o projecto iOS. Abrimos o projeto SampleFormsApp.iOS e vamos ver o ficheiro AppDelegate.cs. Este é o ficheiro que contem a escrita no mundo Xamarin.Forms.

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Forms.Init();

window = new UIWindow(UIScreen.MainScreen.Bounds);

window.RootViewController = App.GetMainPage().CreateViewController();

window.MakeKeyAndVisible();

return true;
}

Primeiro, inicializamos a biblioteca Xamarin.Forms ao invocar Forms.Init(). Depois disso, o único codigo tipo é definir a propriedade window.RootViewController para o resultado da chamada App.GetMainPage().CreateViewController(). Essa linha irá lançar para dentro do mundo de Xamarin.Forms e tudo a partir desse ponto continua a utilizar Xamarin.Forms.

O processo para inicializar Xamarin.Forms em Android e Windows Phone é muito semelhante. Vamos chamar o método Init e definer a Page inicial para ser o resultado do metodo App.GetMainPage(). O aspeto do método MainActivity.OnCreate no projeto SampleFormsApp.Android é o seguinte:

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

Xamarin.Forms.Forms.Init(this, bundle);

SetPage(App.GetMainPage());
}

E no projeto SampleFormsApp.WinPhone no constructor MainPage da classe parcial MainPage:

public MainPage()
{
InitializeComponent();

Forms.Init();
Content = SampleFormsApp.App.GetMainPage().ConvertPageToUIElement(this);
}

Podemos agora ter uma compreensão básica de Xamarin.Forms bem como o que podemos conseguir com a sua utilização.

By apocsantos

Criar Layouts em Android Programaticamente com Xamarin

Quando se trata de projetar e preparer os ecrãs de uma aplicação, existem algumas opções principais, escrevendo código ou usando XAML.

O XAML é o eXtensible Application Markup Language que foi criada para ajudar a definir o aspecto e sensação de uma aplicação sem ter que trata-la toda em código.

É importante notar que o XAML utilizado para Xamarin.Forms não é compatível com outras formas de XAML e ferramentas XAML.

Criar layouts programaticamente:

Se é o tipo de pessoa que adora estar em código e não quer ter nada a ver com qualquer tipo de markup, ou um designer, então provavelmente estará muito confortável com esta opção. Programaticamente instancie diferentes tipos de objectos view e adicione-os diretametne a uma Page ou a um Layout numa Page.

Aqui está um exemplo simples de como criar uma classe SimplePage, instanciando alguns objectos view, e adicionando-os à Page através de um objecto StackLayout.

public class SamplePage : ContentPage {
public SamplePage()
{
Padding = new Thickness(20);

var label = new Label
{
Text = “I am a simple page”,
BackgroundColor = Color.Blue,
Font = Font.SystemFontOfSize(30),
WidthRequest = 150,
HeightRequest = 40
};

var button = new Button {
Text = “I have a button”,
BackgroundColor = Color.Red,
Font = Font.SystemFontOfSize( 20 ),
WidthRequest = 200,
HeightRequest = 200
};

var entry = new Entry {
Placeholder = “I have a entry box”,
BackgroundColor = Color.Green,
WidthRequest = 200,
HeightRequest = 150
};

Content = new StackLayout {
Spacing = 10,
Children = {button, entry, label}
};
}
}

Como podemos ver, os objetos View têm um numero de propriedades em comum, as quais pode utilizar para definir o texto, cores, espaçamento, altura, largura, etc. Tudo o que precisa fazer é modificar o método GetMainPage na classe App para devolver uma nova instância da classe SamplePage, e cá vamos nós.

By apocsantos

Dias uteis (WorkDays) do Excell mas mais completo

Mais um desafio que recentemente tive de superar, num projecto. Engraçado foi o quão simples é a solução e o quão rebuscada a mente consegue ser para a encontrar.

Neste caso uma simples classe em C# permite achar as horas uteis decorridas entre duas datas e horas, sendo estas passadas como objectos Datetime.Outras particularidades da classe é asseitar como parametro os dias feriados (excludeDays), que com pouco código até podem vir de uma tabela de uma BD.

Achei engraçada a facilidade com que se consegue instanciar, fazer os calculos e obter o tempo decorrido com precisão ao segundo.

using System;
using System.Linq;
namespace duteis
{
    public class uteis
    {
        private TimeSpan startingTime;
        private TimeSpan endingTime;
        private DayOfWeek[] excludeDays;
        public uteis(TimeSpan? startingTime, TimeSpan? endingTime, 
                                           DayOfWeek[] excludeDays)
        {
            this.startingTime = startingTime ?? new TimeSpan(8, 30, 0);
            this.endingTime = endingTime ?? new TimeSpan(17, 30, 0);
            this.excludeDays = excludeDays ?? new DayOfWeek[]   
   {   
    DayOfWeek.Saturday ,   
    DayOfWeek.Sunday   
   };
        }
        public uteis()
            : this(null, null, null)
        {
        }
        public double Calculate(DateTime startDate, DateTime endDate)
        {
            var counter = startDate;
            double hours = 0;
            while (counter <= endDate)
            {
                var dayStart = counter.Date.Add(startingTime);
                var dayEnd = counter.Date.Add(endingTime);
                var nextDayStart = startDate.Date.Add(startingTime).AddDays(1);
                if (counter < dayStart)
                    counter = dayStart;
                if (excludeDays == null ||
                  excludeDays.Contains(counter.DayOfWeek) == false)
                {
                    if (endDate < nextDayStart)
                    {
                        var ticks = Math.Min(endDate.Ticks, dayEnd.Ticks) 
                                                         - counter.Ticks;
                        hours = TimeSpan.FromTicks(ticks).TotalHours;
                        break;
                    }
                    else if (counter.Date == startDate.Date)
                    {
                        if (counter >= dayStart && counter <= dayEnd)
                        {
                            hours += (dayEnd - counter).TotalHours;
                        }
                    }
                    else if (counter.Date == endDate.Date &&
                         startDate.Date != endDate.Date)
                    {
                        if (counter >= dayStart && counter <= dayEnd)
                        {
                            hours += (counter - dayStart).TotalHours;
                        }
                        else if (counter > dayEnd)
                        {
                            hours += (endingTime - startingTime).TotalHours;
                        }
                    }
                    else
                    {
                        hours += (endingTime - startingTime).TotalHours;
                    }
                }
                counter = counter.AddDays(1);
                if (counter.Date == endDate.Date)
                    counter = endDate;
            }
            return hours;
        }
    }
}

Atenção a estas linhas onde se define a duração do dia ùtil, e são tratados os “excludeDays” que podem ser os feriados como escrevi anteriormente.

 

this.startingTime = startingTime ?? new TimeSpan(8, 30, 0);  
this.endingTime = endingTime ?? new TimeSpan(17, 30, 0);  
this.excludeDays = excludeDays ?? new DayOfWeek[]  

Espero nos proximos dias implementar a importação dos feriados apartir de uma fonte de dados. Vamos ver o tempo que terei, para pegar nisto que já “é velho” e fazer algo de interessante.

By apocsantos