[转载]c#事件,委托机制 这个例子非常好哦.提供源码

mikel阅读(901)

[转载]c#事件,委托机制 这个例子非常好哦.提供源码,.一下子就了解了,还推荐去看 皮特的故事 要学委托 事件的话, – shenqiboy – 博客园.

事件(event)是一个非常重要的概念,我们的程序时刻都 在触发和接收着各种事件:鼠标点击事件,键盘事件,以及处理操作系统的各种事件。所谓事件就是由某个对象发出的消息。比如用户按下了某个按钮,某个文件发 生了改变,socket上有数据到达。触发事件的对象称作发送者(sender),捕获事件并且做出响应的对象称作接收者(receiver),一个事件 可以存在多个接受者。

在异步机制中,事件是线程之间进行通信的一个非常常用的方式。比如:用户在界面上按下一个按钮,执行某项耗时的任务。程 序此时启动一个线程来处理这个任务,用户界面上显示一个进度条指示用户任务执行的状态。这个功能就可以使用事件来进行处理。可以将处理任务的类作为消息的 发送者,任务开始时,发出“TaskStart”事件,任务进行中的不同时刻发出“TaskDoing”事件,并且携带参数说明任务进行的比例,任务结束 的时候发出“TaskDone”事件,在画面中接收并且处理这些事件。这样实现了功能,并且界面和后台执行任务的模块耦合程度也是最低的。

具体说C#语言,事件的实现依赖于“代理”(delegate)的概念,先了解一下代理。

代理(delegate)

delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate 类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传 递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数 指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数 入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对 象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。

实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:

1.  声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。

2.  创建delegate对象,并将你想要传递的函数作为参数传入。

3.  在要实现异步调用的地方,通过上一步创建的对象来调用方法。

下面是一个简单的例子:

public class MyDelegateTest

{

// 步骤1,声明delegate对象

public delegate void MyDelegate(string name);

// 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型

public static void MyDelegateFunc(string name)

{

Console.WriteLine(“Hello, {0}”, name);

}

public static void Main ()

{

// 步骤2,创建delegate对象

MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);

// 步骤3,调用delegate

md(“sam1111”);

}

}

输出结果是:Hello, sam1111

下面我们来看看事件是如何处理的:

事件(event)

C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:

public delegate void MyEventHandler(object sender, MyEventArgs e);

其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所 有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含特别的参数,那么可以直接用System.EventArgs类 作为参数。

结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:

1:定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。

2:定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。

3:定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。

4:用event关键字定义事件对象,它同时也是一个delegate对象。

5:用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。

6:在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是可以是OnEventName。

7:在适当的地方调用事件触发方法触发事件。

下面是一个例子,例子模仿容器和控件的模式,由控件触发一个事件,在容器中捕捉并且进行处理。

事件的触发者:

/// <summary>

/// 事件的触发者

/// </summary>

public class Control

{

public delegate void SomeHandler(object sender, System.EventArgs e);

/**

* 可以采用系统提供的System.EventHandler, 这里为了说明情况使用了自己定义的delegate

* 如果需要在事件的参数中使用自己定义的类型,也要自己定义delegate

*/

//public event System.EventHandler SomeEvent;

public event SomeHandler SomeEvent;

public Control()

{

//这里使用的delegate必须与事件中声名的一致

//this.SomeEvent += new System.EventHandler(this.Control_SomeEvent);

this.SomeEvent += new SomeHandler(this.ProcessSomeEvent);

}

public void RaiseSomeEvent()

{

EventArgs e = new EventArgs();

Console.Write(“Please input ‘a’:”);

string s = Console.ReadLine();

//在用户输入一个小a的情况下触发事件,否则不触发

if (s == “a”)

{

SomeEvent(this, e);

}

}

//事件的触发者自己对事件进行处理,这个方法的参数必须和代理中声名的一致

private void ProcessSomeEvent(object sender, EventArgs e)

{

Console.WriteLine(“hello”);

}

}

事件的接收者:

/// <summary>

/// 事件的接收和处理者

/// </summary>

class Container

{

private Control ctrl = new Control();

public Container()

{

//这里使用的delegate必须与事件中声名的一致

//ctrl.SomeEvent += new EventHandler(this.OnSomeEvent);

ctrl.SomeEvent += new Control.SomeHandler(this.ResponseSomeEvent);

ctrl.RaiseSomeEvent();

}

public static void Main()

{

Container pane = new Container();

//这个readline是暂停程序用的,否则画面会一闪而过什么也看不见

Console.ReadLine();

}

//这是事件的接受者对事件的响应

private void ResponseSomeEvent(object sender, EventArgs e)

{

Console.WriteLine(“Some event occur!”);

}

}

程序运行的结果如下:

please input ‘a’:a

hello

Some event occur!

事件的应用

例如有下面的需求需要实现:程序主画面中弹出一个子窗口。此时主画面仍然可以接收用户的操作(子窗口是非模态的)。子窗口上进行某些操作,根据操作的结果要在主画面上显示不同的数据。我发现一些程序员这样实现这个功能:

主画面弹出子窗口后,将自己的指针交给子画面,然后在子画面中使用这个指针,调用主画面提供的方法,改变主画面上的数据 显示。这样虽然可以达到目的,但是各个模块之间产生了很强的耦合。一般说来模块之间的调用应该是单方向的:模块A调用了模块B,模块B就不应该反向调用 A,否则就破坏了程序的层次,加强了耦合程度,也使得功能的改变和追加变得很困难。

这时正确的做法应该是在子窗口的操作过程中发出各种事件,而由主窗口捕捉这些事件进行处理,各个模块专心的做自己的事情,不需要过问其他模块的事情。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
class Program
{

public Program()
{
//这里使用的delegate必须与事件中声名的一致
//ctrl.SomeEvent += new EventHandler(this.OnSomeEvent);
ctrl.SomeEvent += new Control.SomeHandler(this.ResponseSomeEvent);
ctrl.RaiseSomeEvent();

}
private Control ctrl = new Control();
static void Main(string[] args)
{
Program pr = new Program();
Console.ReadLine();
}
//这是事件的接受者对事件的响应
private void ResponseSomeEvent(object sender, EventArgs e)
{
Console.WriteLine(“Some event occur!”);
}

}

/// <summary>
///事件触发者
/// </summary>
public class Control
{
public delegate void SomeHandler(object sender, System.EventArgs e);

/**
* 可以采用系统提供的System.EventHandler, 这里为了说明情况使用了自己定义的delegate
* 如果需要在事件的参数中使用自己定义的类型,也要自己定义delegate
*/
//public event System.EventHandler SomeEvent;
public event SomeHandler SomeEvent;
public Control()
{
//这里使用的delegate必须与事件中声名的一致
//this.SomeEvent += new System.EventHandler(this.Control_SomeEvent);
this.SomeEvent += new SomeHandler(this.ProcessSomeEvent);
}

public void RaiseSomeEvent()
{
EventArgs e = new EventArgs();
Console.Write(“Please input ‘a’:”);
string s = Console.ReadLine();

//在用户输入一个小a的情况下触发事件,否则不触发
if (s == “a”)
{
SomeEvent(this, e);
}
}

//事件的触发者自己对事件进行处理,这个方法的参数必须和代理中声名的一致
private void ProcessSomeEvent(object sender, EventArgs e)
{
Console.WriteLine(“hello”);
}
}

}

