65.9K
CodeProject 正在变化。 阅读更多。
Home

Android - 股票行情监测 (COINS) C# 版, 使用 Visual Studio 2010

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (102投票s)

2012 年 9 月 6 日

CPOL

23分钟阅读

viewsIcon

288025

downloadIcon

10203

本文介绍了 C# 在构建专业应用程序方面的强大功能——使用 Mono C# 在 Android 平台上构建带图表的股票市场观察应用。

Main Screenshots

修订历史

2012 年 9 月 13 日重大修订 - 从股票详情活动中添加股票图表(新功能)

引言

欢迎来到 COINS Mobile - 全球交易所市场观察

"COINS Mobile" 允许您查看股票市场数据。它展示了正确的应用程序架构分层,并为业务层和数据层使用了一个通用的代码库。然后将用户界面和应用程序层分离到相应的设备应用程序中。

本文讨论了 C# 在为 Android 构建专业应用程序方面的强大功能。撰写本文的目的是让您了解如何使用 Visual Studio 2010 和 C# 进行开发,就像开发 Windows 应用程序一样。所有功劳归 Xamarin 所有,它为 .NET 开发人员提供了如此出色的平台来在 Android 上构建应用程序。

作为一名 .NET 开发人员(尽管我几年前就开始构建 Android 应用程序),我开始使用 Java/Davlink 和 Eclipse Java。经过一点努力,几周内我用 Java 构建了一个示例应用程序,但在使用 Web 方法(.NET Web 服务)时遇到了一些问题。我不得不使用 Android ksoap(Android 平台的轻量级 SOAP 客户端库),这并不是很直接。此外,要理解和熟悉 Java 语言和 Eclipse IDE(特别是对于 .NET 开发人员)仍然存在学习曲线。因此,我开始评估使用 C# 作为 Android 基础语言的 Xamarin Mono。

本文无意全面讨论每个功能,但我会尽力分解整个应用程序,并逐步详细说明如何在 C# 中构建专业的 Android 应用程序。我相信您将能够利用您现有的 C# 和 Visual Studio 知识,快速构建专业的 Android 应用程序。

背景  

这个应用程序基于“股票市场观察”——从大多数交易所获取实时股票报价。“COINSMobile”面向全球数百万迫切需要手头系统来查看股票详情(而且还是一个漂亮的 Android 应用程序)的股票交易员。

我把它命名为“COINS”。我将在文章描述的几个地方使用“COINS”这个词,它实际上指的是应用程序本身。

COINS 的作用

它通过 Web 服务获取实时股票数据,并显示股票详情(可以在一个屏幕上查看多种股票数据)。它还提供每个股票的进一步向下钻取、详细视图和图表分析。用户可以创建自己的自定义股票脚本并查看他们添加的股票详情。

本文涵盖

  • 使用 C# 和 Visual Studio 开发 Android 应用程序 
  • 理解活动和布局 
  • 连接到 Web 服务
  • 使用 SQLite 数据库进行本地数据存储
  • 构建专业 Android 应用程序的分步指南
  • 在线程中运行方法
  • 交互式布局设计 
  • 在 Android 中创建动作栏 - 菜单 
  • 在模拟器中运行和测试应用程序 
  • 构建专业应用程序“COINS - 全球交易所市场观察”

如何开始?  

本文专为使用 C# 的开发人员编写。无论他们是在 Windows、Web、Silverlight、WPF 还是 Windows 平板电脑上工作,这都无关紧要。您需要了解 C# 并熟悉 Visual Studio。

因此,您需要两件事才能开始用 C# 开发 Android 应用程序

如果您没有 Visual Studio,仍然可以使用 SDK 附带的 Mono 开发环境。

创建“COINSMobile”项目

正如我之前提到的,本文也面向初学者,所以我认为我会提供一个关于如何在 Visual Studio 中创建 Android 项目的快速指南。如果您已经对使用 Mono C# for Android 有了很好的了解,可以跳过此步骤。

Mono Android 的下载和安装最多需要 25-40 分钟,具体取决于您的互联网速度。SDK 安装完成后,打开您的 Visual Studio。

单击文件>新建>项目

您将看到以下屏幕。在左侧窗格中,选择“Mono for Android”。然后选择“Android 应用程序”。提供文件名,然后单击“确定”按钮。

现在您可以看到第一个屏幕,其中包含一些生成的 C# 代码。如您所见,所有内容都非常熟悉,例如命名空间、类以及生成的代码本身。

您看到“using namespace”的最顶部部分是您将使用的默认命名空间。如果您想执行不在引用列表中的特殊任务,则需要使用解决方案资源管理器中的“添加引用”来添加组件(与我们在 C#/VB.NET 中编码时所做的一样)。例如,如果您想对 SQLite(轻量级数据库)进行操作,则需要添加对“Mono.Data.Sqlite”的引用。

我希望在代码中强调的一点是“OnCreate”重写方法

public class MainActivity : Activity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        StartActivity(typeof(MarketWatchActivity));
    }
}

OnCreate() 是什么?

当活动首次创建时调用此方法。您应该在此处执行所有常规的静态设置:创建视图、将数据绑定到列表等。此方法还为您提供一个 Bundle,其中包含活动之前冻结的状态。

哎呀!活动和视图——它们是什么?我稍后会解释这些。让我们看看活动生命周期,它类似于 ASP.NET 页面生命周期。

暂时,可以将 Activity 视为 Windows Form 或 ASP.NET 页面。  

Activity Life Cycle 

如果我们将 Activity 与网页进行比较,Activity 类似于 ASP.NET 中网页的“代码隐藏”。因此,就像网页一样,我们在 C# 中编写代码隐藏,而 Activity 是 Layout 的代码隐藏。

布局是一个名为 AXML 的 XML,就像在 WPF 中我们称之为 XAML 一样。如果您有使用 WPF 或 Silverlight 的经验,那么理解 AXML 布局会更容易。 

应用程序从一个 Activity 开始,该 Activity 通常有一个用于显示 Activity 的布局。它清楚地定义了模型-视图-控制器 (MVC) 模式。布局是我们 Android 中看到的表单(或页面,或您称之为屏幕的任何东西)。语言是 XML。您需要非常彻底地理解布局才能构建创新的 Android 应用程序。 

理解解决方案资源管理器中的文件 

我们将重点关注三个文件夹 

  • Drawable:包含所有图形内容,例如图像 
  • Layout:包含所有布局(我们在 Android 应用程序中看到的屏幕); 
  • Values:我们将在整个应用程序中使用的全局变量 

上图中的 Activity1.cs(创建项目时自动生成)是布局的代码隐藏。如果您需要创建自定义类,可以在 root 下创建一个文件夹并将您的类放在那里。

