使用 Visual Studio 开发的 Xamarin.Android 应用程序进行打印





5.00/5 (2投票s)
使用 Visual Studio 开发的 Xamarin.Android 应用程序进行打印
引言
如果您曾经开发过面向 Android/移动平台的业务应用程序,您可能需要从应用程序中进行打印。与在家中或办公室的台式电脑打印不同,您通常不会将移动设备连接到打印机,因此打印成为一个更加困难和有问题的议题。在本文中,我将描述如何从您的 Xamarin.Android
应用程序进行打印。虽然本文是针对 Xamarin.Android
和 C# 编写的,但同样适用于 Eclipse 和 Java。
背景
本文使用 Star Micronics 热敏打印机。热敏打印机通过加热热敏纸来生成数字图像。Star Micronics 系列产品可以通过 HTTP 请求实现基于 Web 的应用程序打印。它们体积小巧、重量轻,并且内存占用非常小,非常适合从移动设备打印。它们还支持蓝牙,因此可以无线使用,使其更适合移动设备打印。
要在您的应用程序中使用打印机,您首先需要下载 Star Micronics SDK。下载 SDK 后,您需要将 StarPrinter.dll 程序集添加到您的 Visual Studio 项目中。
关于响应性的说明
在开始之前,请务必牢记应用程序的响应性。可能发生在您应用程序中最糟糕的事情是它触发了“应用程序无响应”(ANR) 对话框。在 Android 中,系统会针对一段时间内响应不足的应用程序显示一个对话框,说明您的应用程序已停止响应。
打印正是可能比其他任务花费更长时间才能完成的任务,因此是触发 ANR 对话框的主要候选者。因此,您应该仔细考虑您的应用程序如何在不触发 ANR 的情况下执行打印任务。您不应该在主 UI 线程上执行打印任务,而是考虑其他策略,例如使用 Thread
或 AsyncTask
来实现您的打印任务。
对 Android 应用程序响应性的详细讨论超出了本文的范围,但它无疑是您在设计和实现应用程序时需要考虑的一个因素。
本文假定您熟悉 Android 图形元素,例如 Bitmap
、Canvas
、Paint
、TextLayout
和 StaticLayout
。
将 Star Micronics 添加到您的应用程序
要将 Star Micronics 打印功能包含到您的应用程序中,您需要在代码中添加以下引用:
using Com.Starmicronics.Stario;
查找您的 Star Micronics 蓝牙打印机
在您的应用程序可以使用打印机之前,必须首先找到它。Star 打印机可以使用蓝牙或 TCP (LAN) 协议进行定位。虽然实际上您的应用程序将使用蓝牙,但我为完整起见,添加了一个如何使用 TCP 搜索打印机的示例。
new Thread(() =>
{
try
{
IList<PortInfo> portList = StarIOPort.SearchPrinter("BT");
if (!portList.Any())
{
portList = StarIOPort.SearchPrinter("BT:DeviceName");
}
if (!portList.Any())
{
portList = StarIOPort.SearchPrinter("BT:MacAddress");
}
if (!portList.Any())
{
portList = StarIOPort.SearchPrinter("BT:Star Micronics");
}
if (!portList.Any())
{
portList = StarIOPort.SearchPrinter("TCP");
}
if (!portList.Any())
{
RunOnUiThread(() =>
ShowAlert("No printers found, connect on bluetooth first",
ToastLength.Long, Resource.Drawable.printer_cross));
}
}
catch (StarIOPortException ex)
{
RunOnUiThread(() =>
Toast.MakeText(this, "Error finding printers:
" + ex.Message, ToastLength.Long).Show());
}
}).Start();
要定位您的 Star 打印机,您需要调用 SearchPrinter()
函数。此函数使用指定的接口类型返回它找到的打印机列表。它将在 LAN 或配对的蓝牙设备上搜索打印机。您可能希望在应用程序启动时调用此代码。
请注意,打印机搜索是在 Thread()
上执行的,这样它就不会占用主 UI 线程并可能触发 ANR。还请注意,该机制会顺序调用 SearchPrinter()
函数,指定几种不同的接口类型。这增加了您的应用程序成功找到打印机的机会。
IList<PortInfo> portList = StarIOPort.SearchPrinter(string target);
target 参数的有效值为:
BT
BT:<设备名称>
BT:<MAC地址>
TCP
TCP:<IP地址>
其中 <设备名称>
、<MAC地址>
和 <IP地址>
分别是设备名称、MAC 地址和 IP 地址。这些可以根据需要指定。
查找打印机端口
找到打印机后,您需要打开到它的连接。GetPort()
函数用于完成此操作。如果成功,它将返回一个打印机端口的句柄。
StarIOPort port = StarIOPort.GetPort(string portName, string portSettings, int timeoutMillisecs);
portName
是使用SearchPrinter()
函数之前找到的打印机的名称。- 即
portList[0].PortName
portSettings
用于以太网或蓝牙。就本文而言,我将只关注蓝牙。如果使用迷你打印机,则使用 mini 指定。所有蓝牙打印机都需要此设置。timeoutMillisecs
是在端口上读写时使用的超时(以毫秒为单位)。1000 毫秒为 1 秒。
Example usage
StarIOPort port = StarIOPort.GetPort(portList[0].PortName, "mini", 10000);
内存注意事项
在继续展示如何实际从打印机打印之前,需要注意的是,Android 设备是低功耗、低内存的设备。因此,它们缺乏像笔记本电脑或台式电脑那样的相应 CPU 和内存处理能力。内存限制是我在为 Android 平台实现打印功能时遇到的最大问题。
如果需要打印的文本包含图像(如徽标、文本框、签名等),那么您可能需要考虑创建一个位图图像并打印此图像。由于 Android 设备的内存限制,您可能需要考虑打印这些图像的频率。最初,我的应用程序在打印例程结束时一次性打印所有图像时抛出了“内存不足
”错误。我通过构建一个位图列表并一次打印一个位图到打印机来解决此问题。
这是应用程序使用的位图列表的类声明。
private static List<Bitmap> _imageList;
这是打印位图列表的代码。
//this is declared at the class level
private static readonly List<byte> InitialisePrinter = new List<byte> { Esc, 0x40 };
private static void PrintImageCollection(StarIOPort port)
{
try
{
if (_imageList != null && _imageList.Any())
{
foreach (var bmp in _imageList)
{
var command = new List<byte>();
var starBitmapReceipt = new StarBitmap(bmp, true, 832);
command.AddRange(InitialisePrinter);
command.AddRange(starBitmapReceipt.GetImageEscPosDataForPrinting(true, true));
port.WritePort(command.ToArray(), 0, command.Count);
}
_bitmap.Dispose();
_imageList.Clear();
}
}
catch (Exception ex)
{
throw ex;
}
}
此代码片段中需要注意的关键方法是 WritePort(byte[] writeBuffer, int offset, int size)
。
WritePort(byte[] writeBuffer, int offset, int size);
writeBuffer
是包含要写入数据的数组。offset
是写入数据的起始偏移量。size
是应该写入指针的数据量。
打印类的完整列表
在下面的示例代码中,打印了一些基本信息,即姓名和地址。要打印的每条信息都被逐一添加到位图图像中。位图被添加到图像列表中。打印例程会遍历此图像列表并打印各个位图。
using System;
using System.Collections.Generic;
using Android.Graphics;
using Android.Text;
using Com.Starmicronics.Stario;
public class Printing
{
public PrintForm()
{
private static Canvas _canvas;
private static Paint _paint;
private static Bitmap _bitmap;
private static TextPaint _textPaint;
private static readonly List<byte> InitialisePrinter = new List<byte> { Esc, 0x40 };
private static List<Bitmap> _imageList;
private static readonly Bitmap.Config BitmapConfig = Bitmap.Config.Argb4444;
public static void PrintDetails(StarIOPort port)
{
try
{
//we will assume that the methods SearchPrinter() and GetPort() have already been
//invoked at application startup and were successful
PrintText("\nMy Details", Bold, Layout.Alignment.AlignNormal, 26);
PrintText("Name", String.Format("{0}",
"Dominic Burford"), 400, false, false, rowHeight);
PrintText("Addr1", String.Format("{0}",
"1 High Street"), 400, false, false, rowHeight);
PrintText("Addr2", String.Format("{0}",
"London"), 400, false, false, rowHeight);
PrintText("Postcode", String.Format("{0}",
"XX1 1YY"), 400, false, false, rowHeight);
_imageList.Add(_bitmap.Copy(BitmapConfig, false));
NewBitmapImage(port);
//to print more information you will need to repeat the above steps i.e.
//PrintText("\nMy Further Details", Bold, Layout.Alignment.AlignNormal, 26);
//PrintText("Further Details 1", String.Format("{0}",
"details 1"), 400, false, false, rowHeight);
//PrintText("Further Details 2", String.Format("{0}",
"details 2"), 400, false, false, rowHeight);
//_imageList.Add(_bitmap.Copy(BitmapConfig, false));
//NewBitmapImage(port);
//etc
}
catch(Exception ex)
{
throw ex;
}
}
private static void NewBitmapImage(StarIOPort port)
{
try
{
if (port != null)
PrintImageCollection(port);
_bitmap.Dispose();
_bitmap = null;
_bitmap = Bitmap.CreateBitmap(832, 10, BitmapConfig);
_canvas = new Canvas(_bitmap);
_canvas.DrawColor(Color.White);
_canvas.Translate(0, 0);
}
catch (Exception ex)
{
throw ex;
}
}
private static void PrintImageCollection(StarIOPort port)
{
try
{
if (_imageList != null && _imageList.Any())
{
foreach (var bmp in _imageList)
{
var command = new List<byte>();
var starBitmapReceipt = new StarBitmap(bmp, true, 832);
command.AddRange(InitialisePrinter);
command.AddRange(starBitmapReceipt.GetImageEscPosDataForPrinting(true, true));
port.WritePort(command.ToArray(), 0, command.Count);
}
_bitmap.Dispose();
_imageList.Clear();
}
}
catch (Exception ex)
{
throw ex;
}
}
private static void PrintText(string text, Typeface typeface,
Layout.Alignment alignment, int textSize, int width = 832)
{
try
{
using (var details = _bitmap.Copy(BitmapConfig, false))
{
if (_paint == null)
_paint = new Paint();
_paint.SetTypeface(typeface);
_paint.TextSize = textSize;
_textPaint = new TextPaint(_paint);
var statlayout = new StaticLayout(text, _textPaint, width,
alignment, 1, 0, false);
_bitmap = Bitmap.CreateBitmap(832, statlayout.Height +
details.Height + 30, BitmapConfig);
_canvas = new Canvas(_bitmap);
_canvas.DrawColor(Color.White);
_canvas.Translate(0, 0);
_canvas.DrawBitmap(details, 0, 0, _paint);
_canvas.Translate(0, details.Height);
statlayout.Draw(_canvas);
_canvas.Translate(0, -details.Height);
details.Dispose();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
}
如果您有其他要打印的信息部分,则需要为要打印的每个信息部分重复以下步骤。
//repeat this for each piece of information you want to print
PrintText("\nSome text", Bold, Layout.Alignment.AlignNormal, 26);
//once you have collected your print information,
//you then add the corresponding bitmaps to your image collection for printing
_imageList.Add(_bitmap.Copy(BitmapConfig, false));
NewBitmapImage(port);
摘要
希望本文为您提供了足够的信心,可以开始在您自己的 Xamarin.Android
应用程序中进行打印。如果您希望我进一步阐述本文中的任何内容,请随时发表评论。