[转载]c#事件,委托机制 这个例子非常好哦.提供源码

mikel阅读(853)

[转载]c#事件,委托机制 这个例子非常好哦.提供源码,.一下子就了解了,还推荐去看 皮特的故事 要学委托 事件的话, – shenqiboy – 博客园.

事件(event)是一个非常重要的概念,我们的程序时刻都 在触发和接收着各种事件:鼠标点击事件,键盘事件,以及处理操作系统的各种事件。所谓事件就是由某个对象发出的消息。比如用户按下了某个按钮,某个文件发 生了改变,socket上有数据到达。触发事件的对象称作发送者(sender),捕获事件并且做出响应的对象称作接收者(receiver),一个事件 可以存在多个接受者。

在异步机制中,事件是线程之间进行通信的一个非常常用的方式。比如:用户在界面上按下一个按钮,执行某项耗时的任务。程 序此时启动一个线程来处理这个任务,用户界面上显示一个进度条指示用户任务执行的状态。这个功能就可以使用事件来进行处理。可以将处理任务的类作为消息的 发送者,任务开始时,发出“TaskStart”事件,任务进行中的不同时刻发出“TaskDoing”事件,并且携带参数说明任务进行的比例,任务结束 的时候发出“TaskDone”事件,在画面中接收并且处理这些事件。这样实现了功能,并且界面和后台执行任务的模块耦合程度也是最低的。

具体说C#语言,事件的实现依赖于“代理”(delegate)的概念,先了解一下代理。

代理(delegate)

delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate 类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传 递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数 指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数 入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对 象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。

实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:

1.  声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。

2.  创建delegate对象,并将你想要传递的函数作为参数传入。

3.  在要实现异步调用的地方,通过上一步创建的对象来调用方法。

下面是一个简单的例子:

public class MyDelegateTest

{

// 步骤1,声明delegate对象

public delegate void MyDelegate(string name);

// 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型

public static void MyDelegateFunc(string name)

{

Console.WriteLine(“Hello, {0}”, name);

}

public static void Main ()

{

// 步骤2,创建delegate对象

MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);

// 步骤3,调用delegate

md(“sam1111”);

}

}

输出结果是:Hello, sam1111

下面我们来看看事件是如何处理的:

事件(event)

C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:

public delegate void MyEventHandler(object sender, MyEventArgs e);

其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所 有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含特别的参数,那么可以直接用System.EventArgs类 作为参数。

结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:

1:定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。

2:定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。

3:定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。

4:用event关键字定义事件对象,它同时也是一个delegate对象。

5:用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。

6:在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是可以是OnEventName。

7:在适当的地方调用事件触发方法触发事件。

下面是一个例子,例子模仿容器和控件的模式,由控件触发一个事件,在容器中捕捉并且进行处理。

事件的触发者:

/// <summary>

/// 事件的触发者

/// </summary>

public class Control

{

public delegate void SomeHandler(object sender, System.EventArgs e);

/**

* 可以采用系统提供的System.EventHandler, 这里为了说明情况使用了自己定义的delegate

* 如果需要在事件的参数中使用自己定义的类型,也要自己定义delegate

*/

//public event System.EventHandler SomeEvent;

public event SomeHandler SomeEvent;

public Control()

{

//这里使用的delegate必须与事件中声名的一致

//this.SomeEvent += new System.EventHandler(this.Control_SomeEvent);

this.SomeEvent += new SomeHandler(this.ProcessSomeEvent);

}

public void RaiseSomeEvent()

{

EventArgs e = new EventArgs();

Console.Write(“Please input ‘a’:”);

string s = Console.ReadLine();

//在用户输入一个小a的情况下触发事件,否则不触发

if (s == “a”)

{

SomeEvent(this, e);

}

}

//事件的触发者自己对事件进行处理,这个方法的参数必须和代理中声名的一致

private void ProcessSomeEvent(object sender, EventArgs e)

{

Console.WriteLine(“hello”);

}

}

事件的接收者:

/// <summary>

/// 事件的接收和处理者

/// </summary>

class Container

{

private Control ctrl = new Control();

public Container()

{

//这里使用的delegate必须与事件中声名的一致

//ctrl.SomeEvent += new EventHandler(this.OnSomeEvent);

ctrl.SomeEvent += new Control.SomeHandler(this.ResponseSomeEvent);

ctrl.RaiseSomeEvent();

}

public static void Main()

{

Container pane = new Container();

//这个readline是暂停程序用的,否则画面会一闪而过什么也看不见

Console.ReadLine();

}

//这是事件的接受者对事件的响应

private void ResponseSomeEvent(object sender, EventArgs e)

{

Console.WriteLine(“Some event occur!”);

}

}

程序运行的结果如下:

please input ‘a’:a

hello

Some event occur!

事件的应用

例如有下面的需求需要实现:程序主画面中弹出一个子窗口。此时主画面仍然可以接收用户的操作(子窗口是非模态的)。子窗口上进行某些操作,根据操作的结果要在主画面上显示不同的数据。我发现一些程序员这样实现这个功能:

主画面弹出子窗口后,将自己的指针交给子画面,然后在子画面中使用这个指针,调用主画面提供的方法,改变主画面上的数据 显示。这样虽然可以达到目的,但是各个模块之间产生了很强的耦合。一般说来模块之间的调用应该是单方向的:模块A调用了模块B,模块B就不应该反向调用 A,否则就破坏了程序的层次,加强了耦合程度,也使得功能的改变和追加变得很困难。

这时正确的做法应该是在子窗口的操作过程中发出各种事件,而由主窗口捕捉这些事件进行处理,各个模块专心的做自己的事情,不需要过问其他模块的事情。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
class Program
{

public Program()
{
//这里使用的delegate必须与事件中声名的一致
//ctrl.SomeEvent += new EventHandler(this.OnSomeEvent);
ctrl.SomeEvent += new Control.SomeHandler(this.ResponseSomeEvent);
ctrl.RaiseSomeEvent();

}
private Control ctrl = new Control();
static void Main(string[] args)
{
Program pr = new Program();
Console.ReadLine();
}
//这是事件的接受者对事件的响应
private void ResponseSomeEvent(object sender, EventArgs e)
{
Console.WriteLine(“Some event occur!”);
}

}

