Introduction

Do you like watching that old TV show so much that you want to take it with yourself wherever you go? And what about silently reading the episodes, instead of listening?

In this article, I present Xamarin TV Scripts, a simple entertainment app for Xamarin Forms. If you are interested, please download the source code and enjoy the show!

Background

This is an app I've been long thinking of doing. Not from scratch, but by porting the concepts I once applied on another platform: Windows Phone.

Long time ago, in 2011, I posted an article here on Code Project, called The Big Bang Transcripts Viewer, in which I explored the wonders of my newly acquired Windows Phone 7 device.

Figure: The Big Bang Transcript Viewer - the good ol' days of Windows Phone

I thought Windows Phone had so much growth potential, and for some time it seemed so. For users, the interface was fast, sleek and unique. The experience was nice, although it always played catch-up with Android and iOS in terms of features. For developers, it offered the convenience of Visual Studio IDE, the .NET Framework and a great emulator. But unfortunately, many manufacturers remained reluctant in embracing and adopting the platform for their "flagship" device models, which were reserved for the Android platform. Also, Windows Phone lacked some of the most critically demanded apps and games, and even after years after its release, WinPhone never got much traction in the US mobile market.

Enter Xamarin Forms

For those migrating from Windows Phone development to cross-platform development, Xamarin (and more specifically Xamarin Forms) is a natural next step.

Xamarin Forms development shares some similarities with Windows Forms, and I've been able to explore those to my advantage; both can be developed with Visual Studio using C# language and the user interface can be designed with XAML files. Both platforms were created with MVVM pattern in mind, which means they both share the concepts of view models and binding. Also, the preferred event notification technique is based on pub/sub messaging.

But of course, the differences between Windows Phone and Xamarin Forms development had to be addressed accordingly. Since Xamarin Forms is a cross-platform framework, one can't simply get access to the device's file system to read or write files, or save/retrieve data to/from the local database. Instead, each platform (Android, IoS, etc.) has its own file system API, database API, and so on. That's why I had to figure out how to invoke the device-level APIs of Xamarin Android from the Xamarin Forms (Net Standard) project.

The Solution

Figure: The .NET Standard and the Xamarin Android projects

The solution is built on Visual Studio 2017 and, like most Xamarin solutions, it contains 2 projects: one for the target device's platform (Android) and the other one is the .NET Standard 2.0 project.

When you create a new Xamarin solution, you are presented with a set of options: Platform, UI Technology and Code Sharing Strategy.