理解布局 - UI 

如果您是使用 C# 的 Windows/Web 开发人员,布局是一种陌生的概念。如果您了解 XAML 或 MVC 中的 ASP.NET,会更容易理解。

如果您不熟悉这个术语也没关系。我将尝试提供布局的基本原理概述,但我更喜欢单独写一篇关于布局的文章,因为这是我们需要重点关注的最重要的事情之一,尤其是在您从 Windows/Web 开发转向移动开发时。

让我们从一个场景开始,我们需要在第一行放置一个标题,第二行放置一个文本框,第三行放置一个按钮。

当您使用 HTML 或 aspx 进行设计时,这很简单。您创建三个 div,将 div 样式设置为 clear right,并将每个控件放置在 div 中。

例如 HTML 中的一些内容

<div style="clear:right">Code</div> 
<div style="clear:right"><Input type="text" id="text1"/></div> 
<div style="clear:right"><input type="button" id="button1" value="Ok"/></div>

HTML Layout 

在上面的 Android 布局中,例如, 

Axml Layout

您需要多玩一些控件才能获得更好的理解。您可以使用“视图”>“工具箱”查看控件

简而言之,这就是布局的实际定义: 

布局是 Activity 中用户界面的架构。它定义了布局结构,并包含所有向用户显示的元素。

我们可以通过两种方式声明布局: 

  1. 在 AXML 中声明
  2. 在运行时创建控件

在“COINS”应用程序中,您会看到——大量使用了在 Activity 类中动态创建控件的功能。 

布局有多少种类型?

  • LinearLayout (垂直) 
  • LinearLayout (水平)
  • RelativeLayout
  • FrameLayout
  • TableLayout
  • TableRow 

跳到 COINSMobile 应用程序 

我们对 Android Activity 类和布局已经有了一些相当好的了解,尽管我没有深入探讨 Activity.cs 的细节。在逐步展示如何创建专业 Android 应用程序的过程中,我们将获得更深入的知识。

我们将主要构建五个屏幕

  1. 启动画面 
  2. 市场观察 
  3. 股票脚本详情视图
  4. 添加股票 
  5. 获取股票报价
  6. 显示股票图表  

第一步 - 连接到 Web 服务

我们将使用 .NET Web 服务获取实时股票数据。Web 服务在与外部数据库(无论是什么数据库)通信方面发挥着重要作用。如果您想将数据存储到移动设备中,可以使用 Android 的轻量级数据库 - SQLite。如果您想与外部远程数据库通信,则需要 Web 服务或 WCF 来与数据库通信。 

在此示例中,COINS 应用程序将使用两个 Web 方法

  • StockExists 
  • GetStockQuote
  • GetStockChart 

在实际代码中,请将 Web 服务的 URL 替换为指向您的 localhost 服务(已上传 webservice 项目)

该网络服务从雅虎 API 获取市场数据源,然后上传网络服务源。 

免责声明:尽管在开发股票 Web 服务时已非常谨慎,但它不提供任何可靠性、信息准确性或操作正确性的保证。我不对因使用此 Web 服务而可能造成的任何损害负责。使用此程序完全风险自负。

GetStockQuotes(): 以逗号分隔的字符串发送多个股票,并将其作为参数传递。这将返回所有股票的 XML。

StockExist(): 发送多个股票脚本,以逗号分隔的字符串形式作为参数传递。这将返回股票的“交易所”。如果股票脚本无效,XML 将返回“NA”(不适用)。现在我们有两个 Web 方法,足以创建所有交易所股票的市场观察。

尽管 Web 服务是此应用程序中获取股票详情的重要层,但提供的 URL 仅用于概念验证。无法保证所使用的 Web 服务将获取实时数据。

那么,让我们看看如何在 Android 应用程序中调用 Web 方法?它就像我们在 Windows 或 Web 应用程序中调用 Web 方法一样简单。

向股票服务添加 Web 引用。 

add reference

现在我们准备好使用 Web 方法。获取股票数据

using COINSMobile.stockwebservice;
string strXML = string.Empty;
stockwebservice.StockWebservice quoteObject = null;
try
{
    quoteObject = new stockwebservice.StockWebservice();
    strXML = quoteObject.GetStockQuote(_stocks);
}
finally
{
    quoteObject.Dispose();
    quoteObject = null;
}
//if error occurred while connect8ing to web service
if (strXML.Substring(0, 5) == "error")
{
    var t = Toast.MakeText(this, "Error connecting to web service. Please check your Internet connection...", ToastLength.Short);
    t.SetGravity(GravityFlags.Center, 0, 0);
    t.Show();
    return;
}
if (strXML.ToLower() == "exception")
{
    var t = Toast.MakeText(this, "Service not available now. Please try after sometime...", ToastLength.Short);
    t.SetGravity(GravityFlags.Center, 0, 0);
    t.Show();
    return;
}
//load the xml to XmlDocument
XmlDocument doc = new XmlDocument();
doc.LoadXml(strXML);

这里需要注意的重要一点是,我们使用 `GetStockQuote` 方法并将 XML 字符串加载到 XMLDocument 中以解析 XML。因此,您可以使用 Web 方法与远程数据库进行通信。

使用 Android/Java 并不是那么简单,因为您需要执行几个步骤才能使用 ksoap 消费 .NET Web 服务。 

在深入了解之前,让我们看看从 Web 方法“GetStockQuote”返回的 XML 字符串。这里我将发送两个脚本——“GOOG,MSFT”作为“GetStockQuote()”Web 方法的输入参数。

GOOG:谷歌公司(交易所:纳斯达克)
MSFT:微软公司(交易所:纳斯达克)  

Sample XML Output from Web Method

SQLite 简介

SQLite 是一个关系型数据库管理系统,包含在一个小巧的(约 350 KiB)C 编程库中。与其他数据库管理系统不同,SQLite 不是一个从客户端应用程序访问的独立进程,而是其不可或缺的一部分。SQLite 作为嵌入式数据库,在网络浏览器、移动设备、平板电脑等应用程序软件中用于本地/客户端存储,是一个受欢迎的选择。它无疑是部署最广泛的数据库引擎,因为它目前被几种广泛使用的浏览器、操作系统和嵌入式系统使用。

要开始使用 SQLite,您首先需要添加对 Mono.Data.Sqlite.dll 的引用;

using Mono.Data.Sqlite;

在“COINS”应用程序中,我们将创建一个数据库和一个单独的表,并使用后续方法来获取、添加和删除我们将用于市场观察 - 收藏列表的股票。