/// <summary>
///事件触发者
/// </summary>
public class Control
{
public delegate void SomeHandler(object sender, System.EventArgs e);

/**
* 可以采用系统提供的System.EventHandler, 这里为了说明情况使用了自己定义的delegate
* 如果需要在事件的参数中使用自己定义的类型,也要自己定义delegate
*/
//public event System.EventHandler SomeEvent;
public event SomeHandler SomeEvent;
public Control()
{
//这里使用的delegate必须与事件中声名的一致
//this.SomeEvent += new System.EventHandler(this.Control_SomeEvent);
this.SomeEvent += new SomeHandler(this.ProcessSomeEvent);
}

public void RaiseSomeEvent()
{
EventArgs e = new EventArgs();
Console.Write(“Please input ‘a’:”);
string s = Console.ReadLine();

//在用户输入一个小a的情况下触发事件,否则不触发
if (s == “a”)
{
SomeEvent(this, e);
}
}

//事件的触发者自己对事件进行处理,这个方法的参数必须和代理中声名的一致
private void ProcessSomeEvent(object sender, EventArgs e)
{
Console.WriteLine(“hello”);
}
}

}

[转载]Benchmark Test On Android Devices

mikel阅读(861)

[转载]Benchmark Test On Android Devices – 进则净土,退则凡尘 – 博客园.

一、Android设备上的Benckmark测试概述

同PC相比,在Android设备上的性能测试还没有一个公认的标准。也没有PC上那么多的测试程序集。但我们可以通过一些工具所得到的信息更好的 了解设备的特性,毫无疑问,这也是非常有价值的参考信息。Benchmark Test的关键就在于如何找到这一个标准以及能够提供测试各种性能的功能强大的程序集。
二、目前Android平台上的测试工具收集(以Android手机为例)
1、  Quadrant(Aurora Softworks推出)

针对CPU、内存、I/O输入输出、2D及3D图像的性能,提供了一键式的完整测试,或是根据需要选择其中某些测试项目单独测试。它还提供了内容格式清晰的系统信息查看功能,让你方便的了解非常详尽的手机处理器、内存及系统版本等特性。

该公司网站为:http://www.aurorasoftworks.com
版本一:Quadrant Standard

免费版,2010年8月18日更新最新版本v1.1.2,在Google应用商店有超过3000个评论,综合评分高达4.5星,免费的标准版可以提供所有的测试功能,唯一不便的地方是在测试后需要向极光软件(Aurora Softworks)的服务器提交数据进行分析后,才能显示结果。
版本二:Quadrant Advanced

高级版,2010年8月8日更新最新版本v1.1.1,售价2.99美元,这个版本在测试功能上与免费的标准版完全一样,但在测试结束后会直接显示一个测试结果直方图:

版本三:Quadrant Professional

专业版,2010年8月8日更新最新版本v1.1.1,售价高达24.99美元,主要的差别其实就是“授权”费,允许使用此版本进行测试获得的结果可以用于商业用途,包括将测试结果出版在商业媒体上。
2、  Benchmark 1.03

一个测试系统性能的软件,可以测试Android系统的2D图形、cpu、内存和文件系统等方面的性能.测试项目非常多,且测试速度快,可以给出一个参考 分数供用户参考使用。
3、  Linpack for android

用于测试系统整体性能,偏向单任务下的表现。

官方网址:http://www.greenecomputing.com/apps/linpack/
4、  Sunspider benchmark

测试webkit内核浏览器的JavaScript表现。

测试地址:

http://www.webkit.org/perf/sunspider-0.9.1/sunspider-0.9.1/driver.html

测试方式:直接在浏览器上打开该网页即可,测试结果会直接显示在网页上。
5、  Google V8 benchmark

Google V8 Benchmark测试是Google放出的JavaScript测试集。V8 Benchmark Suite会载入JavaScript代码,对系统的内核、加密、解密、渲染等进行速度测试。

测试地址:http://v8.googlecode.com/svn/data/benchmarks/v6/run.html

测试方式:直接在浏览器上打开该网页即可,测试结果会直接显示在网页上。

说明:测试得分越高说明浏览器的网页显示速度越快。
6、  Dromaeo benchmark

由Mozilla基金会发布,它提供了浏览器表现的第三方视角。

测试网址:http://dromaeo.com/

测试方式:在浏览器上打开该网页,可以选择自己想做的测试。

[转载]所见即所得的Excel报表生成—获取Html table结构

mikel阅读(1223)

[转载]所见即所得的Excel报表生成(一)——获取Html table结构 – iSun – 博客园.

最近做一个小的报表系统,功能本身没什么。最后客户要求一个打印功能,所谓打印,就是按照页面上报表的样子,一模一样的为其生成Excel文件。

再也不想为了构造结构一样的Excel表格而再次考虑繁琐数据逻辑了!于是乎冒出了这样的一个想法:我要是能获得页面上的报表table,那么只要分析其结构,不就可以构造出相应的Excel表格来吗?

思来想去,觉得这应该是一条可以走的通的路,于是便着手寻找实现的办法。终于,发现WebRequest和WebResponse,其分别代表一个Web请求和服务器基于请求的回应,而这个回应包含服务器返回的数据流,从数据流便可获取想要的报表table。

整个Excel生成功能的思路如下图所示:

涉及WebRequest、WebResponse的核心代码如下:

WebRequest wr = WebRequest.Create(psUrl);
HttpWebRequest oRequest = wr as HttpWebRequest;
if (null == oRequest) return null;

HttpWebResponse oResponse  ;= (HttpWebResponse) oRequest.GetResponse();
string sResponseContent = string.Empty;
if (oResponse.StatusCode == HttpStatusCode.OK)
{
using (StreamReader sr = new StreamReader(oResponse.GetResponseStream(),Encoding.UTF8))
{
sResponseContent = sr.ReadToEnd();      //sResponseContent,保存了服务器返回的数据流字符串内容
sr.Close();
}
}
oResponse.Close();

特别需要说明的是,在从数据流向字符串转化的过程中,其内会残留许多\r\t之类的转义字符,我们需要将其剔除。

1 sResponseContent = Regex.Replace (sResponseContent, [\n\r\t] , “”);

本篇至此,下篇将着重介绍table到Excel cell的转化过程。

附示例代码:ExcelGenerator.rar

Html table的结构我们大家都很熟悉,那么在另一端如何构造一个结构,让Excel可以很好的接受和处理呢?

直观的看,一个完整Excel的内容是由位于各个单元格(Cell)中的内容组合而成的。而每个单元格(Cell)都有相应X、Y坐标来标示其 位置。也就是说,一个Excel文件实质上就是许多Cell构成的集合,每个Cell用坐标属性确定位置,用内容属性存储内容。

基于此,我设计了最基本的Cell结构:
◆ X坐标
◆ Y坐标
◆ 合并列情况
◆ 合并行情况
◆ 内容

构成Excel的最基本的结构已经确定,下一步摆在我们面前的就是将html table转化为Excel Cell集合。