Because developing for iOS requires a Mac OS machine (which I don't have) and Windows (Universal Windows Platform) would only make sense in a Windows Phone, I only wanted an Android app, so I unchecked the iOS and Windows checkboxes.

As for the UI Technology, Xamarin Forms comes with a rich set of components, and I think I would be in trouble if I tried to recreate them using the Android Native UI Technology Android.

Component TypeComponentsXamarin Forms ViewActivityIndicator, BoxView, Button, DatePicker, Editor, Entry, Image, Label, ListView, OpenGLView, Picker, ProgressBar, SearchBar, Slider, Stepper, Switch, TableView, TimePicker, WebView

Xamarin Forms PagesContentPage, TabbedPage, MasterDetailPage, NavigationPage, Carousel, TemplatedPage

Xamarin Forms LayoutAbsoluteLayout, ContentPresenter, ContentView, Frame, Grid, RelativeLayout, ScrollViewer, StackLayout, TemplatedLayout

Xamarin Forms CellsEditorCell, EntryCell, ImageCell, SwitchCell

The .NET Standard project type replaced the old PCL (Portable Class Library) and became the new de facto standard cross-platform project type for .NET projects. That is, you can share a .NET Standard assembly with .NET Framework, .NET Core and Xamarin apps. The same "Xamarin,TVScripts.csproj" .NET Standard project contains not only the Xamarin Forms dependencies, but also all the templates needed for adding new components and assets for your Xamarin Forms project.

We can see in the image above the presence of the Xamarin.Forms.Xaml.dll assembly that comes with the Xamarin.Forms assembly. That assembly gives us the ability to design our user interfaces using XAML (eXtensible Markup Language), which is a declarative type of XML document language developed by Microsoft for user interfaces an other applications.

But when will our Xamarin Forms application start?

Since the Xamarin.TVScripts.Android must be the startup project for the solution, at some point, it will necessarily pass the control of the application to the Xamarin.TVScripts Net Standard code. It does so in the MainActivityclass, which is in fact the application's entrypoint, and more specifically in the OnCreate method.

JavaScript

Copy Code

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity

{

...

protected override void OnCreate(Bundle bundle)

{

TabLayoutResource = Resource.Layout.Tabbar;

ToolbarResource = Resource.Layout.Toolbar;

UserDialogs.Init(this);

base.OnCreate(bundle);

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

LoadApplication(new App());

}

}

Listing: The MainActivity class

The LoadApplication(new App()); code will of course load and run the application, which resides in the other (.NET Standard) application.

Now we can talk about the App class.

The App class is in the App.Xaml.csfile, which is the code-behind file for the App.Xaml. Despite having the xaml extension, this file does not represent a view, but rather the Application class in XAML language.

Back to the App.Xaml.cs file: it implements the Xamarin.Forms.pplicationsuperclass. When the application starts, it must define the MainPage property of the App class. Then Xamarin Forms will initiate the navigation flow through our custom XAML pages. The code below instantiates a new NavigationPage, starting at the HomePage:

JavaScript

Copy Code

public partial class App : Application

{

public App ()

{

InitializeComponent();

MainPage = new NavigationPage(new HomePage());

}

}

Listing: The App class

The NavigationPage is a Xamarin component that will handle the navigation and user-experience of a stack made of our custom pages. This "stack" holds the navigation history and, just like in a web browser, allows us to navigate back and forth across our views.

User Interface

We have a user interface composed of 3 views: the HomePage, the SeasonPageand the EpisodePage.

The HomePagejust shows the TV show cover image, along with a ListView containing the show seasons. The ListViewis the Xamarin component and works as a view for presenting lists of data, especially long lists that require scrolling.

Figure: Cover image and ListView from HomePage.xaml

The ListViewcan be used as a simple list of unformatted text or, like in our case, you can customize it to display a set of styled items. You can replace the default plain text presentation of the ListViewby providing the ListView.ItemTemplate property with the desired layout, represented by any arrange of various Xamarin components:

Figure: building the ListView's ItemTemplate

Since the season image is square, we can apply a little trick by using a image mask in order to transform it into a circular icon:

Figure: Applying a circular mask on top of an image

Similarly, the SeasonPageshows a simple list of episodes, and it does not have much styling. Each episode name is defined by a TextBlockcontained in a round-cornered Framecomponent.

Figure: The SeasonPage.xaml view featuring the list of episodes of Season 2

The EpisodePage, on the other hand, is more carefully designed. It is by far the most important view, where users will spend most of the time in the app. This view holds similarities with the other views, because of the styled ListViews displaying a collection of data (the TV Show's quotes), but it does so in a very customized way. Each quote can be shown in one of these three styles:

commentary (without characters)

character speaking at the left

character speaking at the right

An episode commentary is displayed when no character name is found in the associated Quote instance. Otherwise, the character's picture will be displayed at one side, and the speech bubble will be shown at the other. The character's photo will be displayed at the left or right, depending on whether the quote number is even or odd. This way we can easily alternated between those 2 modes.

The character's pictures are retrieved by binding the character's name to the corresponding image residing in the "drawable" folder of the Droid project.

The speech bubble is composed by two parts: a TextBlock contained within a rounded-cornered Frame, and an Image of a triangle immediately connected to it. This gives us the impression of a speech bubble.

Figure: The EpisodePage.xaml view displaying the episode's conversation

In the image below, we can identify the 3 different types of "cards". The first one is called Narration Card, and usually represents a scene description, not spoken by a character. The other two are called OddCards and Even Cards, which are scripts spoken by characters and are displayed in an alternated way.

Figure: The 3 types of speeches in EpisodePage.xaml view

Each Odd/Even Card has its layout defined by a 2-column Grid to separate image from the speech bubble. Since the image column only takes 80 pixels, the speech bubble takes the rest of the space within the Grid. And it looks nice even when the device screen is rotated in landscape mode.

Figure: Anatomy of a speech card

The speech bubble is tricky. At first, I tried to implement some sort of vectorized drawing, but unfortunately that was not possible with Xamarin. Then I ended up combining a round-shaped frame along with an image containing a triangle, and it looked nice.

Figure: XAML code inside a speech bubble

Bindings

Xamarin Forms is all based on the MVVM (Model-View-ViewModel) pattern, so you should expect the extensive use of bindings. Since MVVM decouples the View layer from the data it shows, the bindingsact like a gluethat tie view and its data together dynamically, so that each change in data should be reflected on the view side.

In the fragment below, we can notice 3 bindings:

The label's Textproperty is bound to the Name property of the Seasonobject

The label's Styleproperty is bound to the ListItemTextStylevalue

The label's TextColorproperty is bound to the ListFontColorvalue

JavaScript

Copy Code

LineBreakMode="NoWrap"

TextColor="{DynamicResource ListFontColor}"

Style="{DynamicResource ListItemTextStyle}"

FontSize="16"

VerticalOptions="Center"

VerticalTextAlignment="Center"

HorizontalOptions="StartAndExpand"/>

Listing: the Label component inside a ListView inside the HomePage.xaml file

As mentioned, the data from the Name property comes from the Season class:

JavaScript

Copy Code

[Table("Seasons")]

public class Season : BaseModel

{

public Season() { }

public Season(int seasonNumber, string name)

{

SeasonNumber = seasonNumber;

Name = name;

}

public int SeasonNumber { get; set; }

public string Name { get; set; }

[Ignore]

public string ImageSource => $@"_season{SeasonNumber}.jpg";

}

Listing: The Season class from our model

The TextColor, on the other hand, does not come from the Season class, but is rather provided by a value included in the ResourceDictionaryin the App.xaml file:

JavaScript

Copy Code

...

#FFFFFF

...

Listing: the App.xaml file

This happens because we explicitly defined this binding as a DynamicResource (TextColor="{DynamicResource ListFontColor}").

Files

The app data consists of 3 types of files: the seasons file, the episodesfile and the quotes files (each episode has its own separated quotes file).

Copy Code

1 Season 1

2 Season 2

3 Season 3

4 Season 4

. .

. .

. .

Listing: The seasons.txt file

Copy Code

. . .

. . .

. . .

2 23 The One With The Chicken Pox

2 24 The One With Barry and Mindy's Wedding

3 1 The One With The Princess Leia Fantasy

3 2 The One Where No One's Ready

3 3 The One With All The Jam

. . .

. . .

. . .

Listing: The episodes.txt file

The seasons.txt and the episodes.txt are plain text files that merely display the names of the seasons and the episodes.

Copy Code

2 [Scene Chandler's Office, Chandler is on a coffee break. Shelley enters.) Shelley

3 Chandler Dehydrated Japanese noodles under fluorescent lights... does it get better than this?

4 Shelley Question. You're not dating anybody, are you,

because I met somebody who would be perfect for you.

5 Chandler Ah, y'see, perfect might be a problem. Had you said 'co-dependent',

or 'self-destructive'...

6 Shelley Do you want a date Saturday?

7 Chandler Yes please.

Listing: The 0108.txt file (Season 01, Episode 08)

The quotes files, on the other hand, contains 3 columns: the quote sequence number, the character who is speaking, and the speech (quote) itself.

Since plain text files can hold a large amount of unstructured data, they are often hard to query. And if we could transfer their data to a local database residing in our device, it would be easier to query and fetch the data, as we want.

Local SQLite Database

Among local relational databases, SQLitestands out as a stable and reliable candidate. We can generate a local SQLite database using the code-first approach, that is, by first implementing the entities (season, episode, quote) as C# classes, and then using them as the source for the SQLite database schema.

The plain text files are accessed in the Xamarin Droid project., because the implementation depends upon the platform's file system API. So we place the file access methods directly in the Main Activity class, which contains the assets object from which the files can be accessed. Just like file access, the data base access is also implemented in the Droid project.

Following Xamarin Documentation Guide, it was quite easy to implement a class in the Xamarin.TVScripts.Android project to retrieve the SQLite Connection:

C#

Copy Code

[assembly: Xamarin.Forms.Dependency(typeof(SQLite_android))]

namespace Xamarin.TVScripts.Droid

{

class SQLite_android : ISQLite

{

private const string dbFileName = "TVScripts.db3";

public SQLiteConnection GetConnection()

{

string dbPath = Path.Combine(

System.Environment

.GetFolderPath(System.Environment.SpecialFolder.Personal),

dbFileName);

return new SQLite.SQLiteConnection(dbPath);

}

}

}

Listing: The SQLite_android class

Notice that the SQLite_androidclass implements the ISQLite interface (class SQLite_android : ISQLite), and that we use the annotation [assembly: Xamarin.Forms.Dependency(typeof(SQLite_android))] just above the namespace. What does that mean? Both the concrete class name and the interface are intended to be used for the built-in Dependency Injection Service of Xamarin Forms when the database functionalities are invoked from the other project. This is particularly useful because the Xamarin.TVScripts project does not reference Xamarin.TVScripts.Androidproject where the SQLite_androidclass resides (and it would never would, since this would result in a cross-reference error).

So, from where does exactly the SQLite_android.GetConnection()is called?

The Xamarin.TVScripts project has a set of Data Access Object classes that implement basic CRUD (Create, Read, Update, Delete) methods for the SQLite database. These classes correspond to the model classes of our model (Season, Episode and Quote) and they inherit from a common superclass called BaseDAO. Whenever the data access classes need to access the database, they first obtain the connection through the GetConnection method in the base class:

C#

Copy Code

public class BaseDAO where T : BaseModel, new()

{

public BaseDAO()

{

using (SQLiteConnection connection = GetConnection())

{

connection.CreateTable();

}

}

public IList GetList()

{

using (SQLiteConnection connection = GetConnection())

{

return new List(connection.Table());

}

}

.

.

.

protected SQLite.SQLiteConnection GetConnection()

{

return DependencyService.Get().GetConnection();

}

}

Listing: The BaseDAO class

Notice how the BaseDAO is a generic class that imposes a constraint for the T type. This improves code reusability, because we can put in the base class many methods that would otherwise become different methods in the derived classes, varying only in terms of the model entity being manipulated.

Now, let's take a look at the GetConnection() method:

C#

Copy Code

protected SQLite.SQLiteConnection GetConnection()

{

return DependencyService.Get().GetConnection();

}

Listing: The BaseDAO.GetConnection method

Here, we can see how we obtain the ISQLite implementation using the DependencyServiceclass. The Get() method will make the Xamarin framework to look for a class that implements the interface and return a new instance from it.

Text-to-Speech

The app also offers a features for reading the speech bubbles text, using the features provided by the Text-To-Speech app available at Android Playstore.

C#

Copy Code

[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.TVScripts.Droid.TextToSpeech))]

namespace Xamarin.TVScripts.Droid

{

public class TextToSpeech : Java.Lang.Object, ITextToSpeech,

global::Android.Speech.Tts.TextToSpeech.IOnInitListener

{

global::Android.Speech.Tts.TextToSpeech speaker;

string toSpeak;

public void Speak(string text)

{

toSpeak = text;

if (speaker == null)

{

speaker = new global::Android.Speech.Tts.TextToSpeech(Application.Context, this);

}

else

{

speaker.Speak(toSpeak, QueueMode.Add, null, null);

}

}

public void OnInit(OperationResult status)

{

if (status.Equals(OperationResult.Success))

{

speaker.Speak(toSpeak, QueueMode.Add, null, null);

}

}

}

}

Listing: The TextSpeech class

Just like the SQLite_android class, the TextToSpeech is registered in the Xamarin Dependency Injection Container through the use of the Xamarin.Forms.DependencyAttribute annotation.

When a speech bubble is tapped, the ListView will raise an OnItemSelected event, which in turn will invoke the text-to-speech functionality to speak both the character's name and his/her speech.

C#

Copy Code

async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)