让我们看看两个类,一个用于创建数据存储库,另一个用于所有数据库操作。

业务层中 CRMMobile.Core 项目中的 Stock 类。

public class Stock : Java.Lang.Object
{
    public long Id { get; set; }
    public string StockName { get; set; }
    public Stock()
    {
        Id = -1;
        StockName = string.Empty;
    }
    public Stock(long id, string stockName)
    {
        Id = id;
        StockName = stockName;
    }
    public override string ToString()
    {
        return StockName.ToString();
    }
}

上面的类足够简单易懂。让我们看看核心项目数据层中的 StockDatabase 类。

public class StockDatabase
{
    private static string db_file = "stockdata.db3";
    private string stockName = string.Empty;
    private static string sMessage;
    public string StockName
    {
        get { return stockName; }
        set { stockName = value; }
    }
    private static SqliteConnection GetConnection()
    {
        var dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal)
                     , db_file);
        bool exists = File.Exists(dbPath);
        if (!exists)
            SqliteConnection.CreateFile(dbPath);
        var conn = new SqliteConnection("Data Source=" + dbPath);
        if (!exists)
            CreateDatabase(conn);
        return conn;
    }
    private static void CreateDatabase(SqliteConnection connection)
    {
        var sql = "CREATE TABLE STOCKTABLE (Id INTEGER PRIMARY KEY AUTOINCREMENT, 
            StockName VARCHAR);";
        connection.Open();
        using (var cmd = connection.CreateCommand())
        {
            cmd.CommandText = sql;
            cmd.ExecuteNonQuery();
        }
        connection.Close();
    }
    public static IEnumerable<Stock> GetStocks()
    {
        try
        {
            var sql = "SELECT * FROM STOCKTABLE ORDER BY ID;";
            using (var conn = GetConnection())
            {
                conn.Open();
                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = sql;
                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                            yield return new Stock(reader.GetInt32(0), reader.GetString(1));
                    }
                }
            }
        }
        finally
        {
            StockManager.Message = sMessage;
        }
    }
    public static bool IsStockExists(string _stockname)
    {
        bool Ok = false;
        var sql = string.Format("SELECT * FROM STOCKTABLE WHERE STOCKNAME='{0}';", 
            _stockname);
        try
        {
            using (var conn = GetConnection())
            {
                conn.Open();
                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = sql;
                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                            Ok = true;
                    }
                }
            }
        }
        finally
        {
            StockManager.Message = sMessage;
        }
        return Ok;
    }
    public static bool SaveStock(string _stockname)
    {
        try
        {
            bool Ok = IsStockExists(_stockname.Trim().ToUpper());
            if (Ok)
            {
                sMessage = string.Format("Stock Script '{0}' is already added.",
                    _stockname);
                return false;
            }
            using (var conn = GetConnection())
            {
                conn.Open();
                using (var cmd = conn.CreateCommand())
                {
                    try
                    {
                        // Do an insert
                        cmd.CommandText = 
                            "INSERT INTO STOCKTABLE (StockName) VALUES (@StockName);";
                        cmd.Parameters.AddWithValue("@StockName", 
                            _stockname.ToUpper());
                        cmd.ExecuteNonQuery();
                        sMessage = string.Format(
                            "Stock Script '{0}' is added successfully.", _ stockname.ToUpper());
                        return true;
                    }
                    catch (SqliteException ex)
                    {
                        sMessage = ex.Message;
                        return false;
                    }
                }
            }
        }
        finally
        {
            StockManager.Message = sMessage;
        }
    }
    public static bool DeleteAllStocks()
    {
        try
        {
            using (var conn = GetConnection())
            {
                conn.Open();
                using (var cmd = conn.CreateCommand())
                {
                    try
                    {
                        // Do an insert
                        cmd.CommandText = "DELETE FROM STOCKTABLE;";
                        cmd.ExecuteNonQuery();
                        sMessage = 
                        "All Stocks are deleted successfully...\nTo view the stocks" +
                        "in Market Watch, you need to add your custom stock";
                        return true;
                    }
                    catch (SqliteException ex)
                    {
                        sMessage = ex.Message;
                        return false;
                    }
                }
            }
        }
        finally
        {
            StockManager.Message = sMessage;
        }
    }
}

我使用了 SqLite Helper 类来执行所有数据库操作。代码自解释,代码中没有太多细节可讲。如果您是 SQLite 新手,只需玩玩代码即可探索更多。

有一点要提:在应用程序中,我只使用了一个表来添加自定义股票脚本。

CREATE TABLE STOCKTABLE (
     Id INTEGER PRIMARY KEY AUTOINCREMENT, 
     StockName VARCHAR
);

我们将在活动类中频繁使用 Web 服务和数据库。

现在我们来构建应用程序。

构建启动画面(第一个小屏幕,代码很少)

右键单击解决方案资源管理器。单击“添加”>“新建项”>“Activity”(从 Android 新建窗口)。将文件命名为 MainActivity.cs

[Activity(MainLauncher = true, Theme = "@style/Theme.Splash", NoHistory = true)]
public class MainActivity : Activity
{
   protected override void OnCreate(Bundle bundle)
   {
       base.OnCreate(bundle);
       StartActivity(typeof(MarketWatch));
   }
}

让我们看看代码

[Activity(MainLauncher = true, Theme = "@style/Theme.Splash", NoHistory = true)]
Main Launcher=True

这意味着启动 Activity。当应用程序加载时,它会启动此 Activity。

Theme="@style/Theme.Splash"

您是否注意到解决方案资源管理器中“资源”>“值”下有一个 XML 文件?这是 Strings.xml,我们在此处定义可由应用程序访问的全局变量。

要启动主题,您需要创建一个图形文件并将其添加到 Resources>Drawable 中。添加后,您将在“ResourceDesigner.cs”中看到一个引用,例如

public partial class Drawable
{
    public const int splash = 2130837511;
    .....
}

这是 strings.xml 中用于启动画面的 XML

<style name="Theme.Splash" parent="android:Theme">
    <item name="android:windowBackground">@drawable/splash</item>
    <item name="android:windowNoTitle">true</item>
</style>

所以,当我们写“@style/Theme.Splash”时,Activity 指向启动画面图像。这里图像的名称是 splash.png

StartActivity(typeof(MarketWatch)); - 这里 MarketWatch 是另一个 Activity 类,它从启动画面调用。所以,这是应用程序的起点。

太强大了!是不是很简单?只需创建一个图形文件,使用“添加>添加现有项”从 Drawable 添加文件,在 strings.xml 中添加图形文件的定义,然后只编写两行代码。很好,让我们看看下一个屏幕“市场观察”