Html table中的每个td节点对应一个Excel单元格,其内容不必说,行、列的合并情况也自可由td的rowspan、colspan属性得出,转化的关键点就在于由table的tr td结构定位Excel单元格位置,即X、Y坐标。

Y坐标容易确定,即td所在tr的行数。至于一td的X坐标,其要受到两方面因素的影响:与该td同处一tr,但位于其之前(反映在表格视觉上即其左侧td)td的占位情况和该td所在tr的之前tr中某些td的跨行情况。

基于此种考虑,定位td的X坐标需经过两个过程的推导:用于处理左侧td占位影响的横向推导(Horizontal Deduction)和处理之前行跨行td影响的纵向推导(Vertical Deduction)。

以下图所示table为例,展示两次推导过程。

横向推导(Horizontal Deduction)
一次横向推导(Horizontal Deduction)限定在一tr范围内。整个过程基于递归的原理,递归模型如下:

核心代码为:

1 private int HorizontalDeduction(HtmlNode phnTd)
2 {
3 HtmlNode hnPreviousSibling = phnTd.PreviousSibling;
4 while (hnPreviousSibling != null && hnPreviousSibling.Name != phnTd.Name)
5 {
6 hnPreviousSibling = hnPreviousSibling.PreviousSibling;
7 }
8
9 if (hnPreviousSibling != null)
10 {
11 int nColSpan = hnPreviousSibling.GetAttributeValue(colspan, 1);
12 return HorizontalDeduction(hnPreviousSibling) + nColSpan;
13 }
14
15 return 0;
16 }

经过横向推导,各td的X、Y坐标如下图所示:

纵向推导(Vertical Deduction)
一次纵向推导的过程可以描述为(当前推导td用A表示):

找到A之前的行tr中与A具有相同X坐标的td节点B

if (B.rowspan>(A.Y-B.Y))
{
X+=B.colspan,即A的X坐标向后推B.colspan的位置

同时,与A同处一tr但在其后边的td节点均应向后推B.colspan个位移
}

对td节点A反复执行这样的一个过程,直到确定A无需再次移动。

纵向推导核心代码为:

1 bool bActedPush = false;
2
3 do
4 {
5 int nComparedItemIndex = 1;
6 for (int j = i 1; j >= 0; j)
7 {
8 if (plstCells[j]._nStartX == oCurrentCell._nStartX)
9 {
10 nComparedItemIndex = j;
11 break;
12 }
13 }
14
15 if (nComparedItemIndex >= 0)
16 {
17 if (plstCells[nComparedItemIndex]._nRowSpan > (oCurrentCell._nStartY plstCells[nComparedItemIndex]._nStartY))
18 {
19 oCurrentCell._nStartX += plstCells[nComparedItemIndex]._nColSpan;
20
21 bActedPush = true;
22
23 for (int k = i + 1; k < plstCells.Count; k++)
24 {
25 if (plstCells[k]._nStartY == oCurrentCell._nStartY)
26 {
27 plstCells[k]._nStartX += plstCells[nComparedItemIndex]._nColSpan;
28 }
29 }
30 }
31 else
32 {
33 bActedPush = false;
34 }
35 }
36 else
37 {
38 bActedPush = false;
39 }
40 }
41 while (bActedPush);

以示例table中的four td为例,其经过纵向推导过程后的坐标位置情况如下图:

关于示例代码的几点说明:
1、 在示例代码中,我通过一个Config文件对生成的Excel的文件名、其内报表的内容和位置做了一些控制。基本内容如下:

<ExcelDocument>
<BaseInfo>
<FileName>Sample Excel File</FileName> <!–生成Excel的文件名–>
<SheetCount>1</SheetCount> <!–Excel中sheet的数量–>
</BaseInfo>
<Tables>
<ExcelTable>
<TableName>示例表一</TableName> <!–报表名称–>
<WhichSheet>1</WhichSheet> <!–所在sheet的序号–>
<StartX>2</StartX> <!–左上角X坐标–>
<StartY>2</StartY> <!–左上角Y坐标–>
<Source>Sample_Page_1.aspx</Source> <!–table所在页面–>
</ExcelTable>
</Tables>
</ExcelDocument>

2、 在解析html的过程中,我使用了HtmlAgilityPack解析工具。感兴趣的朋友可以研究一下。地址是这里:http://htmlagilitypack.codeplex.com/
3、 HtmlAgilityPack解析html的过程中,其将html标签之间的空隙也会看成是一个节点,为内容为空字符串的文本节点,这点大家应注意。
4、 该示例代码基本上是一个完整的功能,并且和系统中其他模块耦合度很小。有类似需求的朋友可以拿来直接用。

附示例代码:ExcelGenerator.rar

[转载]AgileEAS.NET平台-文档发布-还等什么?

mikel阅读(960)

[转载]AgileEAS.NET平台-文档发布-还等什么? – CallHot ‘s Perfessional Space – 博客园.

介绍

AgileEAS.NET应用开发平台,简称EAS.NET,是基于敏捷并行开发思想以及.NET构件技术而开发的一个应用系统快速开发平台,用于帮助中小型软件企业建立

一条适合快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的,AgileEAS.NET应用开发平台包含基础类库、资源管理平台、运行

容器、开发辅助工具等四大部分,资源管理平台为敏捷并行开发提供了设计、实现、测试等开发过程的并行,基于AgileEAS.NET平台的应用系统的各个业务功能子

系统,在系统体系结构设计的过程中被设计成各个原子功能模块,各个子功能模块按照业务功能组织成单独的程序集文件,各子系统开发完成后,由AgileEAS.NET

资源管理平台进行统一的集成部署。

AgileEAS.NET应用开发平台也是为应用信息系统开发而提供的一组低层功能集合及开发支撑平台,应用信息系统的开发建立在此平台之上,采用构件式、可复

用开发,节省开发成本,加快开发速度,在软件开发上更好的作到多快省。

AgileEAS.NET平台的核心思想是包含两点,一是基于DotNET构件技术的插件式开发,二是基于敏捷并行开发方法以的构件并行,即应用系统的构件(模块)

同步并行开发,由平台进行总装集成。

image

AgileEAS.NET提供了强大的底层支撑能力,快速的开发,高效的质量都是该平台的优点。

AgileEAS.NET平台功能图

AgileEAS.NET平台的核心理念涵盖两个方面,第一方面是基于一种软件工程的实践,插件模块独立并行开发和总装集成的一种思路,第二方面则是利用.NET技术(反射调

用)实现了这种思想。由此可知EAS.NET应该包含以下四部分内容:第一、软件过程工程的支持,第二、插件标准与平台(运行容器),第三、插件的组织及管理,第四、支持插件

快速开发的技术及工具。

AgileEAS.NET主要提供的功能支持及工具:

image

除了上述的主要功能模块外,还有其他的后续要推出的功能及工具

image