{

var quote = args.SelectedItem as Quote;

if (quote == null)

return;

await Speak(quote.Character);

await Speak(quote.Speech);

}

private async Task Speak(string text)

{

await Task.Run(() =>

{

DependencyService.Get().Speak(text);

});

}

Listing: The client code for the text-to-speech functionality in the EpisodePage.xaml.cs file

This showcases how you could leverage your app's capabilities (and also extend them) in terms of helping people with reading disabilities (both blindness and dyslexia). And you can obtain these results without having to develop a complex and secondary feature in your application. Now you can save that time to develop the critical code for your app.

Conclusion

If you have reached this line, thank you very much for your attention and patience. I really think Xamarin Forms makes Android development a lot of fun for Visual Studio/C# developers, not only for serious applications, but also for taking crazy ideas like mine and make them happen. I hope some of you get inspired to try something new.

So, do you like the article and the code? I'm looking forward to seeing your thoughts. Please give me feedback in the comments section below!

History

2018-03-31: First version

xamarin android tv,Xamarin TV Scripts相关推荐

  1. xamarin android 布局,Xamarin.Android 上中下布局

    xml代码: xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_par ...

  2. xamarin android框架,Xamarin使用的架构

    Xamarin使用的架构 What framework is used by Xamarin? Xamarin使用的架构? Solution 1解决方法1 Xamarin has "port ...

  3. xamarin android 书,xamarin android 开发

    开始环境vs2017 直接创建android 项目,左边是android studio 的项目目录  右边是vs创建的android 项目目录 结构基本相同,有res对应的Resources文件 加载 ...

  4. xamarin android密码,Xamarin.Forms学习历程(七)——用户偏好设置存储

    经常会遇到要存储一下用户账号密码之类的,让用户下次登录时不需要重新输入账号密码,直接进入主界面.Xamarin.Forms里没有自己的解决方案,还是得调用iOS和Android原生的API才可以实现. ...

  5. xamarin android pdf,Xamarin.Android - 下载pdf和视频到应用空间并打开

    1. 下载创建本地应用内文件时,创建模式必须为 FileCreationMode.WorldReadable Stream fos = OpenFileOutput(pdfName, FileCrea ...

  6. xamarin android 邮件,Xamarin Android:通过标准API(电子邮件,脸谱等)分享图像

    我认为应用程序图标是在您的应用程序专用的目录中创建的,因此其他应用程序无法获取它. 您需要将其保存在其他应用程序可以访问的位置,然后从该位置共享它,如下所示: public void Share (s ...

  7. Xamarin.Android使用教程之Android开发所需的模拟器

    2019独角兽企业重金招聘Python工程师标准>>> 如今,在一个模拟器中运行Android应用程序时有很多种选择,今天,我们将为大家介绍当使用Xamarin开发Android应用 ...

  8. html链接phpayadmain数据库,无法从Xamain.iOs和Xamarin.Android使用C#连接到MySql

    在Mac上构建使用xamarin studio的ios和android的mob应用程序,我想连接到mysql数据库.所以香港专业教育学院从下面的链接下载MySQL的连接器:http://dev.mys ...

  9. Xamarin.Android之封装个简单的网络请求类

    http://doc.okbase.net/catcher1994/archive/220195.html Catcher8 2016/4/23 0:28:50 阅读(72) 评论(0) 一.前言 回 ...

最新文章

  1. 如何构建一个理想UI代码表达的自动化工具?
  2. 修改BeEF工具默认密码
  3. Riak VClock
  4. Spring@Cacheable注解在类内部调用失效的问题
  5. k-Nearest Neighbors 实战1 简单的电影分类实例
  6. 表单字段三维数组名_【技术汇】回转式空气预热器温度场三维数值模拟
  7. Zurmo - - 全局配置
  8. 1052 卖个萌 (20 分)
  9. 通过set方式注入的bean为null_Spring 注入集合
  10. AppSettings
  11. 怎么使用PVS stream Linux
  12. leaftlet 显示个性化图标、旋转图标
  13. 友善之臂mini2440使用日志1
  14. 九九乘法表居中c语言,excel图文教程:九九乘法表的制作方法,你会哪种?
  15. MySQL表的增删改查--你都知道吗?
  16. 永恒之蓝 ms17_010漏洞
  17. 项目总结“遇见江小丽”
  18. 2023四川大学图书情报档案专业考研初试介绍(2023.1.02已更新)
  19. 自学 iOS 开发的一些经验 - 转自无网不剩的博客
  20. Java使用spire进行word文档的替换

热门文章

  1. java cloneable 接口_Java8之Cloneable接口
  2. 数字签名技术以及RSA算法的原理实现
  3. oracle将字段nullable设为Y,Oracle10g中约束与列属性NULLABLE的关系
  4. 执行 Workgroup Manager 维护任务
  5. Work20230601
  6. L2接口提供的都是实时股票行情吗?
  7. 常见的pc开机报警声音
  8. Framework not found Pods_xxxxx
  9. 【印象笔记】Mac快捷键大全
  10. 百度CTO王海峰做客《中国经济大讲堂》:文心一言,读书破万亿