从 - 市场观察 开始

在这里,我们将创建一个动态控件“TableView”。创建动态控件的原因是,我们在设计时无法确定将有多少 Table Row。Table Row 将根据股票表中的记录动态创建(最初股票表是空白的,但随后会随着向 SQLite 数据库添加股票脚本而增长)。那么,让我们看看如何在 Android Activity 类中创建动态控件。

<ScrollView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TableLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:stretchColumns="1"
        android:id="@+id/deatlWatchLayout" />
</ScrollView>

注意:我在 TableLayout 的顶部使用了“ScrollView”。原因是,如果您的股票不适合屏幕,您将看到垂直滚动条,您可以使用它来滚动视图。

private void PopulateDataToControls(string _stocks)
{
    //we have now the stocks delimited by comma
    //this string will be passed to Webservice as a paramete to fetch the stock block in xml
    string strXML = string.Empty;
    stockwebservice.StockWebservice quoteObject = null;
    try
    {
        quoteObject = new stockwebservice.StockWebservice();
        strXML = quoteObject.GetStockQuote(_stocks);
    }
    finally
    {
        quoteObject.Dispose();
        quoteObject = null;
    }
    //if error occurred while connect8ing to web service
    if (strXML.Substring(0, 5) == "error")
    {
        var t = Toast.MakeText(this, "Error connecting to web service. Please " + 
          "check your Internet connection...", ToastLength.Short);
        t.SetGravity(GravityFlags.Center, 0, 0);
        t.Show();
        return;
    }
    if (strXML.ToLower() == "exception")
    {
        var t = Toast.MakeText(this, "Service not available now. " + 
          "Please try after sometime...", ToastLength.Short);
        t.SetGravity(GravityFlags.Center, 0, 0);
        t.Show();
        return;
    }
    //load the xml to XmlDocument
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(strXML);
    tablelayout.RemoveAllViews();
    tablelayout.RefreshDrawableState();
    XmlNodeList xnList = doc.SelectNodes("/stock/symbol");
    foreach (XmlNode xn in xnList)
    {
        if (xn != null)
        {
            TableRow demoTableRow = new TableRow(this);
            TextView tv_l = new TextView(this);
            TextView tv_r = new TextView(this);
                    
            tv_l.SetPadding(3, 3, 3, 3);
            tv_r.SetPadding(3, 3, 3, 3);
            tv_r.Gravity = GravityFlags.Right;
            tv_l.SetTextSize(Android.Util.ComplexUnitType.Px, 9);

            tv_l.Text = xn["code"].InnerText.Trim() + 
                "-" + xn["exchange"].InnerText.Trim();
            tv_r.Text = "(" + xn["change"].InnerText.Trim() + 
                ") " + xn["last"].InnerText.Trim( );
            demoTableRow.Clickable = true;
            demoTableRow.Click += (sender, e) =>
            {
                doRowClick(sender);
            };
            demoTableRow.AddView(tv_l);
            demoTableRow.AddView(tv_r);
            tablelayout.AddView(demoTableRow);
            View vLineRow = new View(this);
            vLineRow.SetMinimumHeight(1);
            vLineRow.SetBackgroundColor(Color.Rgb(88, 88, 88));
            tablelayout.AddView(vLineRow);
        }
    }
}

我们将重点关注的代码是动态创建 TableView 控件的部分。TableView 就像我们在 HTML 中使用的 Table 一样。

TableView 包含 TableRows。每个 Table Row 包含 TextView

TableRow demoTableRow = new TableRow(this);
TextView tv_l = new TextView(this);
TextView tv_r = new TextView(this);
tv_l.Text = "Column 1 data";
tv_r.Text = "Column 2 data"
//here we are adding TextView to TableRow
demoTableRow.AddView(tv_l);
demoTableRow.AddView(tv_r);
//add TableRow control to the Layout we define in MarketWatch.axml
tablelayout.AddView(demoTableRow);

这里唯一棘手的部分是为 TableView 控件创建 Click 事件处理程序。请记住,一个 TableLayout 中可以有多个 TableRows。数据库中保存的每个股票脚本都会创建一行。因此,如果我们有 20 个股票脚本,我们将使用“for 循环”创建大约 20 行。要将事件处理程序 (OnClick) 附加到 TextView,

demoTableRow.Clickable = true;
demoTableRow.Click += (sender, e) =>
{ 
    doRowClick(sender);
}
 