上面列出了该平台的强大功能及便携性,不断完善的团队及专家,将会致力于打造一个开放的平台。

AgileEAS.NET相关资料

可能大伙看了上面的平台列出的一些大体的功能,可能大家会有一些兴趣跃跃欲试,但是如果一个平台功能在强大,不好用,或者让人很难学习也是很难受欢迎的,当然

如果功能非常的强大也是可能的,为了让大家能够对感兴趣的东西能够快速的上手实践,那么我这里放出我这段时间整理的相关资料,我也是在这段时间学习了不少的知识,

通过这段时间的整理,让我对这个平台的强大和便易之处有了更深入的了解。

1、平台的开发资料:

平台的相关介绍、平台架构及相关案例、平台类库文档的详细说明书,平台相关资料的完整版本。

下载

2、平台的功能工具与不同工程之间的无缝集成

提供平台在其他软件环境下的集成开发案例,不同的软件开发人员都可以使用该平台集成到现有的软件开发工具中,平台提供的相关功能都可以单独脱离平台来使用。具

体的相关使用细节,请参考说明文档。

下载

3、AgileEAS.NET开发的相关案例及程序集包

包含AgileEAS.NET平台的最新软件程序集,并且提供相关的运行环境及数据库,下载即可。

下载

其他资料

一、系统架构+设计模式+ORM文件整理

我把原来书写的相关的文章进行了整理,整理成chm文档,方便大家的阅读,当然由于个人准备的比较仓促,所以造成可能部分格式或者内容收录不全,还请大家见

谅。

下载

相关联系

如果您在使用AgileEAS.NET开发平台中有什么问题,请使用如下几种联系方式或者沟通方式。

1、邮箱方式

魏先生: mail.james@qq.com

殷先生:549558408@qq.com

何先生:hegezhou_hot@163.com

2、QQ群

AgileEAS.NET[高级群]:116773358(空12人)

系统架构交流群:9105332(已满)
系统架构交流群1:124825459(已满)
系统架构交流群2:49124441(已满)
系统架构交流群3:47025564(新创建)

3、网站留言

请访问下网站留言,谢谢!

留言

作者:CallHot
出处:http://www.cnblogs.com/hegezhou_hot/
关于作者:专注于微软平台项目架构、管理和企业解决方案。熟悉设计模式、极限编程、架构设计、敏捷开发和项目管理。现主要从事WinForm、ASP.NET、等方面的项目开发、架构、管理工作。如有问题或建议,请多多赐教!
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过hegezhou_hot@163.com 联系我,非常感谢。
其他联系方式:
系统架构交流群:9105332(已满)
系统架构交流群1:124825459(已满)
系统架构交流群2:49124441(已满)
系统架构交流群3:47025564(新创建)

[转载]Android源码轻松附加到Eclipse

mikel阅读(838)

想提高Android开发水平,学习源码是最好的途径之一,下面谈谈怎样让Elipse关联Android源码

1、下载Android源码

下面有Android 2.1的源码,只有约7M

更多版本下载 http://repository.grepcode.com/java/ext/com/google/android/android/

2、解压源码,将文件夹命名为sources,再拷贝到<android-sdk-windows>/platforms/对应版本中,

如<android-sdk-windows>/platforms/android-7

现在打开Eclipse,选中某个Android类,按Ctrl,单击鼠标,跳转到相关类中,前提是<android-sdk-windows>/platforms/项目版本  中有源码。

本文附件下载:

[转载]在Android中使用Handler和Thread线程执行后台操作

mikel阅读(979)

[转载]在Android中使用Handler和Thread线程执行后台操作 – 疯狂の小石子 – 博客园.

标题:在Android中使用Handler和Thread线程执行后台操作

作者:CrazyPebble

时间:2011年3月23日

声明:此文在参考《解密Google Android》一书 和 Android视频教程(www.mars-droid.com)。文中存在错误之处,还请各位批评指正。若转载本文,请指明转载出处:http://www.cnblogs.com


大家都知道,在PC上的应用程序当需 要进行一些复杂的数据操作,但不需要界面UI的时候,我们会为应用程序专门写一个线程去执行这些复杂的数据操作。通过线程,可以执行例如:数据处理、数据 下载等比较耗时的操作,同时对用户的界面不会产生影响。在Android应用程序开发中,同样会遇到这样的问题。当我们需要访问网络,从网上下载数据并显 示在我们的UI上时,就会启动后台线程去下载数据,下载线程执行完成后将结果返回给主用户界面线程。

对于线程的控制,我们将介绍一个 Handler类,使用该类可以对运行在不同线程中的多个任务进行排队,并使用Message和Runnable对象安排这些任务。在javadoc中, 对Handler是这样解释的:Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联。每 个Handler的实例都关联了一个线程和线程的消息队列。当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将 发送和处理这些消息或Runnable对象。

下面有几种对Handler对象的构造方法需要了解一下:

a、如果new一个无参构造函数的Handler对象,那么这个Handler将自动与当前运行线程相关联,也就是说这个Handler将与当前运行的线程使用同一个消息队列,并且可以处理该队列中的消息。

private Handler handler = new Handler();

我们做这样一个实验,在主用户界面中 创建一个带有无参构造函数的Handler对象,该Handler对象向消息队列推送一个Runnable对象,在Runnable对象的run函数中打 印当前线程Id,我们比较主用户界面线程ID和Runnable线程ID是否相同。具体代码如下:

HandlerTest01

public class HandlerTest01 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); System.out.println("Activity ---> " + Thread.currentThread().getId()); handler.post(r); } private Handler handler = new Handler(); private Runnable r = new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("Runnalbe ---> " + Thread.currentThread().getId()); } }; }

通过这个例子的输出可以发 现,Runnable对象和主用户界面线程的ID是相同。在这个例子中,我们直接利用handler对象post了一个runnable对象,相当于直接 调用了Runnable对象的run函数,也就说没有经过start函数调用run(),那么就不会创建一个新线程,而是在原有线程内部直接调用 run()方法,因此输出的线程Id是相同的。

b、如果new一个带参构造函数的Handler对象,那么这个Handler对象将与参数所表示的Looper相关联。注意:此时线程类应该是一个特殊类HandlerThread类,一个Looper类的Thread类,它继承自Thread类。

HandlerThread handlerthread = new HandlerThread("MyThread");
handlerthread.start(); private MyHandler handler = new MyHandler(handlerthread.getLooper()); class MyHandler extends Handler { public MyHandler() { } public MyHandler(Looper looper) { super(looper); } }

下面这个例子,将介绍如何开启一个新的线程,并通过Handler处理消息。

HandlerTest02.java

public class HandlerTest02 extends Activity { private MyHandler myhandler = null; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); this.setContentView(R.layout.main); System.out.println("Activity ---> " + Thread.currentThread().getId()); // 生成一个HandlerThread对象,使用Looper来处理消息队列 HandlerThread thread = new HandlerThread("MyThread"); // 必须启动这个线程 thread.start(); // 将一个线程绑定到Handler对象上,则该Handler对象就可以处理线程的消息队列 myhandler = new MyHandler(thread.getLooper()); // 从Handler中获取消息对象 Message msg = myhandler.obtainMessage(); // 将msg对象发送给目标对象Handler msg.sendToTarget(); } class MyHandler extends Handler { public MyHandler() { } // 带有参数的构造函数 public MyHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { System.out.println("MyHandler ---> " + Thread.currentThread().getId()); } } }

根据这个例子返回的结果,可以看出,新线程Id与主用户界面的线程Id不同。由于我们调用了thread.start()方法,真正的创建了一个新线程,与原来的线程处于不同的线程上下文中,因此打印输出的线程Id是不同的。

c、如果需要Handler对象去处理消息,那么就要重载Handler类的handleMessage函数。

private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // TODO : Handle the msg // Usually we update UI here. } }

注意到注释部分,我们通常在handleMessage中处理更新UI界面的操作。

前面介绍了Handler类的基本使用,但是还是没有涉及到Thread类。要想实现在后台重新开启一个新的线程,通过该线程执行一些费时的操作,我们也使用Thread类来完成这个功能。下面我们先给出一个使用Thread类的例子程序。

ThreadTest.java

public class ThreadTest extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); this.setContentView(R.layout.main); System.out.println("Activity ---> " + Thread.currentThread().getId()); Thread thread = new Thread(r); thread.start(); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } thread.stop(); } Runnable r = new Runnable() { @Override public void run() { System.out.println("Runnable ---> " + Thread.currentThread().getId()); } }; }

这个程序执行的结果如下。新线程在创建对象时,传入了Runnable类的一个对象,在Runnable对象中重载了run()方法去执行耗时的操作;新的线程实例执行了start方法,开启了一个新的线程执行Runnable的run方法。


上面这些就是我现在接触到执行线程的方法,在线程中,可以完成我们所需要的操作(比如:下载,处理数据,检测网络状态等),使其与UI界面分离,那么UI界面不会因为耗时操作导致界面被阻塞。

在《解密Google Android》一书中,发现了这样一个启动线程的模型。利用该模型,我们可以把一些耗时的操作放到doStuff方法中去执行,同时在 updateUIHere方法中进行更新UI界面的操作,就可以完成一个线程所需要的功能。其他的说明写在注释部分了。

Handler myHandler = new Handler() { public void handleMessage(Message msg) { updateUIHere(); } } new Thread() { public void run() { doStuff(); // 执行耗时操作 Message msg = myHandler.obtainMessage(); Bundle b = new Bundle(); b.putString("key", "value"); m.setData(b); // 向消息中添加数据 myHandler.sendMessage(m); // 向Handler发送消息,更新UI } }.start();


CrazyPebble

[转载]Android ListView性能优化之视图缓存

mikel阅读(1039)

[转载][Android]ListView性能优化之视图缓存 – 农民伯伯 – 博客园.

前言

ListView是Android中最常用的控件,通过适配器来进行数据适配然后显示出来,而其性能是个很值得研究的话题。本文与你一起探讨Google I/O提供的优化Adapter方案,欢迎大家交流。

声明

欢迎转载,但请保留文章原始出处:)

博客园:http://www.cnblogs.com

农民伯伯: http://over140.cnblogs.com

正文

一、准备

1.1  了解关于Google IO大会关于Adapter的优化,参考以下文章:

Android开发之ListView 适配器(Adapter)优化

Android开发——09Google I/O之让Android UI性能更高效(1)

PDF下载:Google IO.pdf

1.2  准备测试代码:

Activity

private TestAdapter mAdapter;

private String[] mArrData;
private TextView mTV;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTV
= (TextView) findViewById(R.id.tvShow);

mArrData = new String[1000];
for (int i = 0; i < 1000; i++) {
mArrData[i]
= Google IO Adapter” + i;
}
mAdapter
= new TestAdapter(this, mArrData);
((ListView) findViewById(Android.R.id.list)).setAdapter(mAdapter);
}

代码说明:模拟一千条数据,TestAdapter继承自BaseAdapter,main.xml见文章末尾下载。

二、测试

测试方法:手动滑动ListView至position至50然后往回滑动,充分利用convertView不等于null的代码段。

2.1  方案一

按照Google I/O介绍的第二种方案,把item子元素分别改为4个和10个,这样效果更佳明显。

2.1.1  测试代码

private int count = 0;
private long sum = 0L;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//开始计时
long startTime = System.nanoTime();

if (convertView == null) {
convertView
= mInflater.inflate(R.layout.list_item_icon_text,
null);
}
((ImageView) convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
((TextView) convertView.findViewById(R.id.text1)).setText(mData[position]);
((ImageView) convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
((TextView) convertView.findViewById(R.id.text2)).setText(mData[position]);

//停止计时
long endTime = System.nanoTime();
//计算耗时
long val = (endTime startTime) / 1000L;
Log.e(
Test, Position: + position + : + val);
if (count < 100) {
if (val < 1000L) {
sum
+= val;
count
++;
}
}
else
mTV.setText(String.valueOf(sum
/ 100L));//显示统计结果
return convertView;
}

2.1.2  测试结果(微秒除以1000,见代码)

次数 4个子元素 10个子元素
第一次 366 723
第二次 356 689
第三次 371 692
第四次 356 696
第五次 371 662
2.2  方案二

按照Google I/O介绍的第三种方案,是把item子元素分别改为4个和10个。

2.2.1  测试代码

private int count = 0;
private long sum = 0L;

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 开始计时
long startTime = System.nanoTime();

ViewHolder holder;
if (convertView == null) {
convertView
= mInflater.inflate(R.layout.list_item_icon_text,
null);
holder
= new ViewHolder();
holder.icon1
= (ImageView) convertView.findViewById(R.id.icon1);
holder.text1
= (TextView) convertView.findViewById(R.id.text1);
holder.icon2
= (ImageView) convertView.findViewById(R.id.icon2);
holder.text2
= (TextView) convertView.findViewById(R.id.text2);
convertView.setTag(holder);
}
else{
holder
= (ViewHolder)convertView.getTag();
}
holder.icon1.setImageResource(R.drawable.icon);
holder.text1.setText(mData[position]);
holder.icon2 .setImageResource(R.drawable.icon);
holder.text2.setText(mData[position]);

// 停止计时
long endTime = System.nanoTime();
// 计算耗时
long val = (endTime startTime) / 1000L;
Log.e(
Test, Position: + position + : + val);
if (count < 100) {
if (val < 1000L) {
sum
+= val;
count
++;
}
}
else
mTV.setText(String.valueOf(sum
/ 100L));// 显示统计结果
return convertView;
}
}

static class ViewHolder {
TextView text1;
ImageView icon1;
TextView text2;
ImageView icon2;
}