private void doRowClick(object Sender)
{
    TableRow tr = Sender as TableRow;
    if (tr != null)
    {
        TextView v = tr.GetChildAt(0) as TextView;
        string _script = v.Text.Trim();
        int _index = _script.IndexOf('-');
        if (_index >

您可能已经注意到我使用了 Intent 来从一个 Activity 传递值到另一个 Activity。

Intent 提供了一种在不同应用程序中的代码之间执行后期运行时绑定的机制。它最重要的用途是启动活动,在那里它可以被认为是活动之间的粘合剂。它基本上是一个被动数据结构,包含要执行的动作的抽象描述。

所以在 StockDetails Activity 中,我们将获取从 MarketWatch Activity 传递的值。简而言之,当我们单击一行时,我们将发送者传递给自定义方法。从发送者那里,我们获取 TextView 控件并获取“股票名称”。然后,我们将股票名称的值传递给另一个 Activity——“股票详情 Activity”,在该 Activity 中,我们将再次与 Web 服务通信,以获取股票的详细值。

现在我们来看看“MarketWatch.axml”——市场观察的布局。布局中唯一需要关注的部分是在左上角设计一个 TextView,在右上角设计一个刷新图标

使用 RelativeLayout 将解决我们将两个控件并排放置的问题。

<RelativeLayout
    android:layout_width="fill_parent"
    android:background="@color/medium_gray"
    android:layout_height="30dp"
    android:paddingTop="3dip">
    <TextView
        android:id="@+id/symbolcaption"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:textColor="@android:color/white"
        android:background="@color/medium_gray"
        android:layout_marginLeft="0px"
        android:paddingTop="2px" />
    <ImageView
        android:id="@+id/buttonRefresh"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"
        android:lines="1"
        android:clickable="true"
        android:src="@drawable/refresh_icon" />
    <ImageView
        android:id="@+id/buttonHome"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_margin="24dip"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:clickable="true"
        android:src="@drawable/home_icon" />
</RelativeLayout>

让我们回顾一下,ImageView 标签中的“@drawable/refresh”是什么?当您添加刷新图标时,Drawable 类中的 Resource.Designer.cs 会自动生成一行

public partial class Drawable
{
    // aapt resource value: 0x7f020000
    public const int refresh = 2130837504;
        .......
}

所以我们已经了解了如何将一个值从一个 Activity 传递到另一个 Activity。在 StockDetails Activity 中,我们将看到如何获取从 MarketWatch Activity 传递的值。我们还对如何动态创建控件有了一个大致的了解。

第二步 - 使用按下/触摸构建股票详细视图

在上面的屏幕中,您可能已经注意到我们从 MarketWatch Activity 传递的值是发送到 Web 服务以获取股票详细信息的。让我们看看代码。我们将如何获取变量并将其传递给 Web 服务以获取 XML?

首先,您需要从“添加”>“添加项”>“Activity”创建一个新 Activity。将文件命名为“StockDetailsActivity.cs

_script = Intent.GetStringExtra("Script");
if (_script == string.Empty)
{
    var t = Toast.MakeText(this, "Invalid Script...", ToastLength.Long);
    t.SetGravity(GravityFlags.Center, 0, 0);
    t.Show();
    return;
}
ImageView btnRefresh = FindViewById<ImageView>(Resource.Id.buttonRefresh);
btnRefresh.Click += (sender, e) =>
{
    if (!IsLoading)
    {
        ProgressDialog progress = ProgressDialog.Show(this, 
            "", "Loading Quote...", true);
        new Thread(new ThreadStart(() =>
        {
            this.RunOnUiThread(() =>
            {
                doLoadDetails();
                progress.Dismiss();
            });
        })).Start();
        IsLoading = false;
    }
};
ImageView btnHome = FindViewById<ImageView>(Resource.Id.buttonHome);
btnHome.Click += (sender, e) =>
{
    StartActivity(typeof(MarketWatchActivity));
    return;
};
ProgressDialog progressMain = ProgressDialog.Show(this, 
    "", "Loading Quote...", true);
new Thread(new ThreadStart(() =>
{
    this.RunOnUiThread(() =>
    {
        doLoadDetails();
        progressMain.Dismiss();
    });
})).Start();

我希望您已经找到了如何从 Indent 获取值的方法。

_script = Intent.GetStringExtra("Script");
if (_script == string.Empty)
{
    var t = Toast.MakeText(this, "Invalid Script...", ToastLength.Long);
    t.SetGravity(GravityFlags.Center, 0, 0);
    t.Show();
    return;
}

Intent.GetStringExtra("Script") 将返回我们从前一个 Activity 传递的值。在将值传递给 Web 服务以获取实时股票数据之前,我们将进行一个简单的检查。

什么是 Toast?

Toast 是一个视图,其中包含一条给用户的简短消息。我们使用“Toast”来显示任何信息或警告消息。文本显示一段时间后消失,以便用户能够很好地了解应用程序的运行情况。

现在我们将看到如何将股票名称或脚本名称传递给 Web 服务,并拉取 XML 并将数据显示在 TableView 控件中。这部分与市场观察非常相似。

//conecting to webservice and get the xml
string strXML = string.Empty;
stockwebservice.StockWebservice quoteObject = null;
try
{
    quoteObject = new stockwebservice.StockWebservice();
    strXML = quoteObject.GetStockQuote(_script);
}
finally
{
    quoteObject.Dispose();
    quoteObject = null;
}
//if error occurred while connect8ing to web service
if (strXML.Substring(0, 5) == "error")
{
    var t = Toast.MakeText(this, 
    "Error connecting to web service. Please check your Internet connection...", ToastLength.Short);
    t.SetGravity(GravityFlags.Center, 0, 0);
    t.Show();
    return;
}
if (strXML.ToLower() == "exception")
{
    var t = Toast.MakeText(this, 
    "Service not available now. Please try after sometime...", ToastLength.Short);
    t.SetGravity(GravityFlags.Center, 0, 0);
    t.Show();
    return;
}
LinearLayout stockdetailsLinearLayout = FindViewById<LinearLayout>(Resource.Id.linearLayoutstockdetails);
symbolCaption = FindViewById<TextView>(Resource.Id.symbolcaption);
symbolCaption.SetTextAppearance(this, Resource.Style.boldText);
priceCaption = FindViewById<TextView>(Resource.Id.pricecaption);
priceCaption.SetTextAppearance(this, Resource.Style.boldText19);
changeCaption = FindViewById<TextView>(Resource.Id.changecaption);
changeCaption.SetTextAppearance(this, Resource.Style.boldText19);

datetimeCaption = FindViewById<TextView>(Resource.Id.datetimecaption);
datetimeCaption.SetTextAppearance(this, Resource.Style.smallText);
//load the xml to XmlDocument
XmlDocument doc = new XmlDocument();
doc.LoadXml(strXML);
XmlNodeList xnList = doc.SelectNodes("/stock/symbol");
foreach (XmlNode xn in xnList)
{
    data[0, 0] = "Symbol";
    data[0, 1] = xn["code"].InnerText.Trim();
    data[1, 0] = "Name";
    data[1, 1] = xn["company"].InnerText.Trim();
    symbolCaption.Text = xn["company"].InnerText.Trim();
    datetimeCaption.Text = DateTime.Now.ToString("dd-MM-yyyy hh:mm:ss");
    priceCaption.Text = xn["last"].InnerText.Trim() + " " + 
        xn["currency"].InnerText.Trim();
    changeCaption.Text = xn["change"].InnerText.Trim();
    View vLinePrice = new View(this);
    vLinePrice.SetMinimumHeight(2);
    vLinePrice.SetBackgroundColor(Color.Rgb(164, 164, 164));
    stockdetailsLinearLayout.AddView(vLinePrice);
    data[2, 0] = "Exchange";
    data[2, 1] = xn["exchange"].InnerText.Trim();
    data[3, 0] = "Open";
    data[3, 1] = xn["open"].InnerText.Trim();
    data[4, 0] = "Day's High";
    data[4, 1] = xn["high"].InnerText.Trim();
    data[5, 0] = "Day's Low";
    data[5, 1] = xn["low"].InnerText.Trim();
    data[6, 0] = "Change";
    data[6, 1] = xn["change"].InnerText.Trim();
    data[7, 0] = "Change %";
    data[7, 1] = xn["changepercent"].InnerText.Trim();
    data[8, 0] = "Volume";
    data[8, 1] = xn["volume"].InnerText.Trim();
    data[9, 0] = "Previous Close";
    data[9, 1] = xn["previousclose"].InnerText.Trim();
    data[10, 0] = "Trade Time";
    data[10, 1] = xn["tradetime"].InnerText.Trim();
    data[11, 0] = "Market Capital";
    decimal marketcapital = 0;
    if (decimal.TryParse(xn["marketcapital"].InnerText.Trim(), out marketcapital))
        data[11, 1] = string.Format("{0:#,0}", marketcapital);
    else
        data[11, 1] = xn["marketcapital"].InnerText.Trim();

    TableLayout tableLayout = FindViewById<TableLayout>(Resource.Id.deatlLayout);
    tableLayout.RemoveAllViews();
    tableLayout.RefreshDrawableState();
    for (int i = 2; i < 12; i++)
    {
        // add all inforamation to your tablerow in such a maaner that you want to display on screen.
        TableRow demoTableRow = new TableRow(this);
        TextView tv_l = new TextView(this);
        TextView tv_r = new TextView(this);
        tv_l.SetPadding(3, 3, 3, 3);
        tv_r.SetPadding(3, 3, 3, 3);
        tv_r.Gravity = GravityFlags.Right;
        tv_l.Text = data[i, 0];
        tv_r.Text = data[i, 1];
        demoTableRow.AddView(tv_l);
        demoTableRow.AddView(tv_r);
        if (i == 0)
        {
            tv_l.SetTextAppearance(this, Resource.Style.boldText);
        }

        tableLayout.AddView(demoTableRow);
        View vLineRow = new View(this);
        vLineRow.SetMinimumHeight(1);
        vLineRow.SetBackgroundColor(Color.Rgb(88, 88, 88));
        tableLayout.AddView(vLineRow);
    }
}

代码是自解释的,没有太多细节可讲。我宁愿解释刷新图标的功能,当点击或触摸它时,它会刷新视图。我们还将显示一个“加载消息”,在应用程序执行调用 Web 服务、拉取 XML 数据并使用数据创建视图的后台过程时。市场观察中也进行了“刷新”,但我觉得在理解“调用 Web 服务”和“动态创建控件”之后再解释会更好。

ImageView btnRefresh = FindViewById<ImageView>(Resource.Id.buttonRefresh);
btnRefresh.Click += (sender, e) =>
{
    if (!IsLoading)
    {
        ProgressDialog progress = ProgressDialog.Show(this, "", "Loading Quote...", true);
        new Thread(new ThreadStart(() =>
        {
            this.RunOnUiThread(() =>
            {
                doLoadDetails();
                progress.Dismiss();
            });
        })).Start();
        IsLoading = false;
    }
};

我们将使用一个新的线程来运行 doLoadDetails() 方法。在此之前,我们将使用一个额外的功能,ProcessDialog。进度对话框是 Android API 的一部分,可用于显示进度条或旋转进度符号。

所以,基本的想法是先显示进程对话框。然后启动一个新线程并在该线程中运行方法。一旦方法执行完毕,我们将使用 `progress.Dismiss()` 销毁进程。

第三步 - 从动作栏 - 菜单中查看股票图表

当我们谈论市场观察时,如果我们不向用户提供图表选项,那就不公平了。用户经常有兴趣查看不同参数的图表,以了解股票走势趋势。

图表参数有哪些?

  • 1 天
  • 5 天
  • 3 个月
  • 6 个月
  • 1 年
  • 2 年
  • 5 年

Web 方法 - "GetStockChart" 需要两个输入参数 - 股票代码和图表参数。该方法将以字节数组的形式返回图表图像。让我们看看代码,

byte[] image_data;
stockwebservice.StockWebservice quoteObject = null;
try
{
    quoteObject = new stockwebservice.StockWebservice();
    image_data = quoteObject.GetStckChart(_stock, _type);
}
finally
{
    quoteObject.Dispose();
    quoteObject = null;
}

所以,我们现在有了字节数组,将其转换为 ImageView 控件是多么简单。

//convert byte array to image
Bitmap bitmapChart = BitmapFactory.DecodeByteArray(image_data, 0, image_data.Length);
imgChart.SetImageBitmap(bitmapChart);
imgChart.SetBackgroundResource(Resource.Color.transparent); 

现在,我们需要放置七个按钮并添加 onClick 处理程序,当点击时,这些处理程序将调用 Web 方法 - StockGetChart,并带有不同的图表参数。

btn1D.Click += (sender, e) => { doLoadChart(sender, "1D"); };
btn5D.Click += (sender, e) => { doLoadChart(sender, "5D"); };
btn3M.Click += (sender, e) => { doLoadChart(sender, "3M"); };
btn6M.Click += (sender, e) => { doLoadChart(sender, "6M"); };
btn1Y.Click += (sender, e) => { doLoadChart(sender, "1Y"); };
btn2Y.Click += (sender, e) => { doLoadChart(sender, "2Y"); };
btn5Y.Click += (sender, e) => { doLoadChart(sender, "5Y"); };

在上面的方法“doLoadChart”调用 Web 方法——GetStckChart”并以字节数组的形式返回图像。查看 Web 服务代码,了解图像如何转换为字节数组并发送回客户端。我们将在一个线程中运行该方法,以显示进程对话框,直到图像完全加载到 ImageView 控件中。

现在,在进入下一步之前,让我们快速看一下股票图表布局(StckChart.axml)

<?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">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="28dp"
        android:id="@+id/linearLayoutstockdetails"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp">
        <TextView
            android:id="@+id/stockchartcaption"
            android:layout_width="206dp"
            android:layout_height="fill_parent"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:textColor="@android:color/white"
            android:removed="#000000"
            android:layout_marginLeft="0px"
            android:textSize="14dp"
            android:textStyle="bold"
            android:paddingTop="2dp"
            android:layout_gravity="center_horizontal" />
        <Button
            android:text="1D"
            android:layout_width="28dp"
            android:layout_height="18dp"
            android:textSize="12dp"
            android:id="@+id/button1D"
            android:textColor="#ffffff"
            android:padding="3dp"
            android:background="#1C1C1C"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            android:layout_marginRight="2dp"
            android:layout_marginLeft="6dp" />
        <Button
            android:text="5D"
            android:layout_width="28dp"
            android:layout_height="18dp"
            android:textSize="12dp"
            android:textColor="#ffffff"
            android:padding="3dp"
            android:background="#1C1C1C"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            android:layout_marginRight="2dp"
            android:id="@+id/button5D" />
        <Button
            android:text="3M"
            android:layout_width="28dp"
            android:layout_height="18dp"
            android:textSize="12dp"
            android:textColor="#ffffff"
            android:padding="3dp"
            android:background="#1C1C1C"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            android:layout_marginRight="2dp"
            android:id="@+id/button3M" />
        <Button
            android:text="6M"
            android:layout_width="28dp"
            android:layout_height="18dp"
            android:textSize="12dp"
            android:textColor="#ffffff"
            android:padding="3dp"
            android:background="#1C1C1C"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            android:layout_marginRight="2dp"
            android:id="@+id/button6M" />
        <Button
            android:text="1Y"
            android:layout_width="28dp"
            android:layout_height="18dp"
            android:textSize="12dp"
            android:textColor="#ffffff"
            android:padding="3dp"
            android:background="#1C1C1C"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            android:layout_marginRight="2dp"
            android:id="@+id/button1Y" />
        <Button
            android:text="2Y"
            android:layout_width="28dp"
            android:layout_height="18dp"
            android:textSize="12dp"
            android:textColor="#ffffff"
            android:padding="3dp"
            android:background="#1C1C1C"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            android:layout_marginRight="2dp"
            android:id="@+id/button2Y" />
        <Button
            android:text="5Y"
            android:layout_width="28dp"
            android:layout_height="18dp"
            android:textSize="12dp"
            android:textColor="#ffffff"
            android:padding="3dp"
            android:background="#1C1C1C"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            android:layout_marginRight="2dp"
            android:id="@+id/button5Y" />
    </LinearLayout>
    <ImageView
        android:id="@+id/imageChart"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:clickable="true"
        android:layout_marginTop="2dp" />
</LinearLayout>

如果你看到 TextView 和用于显示不同图表的按钮都放在一个方向为水平的 LinearLayout 中。我们已经讨论过,将控件放在 Linearayout(方向:水平)中,简单地意味着控件将并排放置。

我在本节开头遗漏了一个重要信息,我们将强制屏幕以横向模式显示。由于图表在纵向模式下无法最佳适配,我们将强制屏幕以横向模式打开。

做到这一点的简单技巧是,

[Activity(Label = "Stock Chart", ScreenOrientation = Android.Content.PM.ScreenOrientation.Landscape)] 

第四步 - 将自定义股票添加到 SQLite 数据库 

在此 Activity 中,我们将向 SQLite 数据库添加股票。添加的股票将在市场观察中显示摘要,并在 StockDetails Activity 中显示详细信息。

让我们首先关注屏幕。

Add Stock Layout

AddStock Activity 的布局非常简单。请查看 AddStock 布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/linearLayout1">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Enter the full Stock Symbol for your exchange. Valid Stock will be added only."
        android:layout_marginTop="36px"
        android:layout_marginBottom="14px"
        android:layout_marginLeft="8px"
        android:textSize="13sp"
        android:layout_marginRight="8px" />
    <EditText
        android:layout_width="fill_parent"
        android:layout_height="40px"
        android:id="@+id/textStock"
        android:textColor="@android:color/black"
        android:hint="Enter Stock Symbol"
        android:layout_marginLeft="8px"
        android:layout_marginBottom="12px"
        android:layout_marginRight="80px"
        android:padding="4px"
        android:textColorHint="#323232"
        android:textSize="15sp" />
    <Button
        android:id="@+id/buttonAddStock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Stock..."
        android:layout_marginLeft="8px" />
</LinearLayout>
由于我们是将控件一个接一个地放置,所以我们只需要一个方向为“垂直”的线性布局。我们主要有三个控件,
  • TextView
  • EditText
  • 按钮 
您可能已经注意到,为了在每个控件之间获得间距,已经给出了一些填充。这里我要强调的一点是 Hint,即 EditText 控件中灰色的文本。这有点像 HTML 中 input 文本的“placeholder”属性。在移动设备中设计屏幕布局时,我们需要非常注意空间。屏幕上没有太多的空间来容纳所有控件。因此,在移动设备中,Hint 经常被使用,通过它我们可以摆脱一个额外的控件,让用户识别 EditText 的作用。
android:hint="Enter Stock Symbol"
android:textColorHint="#323232"

AddStock Activity 类

当点击“添加”按钮时,我们将进行一些基本的验证。然后我们将检查用户输入的股票脚本是否有效。如果它不是有效的脚本,我们需要在 Toast 中显示消息。最后一个重要的验证是我们需要确保用户不会添加重复的股票。因此,我们将检查输入的股票脚本是否存在于数据库中。 
private bool doCheckStockScript(string _stockscript)
{
    //conecting to webservice and get the xml
    string strXML = string.Empty;
    stockwebservice.StockWebservice quoteObject = null;
    try
    {
        quoteObject = new stockwebservice.StockWebservice();
        strXML = quoteObject.StockExists(_stockscript);
    }
    finally
    {
        quoteObject.Dispose();
        quoteObject = null;
    }
    //if error occurred while connect8ing to web service
    if (strXML.Substring(0, 5) == "error")
    {
        var t = Toast.MakeText(this, 
        "Error connecting to web service. Please check your Internet connection...", 
        ToastLength.Short);
        t.SetGravity(GravityFlags.Center, 0, 0);
        t.Show();
        return false;
    }
    if (strXML.ToLower() == "exception")
    {
        var t = Toast.MakeText(this, 
        "Service not available now. Please try after sometime...", ToastLength.Short);
        t.SetGravity(GravityFlags.Center, 0, 0);
        t.Show();
        return false;
    }
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(strXML);
    XmlNodeList xnList = doc.SelectNodes("/stock/symbol");
    foreach (XmlNode xn in xnList)
    {
        if (xn["exchange"].InnerText.Trim().ToUpper() == "NA")
            return false;
    }
    return true;
}
private bool AddStockScript(string _stockscript)
{
    bool Ok = false;
    StockManager.Message = string.Empty;
    Ok = StockManager.SaveStock(_stockscript);
    if (StockManager.Message != string.Empty)
    {
        var t = Toast.MakeText(this, StockManager.Message, ToastLength.Short);
        t.SetGravity(GravityFlags.Center, 0, 0);
        t.Show();
    }
    return Ok;
}
成功添加后,我们将在“Toast”中显示一条快速消息并返回到 Market Watch Activity。

第五步 - 获取股票详情  

获取股票详情是股票的自定义搜索。用户将输入脚本并按下“获取股票”按钮,股票报价详情将显示在屏幕上。以下是获取股票数据后“获取股票”的屏幕截图。在此示例中,我使用了脚本“GOOG”,它是 Google Inc. 的股票脚本。 

让我们看看 GetStockActivity 类

private bool IsLoading = false;
protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    View titleView = Window.FindViewById(Android.Resource.Id.Title);
    //update the default title
    if (titleView != null)
    {
        IViewParent parent = titleView.Parent;
        if (parent != null && (parent is View))
        {
            View parentView = (View)parent;
            parentView.SetBackgroundColor(Color.Rgb(28, 28, 28));
            parentView.SetMinimumHeight(32);
            parentView.SetMinimumHeight(32);
        }
    }
    // Set our view from the "main" layout resource
    SetContentView(Resource.Layout.GetStock);
    ImageView btnHome = FindViewById<ImageView>(Resource.Id.buttonHomeget);
    btnHome.Click += (sender, e) =>
    {
        StartActivity(typeof(MarketWatchActivity));
        return;
    };
    EditText textStock = FindViewById<EditText>(Resource.Id.textGetStock);
    Button btnGetStock = FindViewById<Button>(Resource.Id.buttonGetStock);
    btnGetStock.Click += (sender, e) =>
    {
        if (textStock.Text == string.Empty)
        {
            //message if no text is entered in script
            var t = Toast.MakeText(this, "Please eneter Stock Script", ToastLength.Short);
            t.SetGravity(GravityFlags.Center, 0, 0);
            t.Show();
            textStock.Focusable = true;
            return;
        }
        //check script from webservice to see, if it is a valid script
        if (doCheckStockScript(textStock.Text.Trim()))
        {
            ProgressDialog progressMain = ProgressDialog.Show(this, "", "Loading Quote...", tru                   e);
            new Thread(new ThreadStart(() =>
            {
                //Thread.Sleep(4 * 1000);
                this.RunOnUiThread(() =>
                {
                    doLoadDetails(textStock.Text.Trim());
                    progressMain.Dismiss();
                });
            })).Start();
        }
        else
        {
            //message if no text entered is not a valid script
            var t = Toast.MakeText(this, "You have eneterd invalis Stock Script. Please eneter a valid Stock Script", ToastLength.Short);
            t.SetGravity(GravityFlags.Center, 0, 0);
            t.Show();
            textStock.Focusable = true;
            return;
        }
    };
}
private bool doCheckStockScript(string _stockscript)
{
     //check, if the stock script is Valid 
}
protected void doLoadDetails(string _script)
{
     //connect to web service to pull the real time stock data and display the result on the screen 
}

没有什么太多细节可解释。如果您查看方法“doCheckStockScript()”,它会检查输入的股票脚本的有效性。如果输入的脚本无效,将使用“Toast”弹出一条消息。

总结应用程序 

所以我们几乎涵盖了所有领域,但有几件事我之前在文章中没有讨论,所以现在我将讨论它们。在阅读讨论时,您是否考虑过我们如何从一个页面导航到另一个页面(除了右上角的 Home 图标)?

我们遵循几种导航方式。但根据应用程序需求,我将使用“OptionMenu”在动作栏中可见,以从一个 Activity 导航或跳转到另一个 Activity。

public override bool OnCreateOptionsMenu(IMenu menu)
{
    menu.Add("Get Quote").SetIcon(Resource.Drawable.ic_stock_get); ;
    menu.Add("Add Stock").SetIcon(Resource.Drawable.ic_quote_add);
    menu.Add("Delete Stocks").SetIcon(Resource.Drawable.ic_stock_delete);
    return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
    switch (item.TitleFormatted.ToString())
    {
        case "Delete Stocks":
            doDeleteStocks(); break;
        case "Add Stock":
            doOpenAddStock(); break;
        case "Get Quote":
            doOpenGetStock(); break;
    }
    return base.OnOptionsItemSelected(item);
}
//delete all saved stocks from database 
protected void doDeleteStocks()
{
    StockManager.DeleteAllStocks();
    if (!IsLoading)
    {
        ProgressDialog progress1 = ProgressDialog.Show(this, "", "Deleting Stock...", true);
        new Thread(new ThreadStart(() =>
        {
            this.RunOnUiThread(() =>
            {
                doLoadMarketWatch();
                progress1.Dismiss();
            });
        })).Start();
        IsLoading = false; 
    };
}
protected void doOpenAddStock()
{
    StartActivity(typeof(AddStockActivity));
}
protected void doOpenGetStock()
{
    StartActivity(typeof(GetStockActivity));
}

我们正在重写方法 OnCreateOptionsMenu(IMenu menu)。我们将使用“menu”在动作栏中创建菜单项。

IMenu 是一个接口,它公开了三个重要方法——Add、AddMenu、AddSubMenu。我们可以根据需求进一步增强 Android 应用程序中的菜单构建。

您已完成 - 构建专业的 Android 应用程序 

您现在已经完成了专业 Android 应用程序的构建。尽管我们只涵盖了此应用程序中使用的一小部分功能和控件,但每个人都有能力在现有基础上进一步开发和提升技能,达到“专业 Android 开发人员”的水平。本文将为您提供一个关于如何使用 C# 和 Visual Studio 2010 创建专业 Android 应用程序的清晰概念。

最后一步是如何运行和测试应用程序。像所有设备应用程序一样,我们可以在模拟器中运行应用程序,然后再将其安装到实际设备中。在下一节中,我将描述如何在模拟器中部署和运行应用程序。

部署和运行应用程序 

成功构建后,转到调试>不带调试启动。如果需要调试,也可以使用带调试启动。点击“启动”后,会出现一个窗口,您需要选择“启动模拟器映像”。

API 版本表示目标设备 - 支持的 Android 版本。我将选择 API 10,这意味着支持的 Android 版本是 2.3。

选择版本并继续后,您将看到以下屏幕

Select Emulator

点击“确定”继续。它将花费一些时间,估计 1 到 4 分钟,具体取决于您的处理器速度。因此,我建议您在编写代码时保持模拟器打开。这将节省您宝贵的时间。一旦模拟器加载 Activity,它将看起来像: 

Running COINS in Emulator

注意:虽然在模拟器中运行和测试应用程序是免费的,但您需要许可证才能将应用程序安装到物理设备。

Mono Android SDK 重要说明 

此应用程序基于 Visual Studio 2010 的 Mono Android SDK 构建。对于 Visual Studio 2012,您可以从以下链接下载 Mono Android:安装 Mono for Android SDK (Visual Studio 2012)

请注意:如果您想在 2012 年运行应用程序,您需要将其转换为 VS 2012 并使用正确的 sdk。

未来展望    

手机、iPad 和平板电脑日益普及。我们绝不局限于游戏应用。我们可以为小型/中型/企业创建强大的商业应用。使用 Mono C# 可以在所有上述设备上构建应用。Apple 使用自己的操作系统 iOS。MonoTouch 支持 C#,用于使用 Mono C# 编写专业应用。

我们已经在 .NET 中开发 Windows Mobile 应用程序。因此,总而言之,Visual Studio (C#) 是我们可以探索的语言,用于开发当今市场上几乎所有上述设备上的跨平台应用程序。

评论    

您的评论很有价值。我将非常感谢您的评论、建议和反馈。 我将尽我所能回复。

© . All rights reserved.