2.2.2  测试结果(微秒除以1000,见代码)

次数 4个子元素 10个子元素
第一次 311 417
第二次 291 441
第三次 302 462
第四次 286 444
第五次 299 436
2.3  方案三

此方案为“Henry Hu”提示,API Level 4以上提供,这里顺带测试了一下不使用静态内部类情况下性能。

2.3.1  测试代码
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 开始计时
long startTime = System.nanoTime();

if (convertView == null) {
convertView
= mInflater.inflate(R.layout.list_item_icon_text, null);
convertView.setTag(R.id.icon1, convertView.findViewById(R.id.icon1));
convertView.setTag(R.id.text1, convertView.findViewById(R.id.text1));
convertView.setTag(R.id.icon2, convertView.findViewById(R.id.icon2));
convertView.setTag(R.id.text2, convertView.findViewById(R.id.text2));
}
((ImageView) convertView.getTag(R.id.icon1)).setImageResource(R.drawable.icon);
((ImageView) convertView.getTag(R.id.icon2)).setImageResource(R.drawable.icon);
((TextView) convertView.getTag(R.id.text1)).setText(mData[position]);
((TextView) convertView.getTag(R.id.text2)).setText(mData[position]);

// 停止计时
long endTime = System.nanoTime();
// 计算耗时
long val = (endTime startTime) / 1000L;
Log.e(
Test, Position: + position + : + val);
if (count < 100) {
if (val < 1000L) {
sum
+= val;
count
++;
}
}
else
mTV.setText(String.valueOf(sum
/ 100L) + : + nullcount);// 显示统计结果
return convertView;
}

2.3.2  测试结果(微秒除以1000,见代码)

第一次:450

第二次:467

第三次:472

第四次:451

第五次:441
四、总结

4.1  首先有一个认识是错误的,我们先来看截图:

可以发现,只有第一屏(可视范围)调用getView所消耗的时间远远多于后面的,通过对

convertView == null内代码监控也是同样的结果。也就是说ListView仅仅缓存了可视范围内的View,随后的滚动都是对这些View进行数据更新。 不管你有多少数据,他都只用ArrayList缓存可视范围内的View,这样保证了性能,也造成了我以为ListView只缓存View结构不缓存数据 的假相(不会只有我一人这么认为吧- – #)。这也能解释为什么GOOGLE优化方案一比二高很多的原因。那么剩下的也就只有findViewById比较耗时了。据此大家可以看看 AbsListView的源代码,看看

obtainView这个方法内的代码及RecycleBin这个类的实现,欢迎分享。

此外了解这个原理了,那么以下代码不运行你可能猜到结果了:

if (convertView == null) {
convertView
= mInflater.inflate(R.layout.list_item_icon_text, null);
((ImageView) convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
((TextView) convertView.findViewById(R.id.text1)).setText(mData[position]);
((ImageView) convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
((TextView) convertView.findViewById(R.id.text2)).setText(mData[position]);
}
else
return convertView;

没错,你会发现滚动时会重复显示第一屏的数据!

子控件里的事件因为是同一个控件,也可以直接放到convertView == null 代码块内部,如果需要交互数据比如position,可以通过tag方式来设置并获取当前数据。

4.2  本文方案一与方案二对比

这里推荐如果只是一般的应用(一般指子控件不多),无需都是用静态内部类来优化,使用第二种方案即可;反之,对性能要求较高时可采用。此外需要 提醒的是这里也是用空间换时间的做法,View本身因为setTag而会占用更多的内存,还会增加代码量;而findViewById会临时消耗更多的内 存,所以不可盲目使用,依实际情况而定。

4.3  方案三

此方案为“Henry Hu”提示,API Level 4以上支持,原理和方案三一致,减少findViewById次数,但是从测试结果来看效果并不理想,这里不再做进一步的测试。

五、推荐文章

Android,谁动了我的内存(1)

Android 内存泄漏调试

结束

对于Google I/O大会这个优化方案一直抱迟疑态度,此番测试总算是有了更进一步的了解,欢迎大家先测试后交流,看看还有什么办法能够再优化一点。

[转载]《Android学习指南》目录

mikel阅读(834)

[转载]《Android学习指南》目录【全】 – qixiinghaitang – 博客园.

课程 描述
第一讲:Android开发环境的搭建
第二讲:Android系统构架分析和应用程序目录结构分析
第三讲:Android模拟器的使用 emulator
第四讲:Activity入门指南 Activity
第五讲:用户界面 View(一) FrameLayout, LinearLayout
第六讲:用户界面 View(二) AbsoluteLayout,RelativeLayout
第七讲:用户界面 View(三) TableLayout
第八讲:Intent入门指南 Intent
第九讲:用户界面 View(四) Button TextView EditView CheckBox

RadioGroup RadioButton ImageView ImageButton

第十讲:用户界面 View(五) Spinner,AutoCompleteTextView

DatePicker,TimePicker

第十一讲:用户界面 View(六) ProgressBar SeekBar RatingBar
第十二讲:用户界面 View(七) ListView
第十三讲:用户界面 View(八) Gallery,GridView
第十四讲:Service入门指南
第十五讲:SQLite入门指南
第十六讲:菜单 Android Menu
第十七讲:对话框 Android Dialog
第十八讲:Android SharedPreferences和File
第十九讲:Android Notification的使用入门
第二十讲:Content Provider 使用入门
第二十一讲:Broadcast Receiver 使用入门
第二十二讲:AIDL和远程Service调用 未写
第二十三讲:Drawable使用入门
第二十四讲:Android动画入门(一) Teen Animation,Frame Animation
第二十五讲:Android动画入门(二) SurfaceView 窈窈莫尔斯灯塔
第二十六讲:Android中的GPS应用入门
第二十七讲:Handler使用入门
第二十八讲:Android多媒体(Media)入门 音乐播放、视频播放、声音录制,窈窈录音
第二十九讲:WebView学习指南
第三十讲:URLConnection和HttpClient使用入门 读取Google天气预报信息
第三十一讲:在Android中解析XML 解析Google天气预报信息
第三十二讲:Android中的主题和风格学习指南 Style, Theme
第三十三讲:自定义Android UI组件 未写
第三十四讲:Android Timer 计时器 火箭发射倒计时
第三十五讲:App Widget入门指南 Hello,App Widget! ,音乐播放器小部件
第三十六讲:Android手机摄像头编程入门 窈窈照相机
第三十七讲:Android传感器编程入门 窈窈录音器
第三十八讲:Android手写输入和手势编程入门 未写
第三十九讲:Android语音识别编程入门 未写
第四十讲:Android Wifi编程入门 未写
第四十一讲:Android蓝牙编程入门 待续
第四十二讲:用户界面 View(九) SlidingDrawer 仿造Home的应用程序列表
第四十三讲:用户界面 View(十) ExpandableListView,HorizontalScrollView,

ImageSwitcher,TextSwitcher 未写

第四十四讲:用户界面 View(十一) TabHost,TabWidget,Tabactivity
第四十五讲:项目实训—记事本xNotePad(一)
第四十六讲:项目实训—记事本xNotePad(二)
第四十七讲:项目实训—记事本xNotePad(三)
第四十八讲:项目实训—贪吃蛇Snake(一)
第四十九讲:项目实训—贪吃蛇Snake(二) 未写
第五十讲:项目实训—贪吃蛇Snake(三) 未写

[转载]双TOP二分法生成分页SQL类(支持MSSQL、ACCESS)

mikel阅读(1101)

[转载]双TOP二分法生成分页SQL类(支持MSSQL、ACCESS) – 祭天 – 博客园.

博客开张,先发以前的几个老物件儿,以前写下来的,现在发上来权当记录和分享。
这个类是用来生成分页SQL的,记得那时思考写一个只传一条查询语句就能生成分页SQL的方法,
然后发现了双TOP分页法,不过双TOP法在后半页就很慢,后来一个同学发过来的一篇文章:
2分法-通用存储过程分页(top max模式)版本(性能相对之前的not in版本极大提高)
通过它,发现了还二分法这东东,甚感神奇,不过它是用MAX的方式,逐改为双TOP法。

代码如下:

using System;
using System.Collections.Generic;
using System.Text;

///
/// 构造分页后的SQL语句
///
public static class PagingHelper
{
///
/// 获取分页SQL语句,排序字段需要构成唯一记录
///
///
记录总数     ///
每页记录数     ///
当前页数     ///
SQL查询语句     ///
排序字段,多个则用“,”隔开     /// 分页SQL语句
public static string CreatePagingSql(int _recordCount, int _pageSize, int _pageIndex, string _safeSql, string _orderField)
{
//重新组合排序字段,防止有错误
string[] arrStrOrders = _orderField.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
StringBuilder sbOriginalOrder = new StringBuilder(); //原排序字段
StringBuilder sbReverseOrder = new StringBuilder(); //与原排序字段相反,用于分页
for (int i = 0; i &lt; arrStrOrders.Length; i++)         {             arrStrOrders[i] = arrStrOrders[i].Trim();  //去除前后空格             if (i != 0)             {                 sbOriginalOrder.Append(", ");                 sbReverseOrder.Append(", ");             }             sbOriginalOrder.Append(arrStrOrders[i]);             int index = arrStrOrders[i].IndexOf(" "); //判断是否有升降标识             if (index &gt; 0)
{
//替换升降标识,分页所需
bool flag = arrStrOrders[i].IndexOf(" DESC", StringComparison.OrdinalIgnoreCase) != -1;
sbReverseOrder.AppendFormat("{0} {1}", arrStrOrders[i].Remove(index), flag ? "ASC" : "DESC");
}
else
{
sbReverseOrder.AppendFormat("{0} DESC", arrStrOrders[i]);
}
}

//计算总页数
_pageSize = _pageSize == 0 ? _recordCount : _pageSize;
int pageCount = (_recordCount + _pageSize - 1) / _pageSize;

//检查当前页数
if (_pageIndex &lt; 1)         {             _pageIndex = 1;         }         else if (_pageIndex &gt; pageCount)
{
_pageIndex = pageCount;
}

StringBuilder sbSql = new StringBuilder();
//第一页时,直接使用TOP n,而不进行分页查询
if (_pageIndex == 1)
{
sbSql.AppendFormat(" SELECT TOP {0} * ", _pageSize);
sbSql.AppendFormat(" FROM ({0}) AS T ", _safeSql);
sbSql.AppendFormat(" ORDER BY {0} ", sbOriginalOrder.ToString());
}
//最后一页时,减少一个TOP n
else if (_pageIndex == pageCount)
{
sbSql.Append(" SELECT * FROM ");
sbSql.Append(" ( ");
sbSql.AppendFormat(" SELECT TOP {0} * ", _recordCount - _pageSize * (_pageIndex - 1));
sbSql.AppendFormat(" FROM ({0}) AS T ", _safeSql);
sbSql.AppendFormat(" ORDER BY {0} ", sbReverseOrder.ToString());
sbSql.Append(" ) AS T ");
sbSql.AppendFormat(" ORDER BY {0} ", sbOriginalOrder.ToString());
}
//前半页数时的分页
else if (_pageIndex &lt; (pageCount / 2 + pageCount % 2))
{
sbSql.Append(" SELECT * FROM ");
sbSql.Append(" ( ");
sbSql.AppendFormat(" SELECT TOP {0} * FROM ", _pageSize);
sbSql.Append(" ( ");
sbSql.AppendFormat(" SELECT TOP {0} * ", _pageSize * _pageIndex);
sbSql.AppendFormat(" FROM ({0}) AS T ", _safeSql);
sbSql.AppendFormat(" ORDER BY {0} ", sbOriginalOrder.ToString());
sbSql.Append(" ) AS T ");
sbSql.AppendFormat(" ORDER BY {0} ", sbReverseOrder.ToString());
sbSql.Append(" ) AS T ");
sbSql.AppendFormat(" ORDER BY {0} ", sbOriginalOrder.ToString());
}
//后半页数时的分页
else
{
sbSql.AppendFormat(" SELECT TOP {0} * FROM ", _pageSize);
sbSql.Append(" ( ");
sbSql.AppendFormat(" SELECT TOP {0} * ", ((_recordCount % _pageSize) + _pageSize * (pageCount - _pageIndex)));
sbSql.AppendFormat(" FROM ({0}) AS T ", _safeSql);
sbSql.AppendFormat(" ORDER BY {0} ", sbReverseOrder.ToString());
sbSql.Append(" ) AS T ");
sbSql.AppendFormat(" ORDER BY {0} ", sbOriginalOrder.ToString());
}

return sbSql.ToString();
}

///
/// 获取记录总数SQL语句
///
///
限定记录数     ///
SQL查询语句     /// 记录总数SQL语句
public static string CreateTopnSql(int _n, string _safeSql)
{
return string.Format(" SELECT TOP {0} * FROM ({1}) AS T ", _n, _safeSql);
}

///
/// 获取记录总数SQL语句
///
///
SQL查询语句     /// 记录总数SQL语句
public static string CreateCountingSql(string _safeSql)
{
return string.Format(" SELECT COUNT(1) AS RecordCount FROM ({0}) AS T ", _safeSql);
}
}

双TOP法相比于NOT IN和MAX法,就是可以传入一条SQL语句来生成分页SQL语句,也可多字段排序;
但是有利也有弊,它要求排序字段必须构成唯一记录,且SELECT后的字段列表中,不允许出现与排序字段同名的字段。
虽然SQL2K5及以上版本已经提供了ROWNUM()来进行分页处理,但是使用SQL2K进行开发的还是较多的。