[转载]Lambda表达式的应用

mikel阅读(1041)

[转载]Lambda表达式的应用 – idior – 博客园.

Windbey中为了增强对集合的访问能力, MS设计了List<T>这么一个泛型集合, 其中有不少的增强功能,比如Foreach,ConvertAll,FindAll等等,并且为了方便使用MS在System名空间下引入了一些特制的 Delegate.主要包括以下几个:

20 public delegate void Action<T>(T obj);   //Used by ForEach

21 public delegate int Comparison<T>(T x, T y);  //Used by Sort

22 public delegate TOutput Converter<TInput, TOutput>(TInput input);    //Used by ConvertAll

23 public delegate bool Predicate<T>(T obj); //Used by FindAll

利用这些特制的Delegate,再加上匿名方法的使用,我们可以获得更加简洁,有效的代码. 具体的例子我以前有过介绍.现在在Orcas中, MS加入了lambda表达式的概念. lambda表达式是匿名方法的进一步增强. 利用它可以更加方便的写出新的方法. 而且语义上更加接近人性化.

同样它也引入了一些特制的Delegate:

20 public delegate T Func<T>();

21 public delegate T Func<A0, T>(A0 arg0);

22 public delegate T Func<A0, A1, T>(A0 arg0, A1 arg1);

23 public delegate T Func<A0, A1, A2, T>(A0 arg0, A1 arg1, A2 arg2);

24 public delegate T Func<A0, A1, A2, A3, T>(A0 arg0, A1 arg1, A2 arg2, A3 arg3);

和2.0中特制的Delegate对比, 你会发现它们有很多相同之处:

20 public delegate int Comparison<T>(T x, T y);
21 public delegate int Func<T,T,int>(T arg0, T arg1);

22 public delegate TOutput Converter<TInput, TOutput>(TInput input);
23 public delegate TOutput Func<TInput, TOutput>(TInput arg0);

24 public delegate bool Predicate<T>(T obj);

25 public delegate bool Func<T,bool>(T arg0);

也就是说3.0中特制的Delegate比2.0的更一般化, 2.0是3.0的特例. 所以我们完全可以将lambda表达式运用于List<T>的一些增强方法中.

Sort方法

20 List<int> list=new List<int>();

21 var numbers = new []{ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

22 list.AddRange(numbers);

23 list.Sort(delegate (int a, int b)

24 {

25 return a.CompareTo(b);

26 }

27 );

28 //use lambda

29 list.Sort((a,b)=>a.CompareTo(b));


ConvertAll方法

20 List<int> doubleList =list.ConvertAll<int>(delegate (int i)

21 {

22 return i*2;

23 });

24 //use lambda

25 var doubleList2=list.ConvertAll<int>(i=>i*2);


FindAll方法

20 List<int> lowerThanFiveList =list.FindAll(delegate (int i)

21 {

22 return i<5;

23 }

24 );

25 var lowerThanFiveList2=list.FindAll(i=>i<5);

从上面的例子可以看出利用lambda表达式写出的代码更加简洁易懂.  (以后代码都经过编译测试,可不是我杜撰的.)

以上是将lambda表达式运用于2.0当中. 但是在熟悉了3.0后, 你会发现2.0中的List<T>提供的增强方法完全是多余的了. 其实这些增强方法往往并不限于List<T>, 通常对于IEnumerable<T>对象都是适用的. 但是如果去改动IEnumable<T>接口那么影响实在太大了,将涉及很多的类. 所以MS仅仅在List<T>中提供了这些增强方法. 不过通过List<T>的一个构造函数,你可以使得所有的IEnumerable<T>对象可以方便的转化为List<T>,然后再利用这些方法.

这可以说是一个很取巧的方法, 不过在有了3.0的Extension Method的支持下, 就不用这么麻烦了, 而且MS还内置了一系列更强的集合操作方法.

比如之前的FindAll方法,我们现在可以这样写:

21 var numbers = new []{ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

22 var lowerThanFive=numbers.Where(i=>i<5); //never need List<T>, just operate on T[] or any other types implement IEnumerable<T>

23 foreach (var v in lowerThanFive)

24 Console.WriteLine(v);

ConvertAll方法

21 var doubleList3=numbers.Select(i=>i*2);

22 foreach (var v in doubleList3)

23 Console.WriteLine(v);

Sort方法

21 var orderList =numbers.OrderBy(i=>i);

22 foreach (var v in orderList)

23 Console.WriteLine(v);

甚至还有很多更强大的功能:
比如我要取numbers数组中最大的5个数.

21 var big5 =numbers.OrderByDescending(i=>i).Take(5);

22 foreach (var v in big5)

23 Console.WriteLine(v);

通过Orcas的Extension Method和Lambda表达式, MS为集合的操作提供了更加方便强大的功能. 这里尚未用到Standard Query Operators, 不然代码还要被简化.

当然Linq现在仅仅是一个Tech Preview 版本. 尚有很多不足.尤其在智能感知(IntelliSense)方面:
1.  var numbers = new []{ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 采用这种写法时, numbers没有智能感知功能.
2. 使用lambda表达式时,如果不声明T类型,对于其中的变量没有智能感知功能.这点很是奇怪, Linq竟然没有强制声明类型.

21 var lowerThanFive=numbers.Where<int>(i=>i<5);

22 var lowerThanFive=numbers.Where(i=>i<5);

以上两种写法竟然都没问题, 显然在下一种写法中变量i 无法获得智能感知的能力.
3. numbers.OrderBy(…),在写这个方法时,numbers的智能感知中并没有OrderBy这个方法, 但是编译运行没有问题.
4. lambda表达式目前不支持多条语句.

[转载]基于AppDomain的"插件式"开发

mikel阅读(1355)

[转载]基于AppDomain的”插件式”开发 – 空逸云 – 博客园.

很多时候,我们都想使用(开发)USB式(热插拔)的应用, 例如,开发一个WinForm应用,并且这个WinForm应用能允许开发人员定制扩展插件,又例如,我们可能维护着一个WinService管理系统, 这个WinService系统管理的形形色色各种各样的服务,这些服务也是各个”插件式”的类库,例如:

public interface IJob
{
void Run(DateTime time);
}

public class CollectUserInfo : IJob
{

public void Run(DateTime time)
{
//doing some thing...
}
}

我们提供了一个IJob接口,所有”服务”都继承该接口,然后做相关的配置,在服务启动时,就可以根据配置,反射加载程序集,执行我们预期的任务.

更新程序集(dll/exe)

服务/插件程序(后面只称为服务,虽然两者应用不同,但是在此处他们所运用的原理和作用是相同的 🙂 )很健稳的运行着.但在服务/插件程序运行一段时间之后,某些”插件”的业务需求发生的变化,或者版本升级等种种外部原因,导致我们对原本的”插件”程序 集进行了升级(可能从v1.0升级至v2.0).当我们想像ASP.NET应用一样.把新的dll替换旧dll的时候,错误发生了.

image

发生该错误的原因很简单,因为我们的程序中已经调用了该dll,那么在CLR加载该dll到文件流中也给其加了锁,所以,当我们要进行覆盖,修改,删除的时候自然就无法操作该文件了.那我们该怎么做?为什么ASP.NET可以直接覆盖?

AppDomain登场

我们知道,AppDomain是.Net平台里一个很重要的特性,在.Net以前,每个程序是”封装”在不同的进程中的,这样导致的结果就造就占用 资源大,可复用性低等缺点.而AppDomain在同一个进程内划分出多个”域”,一个进程可以运行多个应用,提高了资源的复用性,数据通信等.详见应用程序域

CLR在启动的时候会创建系统域(System Domain),共享域(Shared Domain)和默认域(Default Domain),系统域与共享域对于用户是不可见的,默认域也可以说是当前域,它承载了当前应用程序的各类信息(堆栈),所以,我们的一切操作都是在这个 默认域上进行.”插件式”开发很大程度上就是依靠AppDomain来进行.

“热插拔”实现说明

当加载了一个程序集之后,该程序集就会被加入到指定AppDomain中,按照原来的想法,要实现”热插拔”,只要在需要使用该”插件”的时候,加 载该”插件”的程序集(dll),使用结束后,卸载掉该程序集便可达到我们预期的效果.加载程序集很简单,.C#提供一个Assembly类,方便又快 捷.
var _assembly = Assembly.LoadFrom(assemblyFile);

Assembly提供了数个加载方法详见Assembly类.

然后,C#却没有提供卸载程序集的方法,唯一能卸载程序集的方法只有卸载该程序集所在的AppDomain,这样,该AppDomain下的程序集都会被释放.知道这一点,我们便可以利用AppDomain来达到我们预期的效果.

AppDomain实现”热插拔”

首先,我们需要先实例化一个新AppDomain作为”插件”的宿主.在实例化一个Domain之前,先声明该Domain的一些基本配置信息

AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = "ApplicationLoader";
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
setup.CachePath = setup.ApplicationBase;
setup.ShadowCopyFiles = "true"; //启用影像复制程序集
setup.ShadowCopyDirectories = setup.ApplicationBase;
AppDomain.CurrentDomain.SetShadowCopyFiles();

setup.ShadowCopyFiles = “true”;这句很重要,其作用就是启用影像复制程序集,什么是影像复制程序集,复制程序集是保证”热插拔”

实现的主要工作.AppDomain加载程序集的时候,如果没有ShadowCopyFiles,那就直接加载程序集,结果就是程序集被锁定,相 反,如果启用了ShadowCopyFiles,则CLR会将准备加载的程序集拷贝一份至CachePath,再加载CachePath的这一份程序集, 这样原程序集也就不会被锁定了. AppDomain.CurrentDomain.SetShadowCopyFiles();的作用就是当前AppDomain也启用 ShadowCopyFiles,在此,当前AppDomain也就是前面我们说过的那个默认域(Default Domain),为什么当前域也要启用ShadowCopyFiles呢?

主AppDomian在调用子AppDomain提供过来的类型,方法,属性的时候,也会将该程序集添加到自身程序集引用当中去,所以,”插件”程序集就被主AppDomain锁定,这也是为什么创建了单独的AppDomain程序集也不能删除,替换(释放)的根本原因

利用SOS,可以很清楚的看到这一点

0:018&gt; !dumpdomain
--------------------------------------
System Domain:      5b912478
LowFrequencyHeap:   5b912784
HighFrequencyHeap:  5b9127d0
StubHeap:           5b91281c
Stage:              OPEN
Name:               None
--------------------------------------
Shared Domain:      5b912140
LowFrequencyHeap:   5b912784
HighFrequencyHeap:  5b9127d0
StubHeap:           5b91281c
Stage:              OPEN
Name:               None
Assembly:           00109de0 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader:        00110f68
Module Name
58631000            C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

--------------------------------------
Domain 1:           000f4598
LowFrequencyHeap:   000f4914
HighFrequencyHeap:  000f4960
StubHeap:           000f49ac
Stage:              OPEN
SecurityDescriptor: 000f5568
Name:               AppDomainTest.exe
Assembly:           00109de0 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader:        00110f68
SecurityDescriptor: 001097b0
Module Name
58631000            C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly:           0011d448 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\AppDomainTest.exe]
ClassLoader:        00117fd0
SecurityDescriptor: 0011d3c0
Module Name
001c2e9c            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\AppDomainTest.exe

Assembly:           00131370 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll]
ClassLoader:        0011fa00
SecurityDescriptor: 001299a0
Module Name
579c1000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll

Assembly:           00131400 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll]
ClassLoader:        00131490
SecurityDescriptor: 0012e9c0
Module Name
62661000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll

Assembly:           00131d20 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll]
ClassLoader:        00133d08
SecurityDescriptor: 0012f078
Module Name
5aa81000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll

Assembly:           00131ed0 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll]
ClassLoader:        001415a8
SecurityDescriptor: 0012f430
Module Name
5a981000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll

Assembly:           00132080 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll]
ClassLoader:        00141620
SecurityDescriptor: 0012f5c8
Module Name
546e1000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll

Assembly:           00132ce0 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\CrossDomainController.dll]
ClassLoader:        001b3450
SecurityDescriptor: 06f94560
Module Name
001c7428            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\CrossDomainController.dll

Assembly:           00132350 [C:\Users\kong\AppData\Local\assembly\dl3\6ZYK3XE9.86Q\2AQ35O7C.VHE\1f704bbb\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL]
ClassLoader:        001b32e8
SecurityDescriptor: 070a8620
Module Name
001c7d78            C:\Users\kong\AppData\Local\assembly\dl3\6ZYK3XE9.86Q\2AQ35O7C.VHE\1f704bbb\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL

--------------------------------------
Domain 2:           06fd0238
LowFrequencyHeap:   06fd05b4
HighFrequencyHeap:  06fd0600
StubHeap:           06fd064c
Stage:              OPEN
SecurityDescriptor: 06724510
Name:               ApplicationLoaderDomain
Assembly:           00109de0 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader:        00110f68
SecurityDescriptor: 06f93bd0
Module Name
58631000            C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly:           00132e90 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\c91a2898\f6f7f865_9a4fcc01\CrossDomainController.DLL]
ClassLoader:        001b3540
SecurityDescriptor: 06f92be0
Module Name
00a833c4            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\c91a2898\f6f7f865_9a4fcc01\CrossDomainController.DLL

Assembly:           001330d0 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\32519346\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL]
ClassLoader:        001b39f0
SecurityDescriptor: 06f92f98
Module Name
00a83adc            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\32519346\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL

除了新建的AppDomain(Domain2)中的Module引用了ShowHelloPlug.dll,默认域(Domian1)也有ShowHelloPlug.dll的

程序集引用.

应用程序域之间的通信

每个AppDomain都有自己的堆栈,内存块,也就是说它们之间的数据并非共享了.若想共享数据,则涉及到应用程序域之间的通信.C#提供了MarshalByRefObject类进行跨域通信,那么,我们必须提供自己的跨域访问器.

public class RemoteLoader : MarshalByRefObject
{
private Assembly _assembly;

public void LoadAssembly(string assemblyFile)
{
try
{
_assembly = Assembly.LoadFrom(assemblyFile);
//return _assembly;
}
catch (Exception ex)
{
throw ex;
}
}

public T GetInstance(string typeName) where T : class
{
if (_assembly == null) return null;
var type = _assembly.GetType(typeName);
if (type == null) return null;
return Activator.CreateInstance(type) as T;
}

public void ExecuteMothod(string typeName, string methodName)
{
if (_assembly == null) return;
var type = _assembly.GetType(typeName);
var obj = Activator.CreateInstance(type);
Expression lambda = Expression.Lambda(Expression.Call(Expression.Constant(obj), type.GetMethod(methodName)), null);
lambda.Compile()();
}
}

为了更好的操作这个跨域访问器,接下来我构建了一个名为AssemblyDynamicLoader的类,它内部封装了RemoteLoader类

的操作.

public class AssemblyDynamicLoader
{
private AppDomain appDomain;
private RemoteLoader remoteLoader;
public AssemblyDynamicLoader()
{
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = "ApplicationLoader";
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
setup.CachePath = setup.ApplicationBase;
setup.ShadowCopyFiles = "true";
setup.ShadowCopyDirectories = setup.ApplicationBase;
AppDomain.CurrentDomain.SetShadowCopyFiles();
this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain", null, setup);
String name = Assembly.GetExecutingAssembly().GetName().FullName;
this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);
}

public void LoadAssembly(string assemblyFile)
{
remoteLoader.LoadAssembly(assemblyFile);
}

public T GetInstance(string typeName) where T : class
{
if (remoteLoader == null) return null;
return remoteLoader.GetInstance(typeName);
}

public void ExecuteMothod(string typeName, string methodName)
{
remoteLoader.ExecuteMothod(typeName, methodName);
}

public void Unload()
{
try
{
if (appDomain == null) return;
AppDomain.Unload(this.appDomain);
this.appDomain = null;
}
catch (CannotUnloadAppDomainException ex)
{
throw ex;
}
}
}

这样我们每次都是通过AssemblyDynamicLoader类进行跨域的访问.

AppDomain.CurrentDomain.SetShadowCopyFiles();
this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain", null, setup);
String name = Assembly.GetExecutingAssembly().GetName().FullName;
this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);

通过我们前面构造的一个AppDomainSetup,构建了一个我们所需的AppDomain,并且在这个appDomain中构建了

一个RemoteLoader类的实例(此时该实例已具备跨域访问能力,也就是说我们在主域能获取子域内部的数据信息).目前RemoteLoader只提供了少数的几个方法.

跨域操作

下面,我们就模拟一次”插件式”的跨域操作.首先我们构造了一个窗体,其有以下元素.

image

选择程序集路径之后,加载程序集,然后就触发程序集指定类型(通过配置获取)的特定操作.这里我们定义了一个公共接口,它是所有”插件”操作的主要入口了.
public interface IPlug
{
void Run();
}

随后定义了一个实现该接口的类.
[Serializable]
public class ShowHelloPlug : IPlug
{
public void Run()
{
MessageBox.Show(“Hello World…”);
}
这个”插件”的工作很简单.仅仅弹出一个对话框,说声”Hello World…”,接下来将其编译成一个dll.

image

回到界面,选择刚才编译的Dll,然后直接加载.

到这里,我们的工作完成了一半了.呼呼.OK.我们的需求发生了变化,不再是弹出Hello World了.而时候弹出Hi,I’m Kinsen,我们修改刚才的子类,并再编译一次.再将Dll替换刚才的Dll,这次,Dll没有没锁定(因为我们前面启用了 ShadowCopyFiles.).再加载一下程序集,你会发现结果并不是”Hi,I’m Kinsen”,而是”Hello World..”为什么会这样呢?这时候,借助SOS的力量(前面有SOS结果).

我们发现Domain1(Default Domain)和Domain2(新创建Domain)都引用了程序集ShowHelloPlug.DLL,但是两个引用的Dll地址却不相同,这是因为 启用了ShadowCopyFiles,它们加载的都是各自程序集的备份,我们根据Domain2的Assembly地址查看ShowHelloPlug 的编译代码.
0:011> !dumpmt 00fc40ac
00fc40ac is not a MethodTable
0:011> !dumpmd 00fc40ac
Method Name: Plug.ShowHelloPlug.Run()
Class: 046812b4
MethodTable: 00fc40bc
mdToken: 06000001
Module: 00fc3adc
IsJitted: no
CodeAddr: ffffffff
Transparency: Critical

从IsJitted为no可以看出,该程序集并没有被调用,那调用的是谁?我们再次查看Domain1(Default Domain

)中的ShowHelloPlug.
0:011> !dumpmd 001f8240
Method Name: Plug.ShowHelloPlug.Run()
Class: 004446e4
MethodTable: 001f8250
mdToken: 06000001
Module: 001f7d78
IsJitted: yes
CodeAddr: 00430de0
Transparency: Critical

已知每个AppDomain都有自己的堆栈信息,各自不互相影响,所以,当我们在主域中获取到了子域中的数据,并非新建一个指向该实例的引用,而是在自己的堆栈上开辟出一块空间”深度拷贝”该实例,那么必然就达不到我们我需的结果.

子域内部调用

那么为了达到我们预期的效果,我们必须在子域内部执行我们所需的操作(调用),所以在RemoteLoader类中增加了一个Execute方法

public void ExecuteMothod(string typeName, string methodName)
{
if (_assembly == null) return;
var type = _assembly.GetType(typeName);
var obj = Activator.CreateInstance(type);
Expression lambda = Expression.Lambda(Expression.Call(Expression.Constant(obj), type.GetMethod(methodName)), null);
lambda.Compile()();
}

此处我暂时只想到了利用反射调用,这样的代价就是调用所需消耗的资源更多,效率低下.目前还没有

想出较好的解决方案,有经验的童鞋欢迎交流.

这样外部的调用就变成以下

loader = new AssemblyDynamicLoader();
loader.LoadAssembly(txt_dllName.Text);
//var obj = loader.GetInstance("Plug.ShowHelloPlug");
//obj.Run();
loader.ExecuteMothod("Plug.ShowHelloPlug", "Run");

现在在将Dll替换,结果正常.

image

尾声

做”插件式”开发,除了利用AppDomain之外,也有童鞋给出了另一种解决方案,也就是在加载Dll的时候,先将Dll在内存中复制一份,这样原来的Dll也就不会被锁定了.详见插件的“动态替换”.

以上实例本人皆做过实验,但可能还存在一定不足或概念错误,若有不当之处,欢迎各位童鞋批评指点.

更多

通过应用程序域AppDomain加载和卸载程序集

什么是的AppDomain

[转载]Adnroid一个录制屏幕的开源项目介绍

mikel阅读(1054)

[转载]Adnroid一个录制屏幕的开源项目介绍 – Android – mobile – ITeye论坛.

最近发现一个录制屏幕的开源项目,跟大家分享下。
Androidscreencastcase 一个适用于Windows/linux/MacOS 平台控制任何Android 设备,通过USB连接就可实现鼠标键盘控制你的android.
官方描述:
Desktop app to control an android device remotely using mouse and keyboard. Should work on Windows/Linux/MacOS with any android device.

官方网址:http://code.google.com/p/androidscreencast/

安装及需求描述:

1.需要Android环境(download here)
2.确保已经通过USB连接到android设备
3.确保jdk环境在5以及以上
4.下载androidscreencast.jnl(Download here)
几点说明:
1.使用需要一定的权限如root,USB调试已经启用
2.鼠标右键可以旋转屏幕方向
3.反映数度有一定的延迟

对应的key值
KeyMapping
Home HOME
Menu (left softkey) F2 or Page-up button
Star (right softkey) Page Down
Back ESC
Call/dial button F3
Hangup/end call button F4
Search F5
Power button F7
Switch orientation Right click

5进制空间 最后在放上一张截图

[原创]数据库重构1:表合并

mikel阅读(940)

最近项目中充斥着以往没用的功能,严重影响性能,于是准备对项目进行整理重构,清理没用的功能,涉及到数据库的清理,学名就是数据库重构,首当其冲的就是表结构的重构,本系列包含以下内容:

  1. 表合并

  2. 视图关系重构

  3. 存储过程业务逻辑整理

好了,下面开始第一部分的内容:

目前项目中的数据库存在过度设计的问题,首当其冲的是用户信息的存储表过于零碎,多达10个表之多,每个表存储与用户直接关联的不同内容,而且都是一对一的关系,造成对用户操作处理过程繁琐,查询和更新数据严重影响性能,于是从用户开始重构。特别提醒:重构之前必备的准备工作就是备份数据库和现有程序代码,避免重构过程中出现数据丢失和程序混乱,到时候就追悔莫及了!

用户表之间的关系图:

这些都是UserInfo的属性,于是将这些表整合到UserInfo表中,如下图:

下面说下重构步骤:

  1. 先在UserInfo表中加入其他的表的字段,注意:其他表这时候不需要删除
  2. 然后按照User_ID=UserInfo.ID为条件依次更新UserInfo表中字段的值,也就是导入其他表的数据
  3. 最后一步就是不要删除其他表,为什么多这么一步,因为数据库中除UserInfo表之外还有其他表可能关联这些废弃的表,删除后会引起数据查询出错、视图无法显示、存储过程报错等一系列的问题
  4. 接下来就是视图重构需要讲解的内容了,如何清理视图并提出废弃的表

[转载]android下载图片在手机中展示

mikel阅读(1061)

[转载]android下载图片在手机中展示 – 蓝之风 – 博客园.

在项目开发中从互联网上下载图片是经常用到的功能,再次总结一下

1.普通的下载方式

布局文件:

<!--?xml version="1.0" encoding="utf-8"?-->

java文件

public class DownloadImage extends Activity {
private ImageView imgPic;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.download_image);
imgPic = (ImageView) findViewById(R.id.imgPic);
String url = "http://ww1.sinaimg.cn/bmiddle/6834c769jw1djjf4p3p9rj.jpg";
loadRmoteImage(url);
}

/**
* @param imgUrl
*            远程图片文件的URL
*
*            下载远程图片
*/
private void loadRmoteImage(String imgUrl) {
URL fileURL = null;
Bitmap bitmap = null;
try {
fileURL = new URL(imgUrl);
} catch (MalformedURLException err) {
err.printStackTrace();
}
try {
HttpURLConnection conn = (HttpURLConnection) fileURL
.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
int length = (int) conn.getContentLength();
if (length != -1) {
byte[] imgData = new byte[length];
byte[] buffer = new byte[512];
int readLen = 0;
int destPos = 0;
while ((readLen = is.read(buffer)) &gt; 0) {
System.arraycopy(buffer, 0, imgData, destPos, readLen);
destPos += readLen;
}
bitmap = BitmapFactory.decodeByteArray(imgData, 0,
imgData.length);
}
} catch (IOException e) {
e.printStackTrace();
}
imgPic.setImageBitmap(bitmap);
}

2.带进度条的下载

有时候网络差,或者是图片太大,会出现黑屏的情况,用户体验比较差,那么增加一个进度条是提高用户体验的好方法

/**
* @author xushilin xsl xushilin@kingtoneinfo.com
* @version: 创建时间:2011-7-27 下午02:55:56
* 说 明: android中下载图片
* 修改历史:
*/
public class DownloadImage extends Activity {
private ImageView imgPic;
private ProgressBar progressBar;
private int totalSize=0;
private int size=0;
private Handler mHandler;
String url = "http://ww1.sinaimg.cn/bmiddle/6834c769jw1djjf4p3p9rj.jpg";
private Bitmap bitmap=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.download_image);
imgPic = (ImageView) findViewById(R.id.imgPic);

progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setProgress(getProgressInt(progressBar.getMax()));
mHandler = new Handler() {
public void handleMessage(Message msg) {
progressBar.setProgress(getProgressInt(progressBar.getMax()));
if(bitmap!=null){
imgPic.setImageBitmap(bitmap);
}
}
};
new Thread(){
public void run(){
loadRmoteImage(url);
}
}.start();
}

/**
* @param imgUrl
*            远程图片文件的URL
*
*            下载远程图片
*/
private void loadRmoteImage(String imgUrl) {
URL fileURL = null;
try {
fileURL = new URL(imgUrl);
} catch (MalformedURLException err) {
err.printStackTrace();
}
try {
HttpURLConnection conn = (HttpURLConnection) fileURL
.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
int length = (int) conn.getContentLength();
totalSize=length;
if (length != -1) {
byte[] imgData = new byte[length];
byte[] buffer = new byte[512];
int readLen = 0;
int destPos = 0;
while ((readLen = is.read(buffer)) &gt; 0) {
System.arraycopy(buffer, 0, imgData, destPos, readLen);
destPos += readLen;
size=destPos;
mHandler.sendEmptyMessage(1);
Thread.sleep(100);
}
bitmap = BitmapFactory.decodeByteArray(imgData, 0,
imgData.length);
mHandler.sendEmptyMessage(1);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

}
private  int getProgressInt(int max) {
int result = (totalSize &gt; 0) ? (int) (size * max * 1.0 / totalSize) : 0;
return result;
}
}

效果如下:

下载过程

image

下载完成:

image

[转载]关于Flex4提示序列号失效的问题解决

mikel阅读(1150)

[转载]关于Flex4提示序列号失效的问题解决 .

当下安装的Flash Builder 4在正确输入序列号安装之后,每次打开都会提示序列号失效,而必须再次输入一个不同的序列号或者就改成试用版接受60天试用!其解决方法如下:

可以用记事本编辑“C:\Windows\System32\Drivers\etc\”目录下的 hosts 文件,在末尾加上:
127.0.0.1 activate.adobe.com
127.0.0.1 practivate.adobe.com
127.0.0.1 ereg.adobe.com
127.0.0.1 activate.wip3.adobe.com
127.0.0.1 wip3.adobe.com
127.0.0.1 3dns-3.adobe.com
127.0.0.1 3dns-2.adobe.com
127.0.0.1 adobe-dns.adobe.com
127.0.0.1 adobe-dns-2.adobe.com
127.0.0.1 adobe-dns-3.adobe.com
127.0.0.1 ereg.wip3.adobe.com
127.0.0.1 activate-sea.adobe.com
127.0.0.1 wwis-dubc1-vip60.adobe.com
127.0.0.1 activate-sjc0.adobe.com

Flash CS5也同样适用!

欢迎交流学习!不喜勿喷!编辑不易,转载请注明出处http://hi.baidu.com/sxw52039/home

[转载]Android开发教程 --- 葵花宝典第六层 控件之 Dialog ListView GridView

mikel阅读(1026)

[转载]Android开发教程 — 葵花宝典第六层 控件之 Dialog ListView GridView – Jason_CC – 博客园.

Hi 大家好!

今天和大家一起来学习三种控件,对话框、列表、网格视图。

这三种控件比较重要,使用率也比较频繁,相对来说也比前面所讲的控件复杂,希望大家多练习,熟练掌握它们。

照例,上笑话。

论坛楼主:帅有个屁用——到头来还不是被卒吃掉!
论坛回复:帅有士陪,有炮打,有马骑,有车坐,有相暗恋……帅怎么不好?!!

Dialog 对话框,它运行起来的效果是什么样呢?如下图

这种是最常用的对话框

当点击了上图的确定后,会弹此对话框,这种对话框属于自定义布局类型

当执行一些比较费时的操作时,用这种对话框是个不错的选择

当我们需要用户进行选择操作,又不能使用下来列表时,可以使用这种自定义布局的对话框

接下来我们就一起来看看这些通过代码是如何实现的?

package TSD.Jason.Example;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;

/**
* 对话框
* @author Administrator
* 常用方法:
*     setTitle():给对话框设置title.
setIcon():给对话框设置图标。
setMessage():设置对话框的提示信息
setItems():设置对话框要显示的一个list,一般用于要显示几个命令时
setSingleChoiceItems():设置对话框显示一个单选的List
setMultiChoiceItems():用来设置对话框显示一系列的复选框。
setPositiveButton():给对话框添加”Yes”按钮。
setNegativeButton():给对话框添加”No”按钮。
*
*/
public class DialogActivity extends Activity {
Button btn1;
ProgressDialog p;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.diaolg);
btn1 = (Button)findViewById(R.id.btnDialog);
btn1.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
Builder dialog = new AlertDialog.Builder(DialogActivity.this);
dialog.setTitle("登录提示");
dialog.setIcon(android.R.drawable.ic_dialog_info);
dialog.setMessage("是否登录?");
dialog.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ShowLoginDialog();
}
});
dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DialogActivity.this.finish();
}
});
dialog.show();
}
});
}
}

以上代码是我们第一张对话框的效果实现代码,大家发现是不是挺简单,当我们单击确定按钮后,将调用一个叫做ShowLoginDialog的方法。

这个方法马上会贴出来,在这里我还是要强调下,大家在写代码的时候一定要有一个良好的编程思想,将功能拆分,降低代码的耦合度,一个方法只做一件事情,不要一股脑的将代码写到一个方法里。希望大家记得。

private void ShowLoginDialog()
{
Builder builder = new AlertDialog.Builder(DialogActivity.this);
builder.setTitle("用户登录");
LayoutInflater factory = LayoutInflater.from(DialogActivity.this);
View dialogView  = factory.inflate(R.layout.dialogview, null);
builder.setView(dialogView);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DialogWait();
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DialogActivity.this.finish();
}
});
builder.show();
}

上边被标注的代码是为了让Dialog中的内容部分显示我们自定义的布局文件,通过builder对象的setView方法就可以将R.layout.dialogview这个布局文件绑定到对话框中。

布局文件代码如下

<!--?xml version="1.0" encoding="utf-8"?-->

第三个等待效果的对话框是如何实现的呢?上边我们调用了一个方法叫做 DialogWait

private void DialogWait()
{
p = ProgressDialog.show(DialogActivity.this, "正在登录", "请稍后...", true);
new Thread(){
public void run(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
p.dismiss();//登录结束后,取消对话框
}
}
}.start();

}

大家注意,此时的类就不在是AlertDialog,而是ProgressDialog。

上边代码我们为了测试看效果,所以开启了一个线程,并挂起2秒,这在以后项目中是不需要的,如果大家看不太懂,那么这里先跳过。

接下来我们来看看如何在对话框中嵌套一个ListView。

首先,需要一个布局文件,布局文件里只创建一个ListView,如下代码

<!--?xml version="1.0" encoding="utf-8"?-->

Java代码如下

private void ShowLoginDialog()
{
Builder builder = new AlertDialog.Builder(Tab1Activity.this);
builder.setTitle("选择城市");
LayoutInflater factory = LayoutInflater.from(Tab1Activity.this);
View dialogView  = factory.inflate(R.layout.dialogcity, null);
listCity =(ListView)dialogView.findViewById(R.id.listCity);
GetCity();
builder.setView(dialogView);
builder.show();
}

private void GetCity()
{
System.out.println("asd");
ArrayList&gt; listData = new ArrayList&gt;();
HashMap hmItem = new HashMap();
hmItem.put("city", "北京");
listData.add(hmItem);
hmItem = new HashMap();
hmItem.put("city", "上海");
listData.add(hmItem);
hmItem = new HashMap();
hmItem.put("city", "深圳");
listData.add(hmItem);
hmItem = new HashMap();
hmItem.put("city", "天津");
listData.add(hmItem);
hmItem = new HashMap();
hmItem.put("city", "南京");
listData.add(hmItem);
hmItem = new HashMap();
hmItem.put("city", "武汉");
listData.add(hmItem);
hmItem = new HashMap();
hmItem.put("city", "江苏");
listData.add(hmItem);
hmItem = new HashMap();
hmItem.put("city", "宁波");
listData.add(hmItem);
SimpleAdapter sim = new SimpleAdapter(this, listData, android.R.layout.simple_list_item_1, new String[]{"city"}, new int[]{android.R.id.text1});
listCity.setAdapter(sim);
}

直接调用ShowLoginDialog方法即可。注意标注的代码,需要先获取ListView。这里已经用到了ListView,如果不太懂下边就将ListView,大家注意看。

ListView

上边已经展示过它运行的效果了,这里就不展示运行效果了。

那么要使用ListView需要哪些步骤呢?举一个例子,可能不太恰当

冰箱里没有鸡蛋了,我们从家里提了一个篮子去超市买鸡蛋。就是这样的一个过程。我们来分解下这个步骤

冰箱 == 展示数据 == ListView

超市里的鸡蛋 == 数据 == ArrayList 泛型集合

篮子 == 适配器 == SimpleAdapter

我们应该将 鸡蛋(ArrayList) 装到 篮子里(SimpleAdapter) 然后提回家 放到 冰箱里( ListView)

分解完步骤后,那么我们看看如何用代码实现这个过程。

ListView userList; //声明一个ListView对象(冰箱)

userList = (ListView)findViewById(R.id.listUserInfo); //获取布局文件中的ListView控件赋值给ListView对象

ArrayList> listData = new ArrayList>(); //数据源 (超市装鸡蛋的盒子)

HashMap hmItem = new HashMap(); //需要一个HashMap键值对 (每一个鸡蛋)
hmItem.put(“userName”, “张三”);
hmItem.put(“userPhone”, “1234567890”);
listData.add(hmItem); //将鸡蛋装到数据源中

String[] s = new String[2]; //列 和键值对中的键 一一对应 每个键值对应该是一样的列数
s[0] = “userName”;
s[1] = “userPhone”;
int[] i = new int[2]; //用什么控件来装载上边String集合中的列 和上边的String数组也是一一对应的
i[0] = Android.R.id.text1;
i[1] = Android.R.id.text2;
SimpleAdapter sim = new SimpleAdapter(this, listData, android.R.layout.simple_list_item_1, s, i); //这就是我们的篮子
userList.setAdapter(sim); //将篮子中的鸡蛋装到冰箱中 🙂

完整的代码如下

package TSD.Jason.Example;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;

/**
* ListView基本使用方法
*
* 使用ListView的基本步骤
* 1.准备ListView要显示的数据 ;
* ArrayList&gt; listData = new ArrayList&gt;();
*
* 2.使用 一维或多维 动态数组 保存数据;
* HashMap hmItem = new HashMap();
hmItem.put("userName", "张三");
hmItem.put("userPhone", "1234567890");

* 3.构建适配器 , 简单地来说, 适配器就是 Item数组 , 动态数组 有多少元素就生成多少个Item;
* SimpleAdapter simpleAdapter;
* 数据绑定的类
* 参数解释
*
* 第一个context,很明显大家根据英文可以知道是上下文的意思,它官方的意思是:SimpleAdapter所要运行关联到的视图,这个是什么呢?就是你这个SimpleAdapter所在的Activity(一般而言),所以这个参数一般是this

第二个是一个泛型只要是一个List就行,这一般会想到是ArrayList,而他内部存储的则是Map或者继承自Map的对象,比如HashMap,这些语法都是Java的基本语法,不再详述了!这里呢是作为数据源,而且每一个ArraList中的一行就代表着呈现出来的一行,Map的键就是这一行的列名,值也是有列名的。

第三个资源文件,就是说要加载这个两列所需要的视图资源文件,你可以左边一个TextView右边一个TextView,目的在于呈现左右两列的值!

第四个参数是一个数组,主要是将Map对象中的名称映射到列名,一一对应

第五个是将第四个参数的值一一对象的显示(一一对应)在接下来的int形的id数组中,这个id数组就是LayOut的xml文件中命名id形成的唯一的int型标识符

*  context   关联SimpleAdapter运行着的视图的上下文。
data        一个Map的列表。在列表中的每个条目对应列表中的一行,应该包含所有在from中指定的条目
resource              一个定义列表项目的视图布局的资源唯一标识。布局文件将至少应包含哪些在to中定义了的名称。
from       一个将被添加到Map上关联每一个项目的列名称的列表
to    应该在参数from显示列的视图。这些应该全是TextView。在列表中最初的N视图是从参数from中最初的N列获取的值。
*
* 4.把 适配器 添加到ListView,并显示出来。
* @author Administrator
*
*/
public class ListViewActivity extends Activity {

ListView userList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview);
userList = (ListView)findViewById(R.id.listUserInfo);

ArrayList&gt; listData = new ArrayList&gt;();

HashMap hmItem = new HashMap();
hmItem.put("userName", "张三");
hmItem.put("userPhone", "1234567890");
listData.add(hmItem);

hmItem = new HashMap();
hmItem.put("userName", "李四");
hmItem.put("userPhone", "981234502");
listData.add(hmItem);

hmItem = new HashMap();
hmItem.put("userName", "王五");
hmItem.put("userPhone", "5622435566221");
listData.add(hmItem);

//SimpleAdapter simpleAdapter = new SimpleAdapter(this, listData, R.layout.textviewitem, new String[]{"userName","userPhone"}, new int[]{R.id.txtUserName,R.id.txtUserPhone});
//SimpleAdapter simpleAdapter = new SimpleAdapter(this, listData, android.R.layout.simple_list_item_1, new String[]{"userName","userPhone"}, new int[]{android.R.id.text1,android.R.id.text2});
//SimpleAdapter simpleAdapter = new SimpleAdapter(this, listData, android.R.layout.simple_list_item_2, new String[]{"userName","userPhone"}, new int[]{android.R.id.text1,android.R.id.text2});

String[] s = new String[2];
s[0] = "userName";
s[1] = "userPhone";
int[] i = new int[2];
i[0] = android.R.id.text1;
i[1] = android.R.id.text2;
SimpleAdapter sim = new SimpleAdapter(this, listData, android.R.layout.simple_list_item_1, s, i);

userList.setAdapter(sim);

//列表项单击事件
userList.setOnItemClickListener(new ListView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<!--?--> arg0, View arg1, int arg2,
long arg3) {
System.out.println(arg2);
System.out.println(arg3);
}
});

//列表项选中事件
userList.setOnItemSelectedListener(new ListView.OnItemSelectedListener() {

@Override
public void onItemSelected(AdapterView<!--?--> arg0, View arg1,
int arg2, long arg3) {
// TODO Auto-generated method stub
System.out.println("selected----------" +arg2);
System.out.println("selected----------" +arg3);
}

@Override
public void onNothingSelected(AdapterView<!--?--> arg0) {
// TODO Auto-generated method stub

}
});

//列表项长按事件
userList.setOnItemLongClickListener(new ListView.OnItemLongClickListener() {

@Override
public boolean onItemLongClick(AdapterView<!--?--> arg0, View arg1,
int arg2, long arg3) {
System.out.println("long---------" + arg2);
System.out.println("long---------" + arg3);
return true;
}
});
}
}

上边注释的三句话

第一句 是我们可以自定义布局文件展示数据

第二句 我们可以用内置的布局文件来展示

第三句 和第二句一样,但是效果不一样,大家运行看看就明白了

GridView

类似与手机主菜单中展示的效果,如图

网格视图控件和我们的ListView 操作很像,上边已经解释过了,这里直接贴代码了

package TSD.Jason.Example;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.os.Bundle;
import android.widget.GridView;
import android.widget.SimpleAdapter;

public class GridViewActivity extends Activity {

// 定义整型数组 即图片源
private Integer[]    mImageIds    =
{
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
R.drawable.img5,
R.drawable.img6,
R.drawable.img7,
R.drawable.img8,
R.drawable.img1,
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.gridview);
GridView gridview = (GridView) findViewById(R.id.gridview);
// 生成动态数组,并且转入数据
ArrayList&gt; lstImageItem = new ArrayList&gt;();

for (int i = 0; i &lt; 9; i++) {
HashMap map = new HashMap();
map.put("ItemImage", mImageIds[i]);// 添加图像资源的ID
map.put("ItemText", "NO." + String.valueOf(i));// 按序号做ItemText
lstImageItem.add(map);
}
SimpleAdapter simple = new SimpleAdapter(this, lstImageItem,
R.layout.gridviewitem,
new String[] { "ItemImage", "ItemText" }, new int[] {
R.id.ItemImage, R.id.ItemText });
gridview.setAdapter(simple);
}
}

好,今天就到这里,源代码已经上传到天圣达网站,大家去下载下来,动手实践下。 http://www.bj-stl.com/android.html

[转载]Android开发教程 --- 葵花宝典第五层 控件之 Menu DatePicker TimePicker AutoCompleteTextView MultiAutoCompleteTextView

mikel阅读(848)

转载Android开发教程 — 葵花宝典第五层 控件之 Menu DatePicker TimePicker AutoCompleteTextView MultiAutoCompleteTextView – Jason_CC – 博客园.

从今天起,课程中所讲的控件会比前几节复杂,功能也越强大,希望大家能将这些控件消化理解。

照例,上笑话。。

楼主:给我一个女人,我就能创造出一个民族!
回复:嗯,给你一头母猪,明年的肉价就能下跌! 🙂

开始今天的课程

1 Menu

菜单控件,很实用,也很常用的一种控件,那么运行的效果什么样呢?如下图

创建Menu有三种方式

第一种 通过配置文件实现菜单效果

第二种 通过编码方式实现菜单效果

第三种 在特定条件下动态创建菜单效果

那么这里我们先讲解前两种,至于第三种,后面我们做项目的时候会相信描述。

通过配置文件创建Menu

既然是通过配置文件创建,自然需要XML,首先需要在 res文件夹下创建一个menu文件夹,如下图

在创建XML时,大家要注意一点,最好不要直接对着 menu文件夹右键 新建 文件 的方式,可能是由于我的环境问题,我通过这种方式老是导致Eclipse 宕掉,无响应。。大家可以试试看你们的环境是否可以,如果不可以,请用以下两种方式来创建

第一 直接复制一个menu文件到menu文件夹下,然后打开进行修改

第二 通过 右击menu文件夹 -> 新建 -> 其它 -> Android -> Android XML File 下一步

在弹出的窗体中,创建文件名称,并选中Menu单选按钮 进行创建。

新建完成后,双击打开menu.xml文件,进行配置,代码如下:

<!--?xml version="1.0" encoding="utf-8"?-->
<!--      创建Menu的XML时,需要注意     1 不要直接去创建文件,然后创建一个.xml的文件,会导致Eclipse无响应     如何创建?     1 直接从外部访问 复制文件到menu文件夹     2 menu new Other——>Android——>XML,选择menu类型,填写名称
-->

创建完配置文件后,如何与代码关联呢?

package TSD.Jason.Example;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;

/**
* 通过配置文件实现
* @author Administrator
*
*/
public class MenuActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

/**
* 创建菜单
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {

//将XML文件转化成Menu对象
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.appmenu, menu);
return true;
}

/**
* 选中某项菜单事件
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item1:

break;
case R.id.item2:

break;
default:
break;
}
ShowToast("选则了" + item.getTitle());
return true;
}

private void ShowToast(String str)
{
Toast.makeText(this,str,Toast.LENGTH_SHORT).show();
}
}

大家可以注意到是通过 onCreateOptionsMenu方法进行Menu的创建操作

并通过MenuInflater类来将menu.xml转换成menu对象,进行操作。

onOptionsItemSelected方法是用来捕获菜单中某项被单击时,所要执行的功能。

当大家编写完成后,将程序启动起来,然后单击模拟器右边的功能键区域中的 MENU 按钮 就能看到菜单效果

通过代码实现

这种方式就不需要创建XML文件了,直接在编辑器中编写代码即可,代码如下

package TSD.Jason.Example;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

/**
* 通过代码生成菜单
* @author Administrator
*
*/
public class MenuActivity1 extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, 1, 1, R.string.menu1).setIcon(R.drawable.add);
menu.add(0, 2, 1, R.string.menu2);
menu.add(0, 3, 1, R.string.menu3);
menu.add(0, 4, 1, R.string.menu4);

return super.onCreateOptionsMenu(menu);
}

/**
* 选中某项菜单事件
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 1:

break;
case 2:

break;
default:
break;
}
ShowToast("选则了" + item.getTitle());
return true;
}

private void ShowToast(String str)
{
Toast.makeText(this,str,Toast.LENGTH_SHORT).show();
}
}

大家可以发现基本上和用XML方式没有太大区别,唯一的区别就在onCreateOptionsMenu方法中

menu.add(0, 1, 1, R.string.menu1).setIcon(R.drawable.add);

直接使用这个方法的参数menu对象的add方法创建

第一个参数 组ID 一般我们都设置成一样的

第二个参数 项ID 这个项ID不要重复,否则在onOptionsItemSelected方法中你将无法正确捕捉用户点击了哪一项

第三个参数 排序 这个就按顺序设置就OK

第四个参数 要显示的提示信息

DatePicker

这是一个日期控件,在项目中可能会有一些需求是需要通过用户去选择日期的,那么通过这个控件我们可以很好的和用户进行交互,并获取到用户选择的日期,进行下一步操作,运行效果如下图

TimePicker

时间控件 和上边的DatePicker所要表达的意思是一样的,用户可以选择时间,运行效果如下图

代码如下:

package TSD.Jason.Example;

import java.util.Calendar;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;

/**
* DatePicker

重要方法

getDayOfMonth():获取当前Day

getMonth():获取当前月

getYear()获取当前年

updateDate(int year, int monthOfYear, int dayOfMonth):更新日期

*
*
*
* TimePicker 重要方法
*
setCurrentMinute(Integer currentMinute)设置当前时间的分钟

getCurrentMinute()获取当前时间的分钟

setEnabled(boolean enabled)设置当前视图是否可以编辑。

m_TimePicker.setIs24HourView(true);设置为24小时制显示

setOnTimeChangedListener(TimePicker.OnTimeChangedListener onTimeChangedListener)当时间改变时调用

* @author Administrator
*
*/
public class DataActivity extends Activity {

TextView    m_TextView;
//声明DatePicker对象
DatePicker    m_DatePicker;
//声明TimePicker对象
TimePicker    m_TimePicker;

Button         m_dpButton;
Button         m_tpButton;
//java中的日历类
Calendar     c;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.date);

c=Calendar.getInstance(); //获得当前日期
m_TextView= (TextView) findViewById(R.id.TextView01);
m_dpButton = (Button)findViewById(R.id.button1);
m_tpButton = (Button)findViewById(R.id.button2);
//获取DatePicker对象
m_DatePicker = (DatePicker) findViewById(R.id.DatePicker01);
//将日历初始化为当前系统时间,并设置其事件监听
m_DatePicker.init(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH), new DatePicker.OnDateChangedListener() {
@Override
public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth)
{
DisplayShow("年:" + year + " 月:" + monthOfYear + " 日:" + dayOfMonth);
}
});

//获取TimePicker对象
m_TimePicker = (TimePicker) findViewById(R.id.TimePicker01);
//设置为24小时制显示
m_TimePicker.setIs24HourView(true);

//监听时间改变
m_TimePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute)
{
DisplayShow("时:" + hourOfDay +" 分:"+ minute);

}
});

m_dpButton.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v)
{
//创建、显示日期对话框
new DatePickerDialog(DataActivity.this,
new DatePickerDialog.OnDateSetListener()
{
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth)
{
//当日期更改时,在这里处理
//m_DatePicker.updateDate(year, monthOfYear, dayOfMonth);
DisplayShow("年:" + year + " 月:" + monthOfYear + " 日:" + dayOfMonth);
}
},c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show();
}
});

m_tpButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v)
{
//创建、显示时间对话框
new TimePickerDialog(DataActivity.this,
new TimePickerDialog.OnTimeSetListener()
{
public void onTimeSet(TimePicker view, int hourOfDay,int minute)
{
//时间改变时处理
//m_TimePicker.setCurrentHour(hourOfDay);
//m_TimePicker.setCurrentMinute(minute);
DisplayShow("时:" + hourOfDay +" 分:"+ minute);
}
},c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true).show();
}
});
}

private void DisplayShow(String str)
{
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
}

AutoCompleteTextView

智能提示 类似与百度的输入框,输入关键字后,跳出和此关键字匹配的数据 此控件只能匹配一次操作,运行如下图

当数据源中有匹配项时,就会弹出和输入的字符所匹配的数据,用户可以进行选择,并且可以继续输入

例如 当输入 ab 时,会提示上图所显示的这些项,当选择了abcde这项时,AutoCompleteTextView 输入框中则会显示abcde,当再次输入f时,会继续匹配abcdef这些字符。

MultiAutoCompleteTextView

和上边的AutoCompleteTextView 类似,区别在于它可以多次输入,选择,每次选择后,会以逗号分隔。效果如下

当选择了abcde时

大家通过上边的效果图就能明白,此控件是可以进行多次输入 选择的 ,只不过每次选择后,都会生出一个逗号,并且可以再次输入 选择

布局代码如下:

<!--?xml version="1.0" encoding="utf-8"?-->
<!-- android:completionThreshold 设置最少输入几个字符进行提示 -->



Java代码:

package TSD.Jason.Example;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.MultiAutoCompleteTextView;

/**
* 提供了2种智能提示控件 AutoCompleteTextView 和 MultiAutoCompleteTextView 区别
* AutoCompleteTextView 只能在文本框中查询一次 MultiAutoCompleteTextView 可以匹配多次,每次之前都用逗号分隔
*
* 默认最少2个字符进行提示 方法 setThreshold(2) 设置最少输入几个字符进行提示
*
* @author Administrator
*
*/
public class AutoCompleteAndMultiAuto extends Activity {
private static final String[] autoString = new String[] { "a2", "abf",
"abe", "abcde", "abc2", "abcd3", "abcde2", "abc2", "abcd2",
"abcde2" };

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.autocomplete);
// 关联关键字
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line, autoString);

AutoCompleteTextView m_AutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.AutoCompleteTextView01);

// 将adapter添加到AutoCompleteTextView中
m_AutoCompleteTextView.setAdapter(adapter);
// /////////////////
MultiAutoCompleteTextView mm_AutoCompleteTextView = (MultiAutoCompleteTextView) findViewById(R.id.MultiAutoCompleteTextView01);
// 将adapter添加到AutoCompleteTextView中
mm_AutoCompleteTextView.setAdapter(adapter);
mm_AutoCompleteTextView
.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());

}
}

今天的课程就到这里,源码已经上传到天圣达网站,大家可以去下载进行学习 http://www.bj-stl.com/android.html

[转载]集成Lua到你的Android游戏

mikel阅读(1213)

[转载]集成Lua到你的Android游戏 – Astin – 博客园.

当前众多游戏引入脚本语言作为快速发布内容以及灵活扩展的解决方案,在寻找向Android应用集成Lua脚本语言的方案时,大量零碎的资料并 没有向开发者指引一个明确的方向。在这里我把自己通过整合资料完成的方案写下来,希望能给需要的程序员帮助,欢迎大家提供更快捷的方案。

不再赘述Android开发环境的配置以及Lua脚本语言的介绍,需要了解的朋友可以Google资料。

我的开发环境是Window,列举我们将要用到的工具和组件:

简要介绍解决方案涉及到的几个基本概念,高手自动略过:

Lua用C实现,所以如果我们不打算用Java重写解释器的话,我们需要一种方式使C和Java能良好的沟通,熟悉Java的程序员肯定想到了JNI(Java Native Interface)。 你可能在Linux环境和Window环境使用过JNI,过程并不复杂:写好Java文件并在其中声明好native方法→用javah生成C头文件→为 C文件中声明的函数写好实现→编译C代码生成动态链接库,这样就可以在Java中使用native方法了。在Android下我们同样要经过这些步骤,只 不过编译生成动态链接库的时候我们需要Google提供的NDK工具。在Window环境下使用NDK还需要安装UNIX模拟环境Cygwin。我们还将 用到一个对Lua进行良好封装的开源项目luajava。

下面开始详细步骤:

  1. 安装Cygwin,下载到的setup是一个安装器,负责在线或者本地找到安装需要的文件并执行安装,建议把文件下载到本地以备下次使用,安装过 程执行到选择安装包时一定确定All结点(树形结点的根结点)在Install状态以免需要的包不被安装,如果是在线安装时间会很长,耐心等待过后 Cygwin安装成功。
  2. 安装NDK,NDK是一个压缩文件,下载完成解压缩(建议解压缩到一个方便输入路径的目录)。
  3. 下载上面lua和luajava的源代码并解压缩到任意目录。
  4. 在任意目录创建luajava4android(名字可以任意)目录,在目录下创建子目录jni(名字指定为jni),把lua和luajava中的C文件(包括头文件和源文件)都拷入该目录,并在该目录下创建Android.mk文件,拷贝下面内容至文件:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_ARM_MODE  := arm
LOCAL_MODULE    := luajava-1.1
LOCAL_SRC_FILES := lapi.c \
lauxlib.c \
lbaselib.c \
lcode.c \
ldblib.c \
ldebug.c \
ldo.c \
ldump.c \
lfunc.c \
lgc.c \
linit.c \
liolib.c \
llex.c \
lmathlib.c \
lmem.c \
loadlib.c \
lobject.c \
lopcodes.c \
loslib.c \
lparser.c \
lstate.c \
lstring.c \
lstrlib.c \
ltable.c \
ltablib.c \
ltm.c \
lundump.c \
lvm.c \
lzio.c \
print.c \
luajava.c

include $(BUILD_SHARED_LIBRARY)
  1. 运行Cygwin安装目录下的Cygwin.bat进入命令行界面,进入刚才建立的luajava4android路径,执行ndk-build,成功以后就会在目录下生成一个lib目录,里面以.so为后缀名的文件即为我们需要的动态链接库文件。
  2. 把luajava中的java源代码导入到自己的Android工程中,在工程目录下建立\libs\armeabi\结构的路径,并把动态链接库文件拷入,刷新工程,这样我们就完成了所有的步骤,并不复杂,可以写一个简单的实例来验证Lua环境是否搭建好了。
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import org.keplerproject.luajava.*;

public class HelloAndroidActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
LuaState L = LuaStateFactory.newLuaState();
L.openLibs();
L.LdoString("text = 'Hello Android, I am Lua.'");
L.getGlobal("text");
String text = L.toString(-1);
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText(text);
setContentView(tv);
}
}

下面是运行结果图:

到此Android下的Lua环境就配置好了,至于怎么运用就看各位程序员的设计了。

[转载]SQL Server客户端工具(WPF,开源)

mikel阅读(985)

[转载]SQL Server客户端工具(WPF,开源) – 路人.乙 – 博客园.

VS2010 WPF开发的SQL Server客户端工具,可以完美操作远程数据库,包括查看所有数据表、视图、存储过程、自定义函数,导出各种脚本。

登陆界面

管理界面

源码下载:http://download.csdn.net/source/3471019

更多资源:http://sq_zhuyi.download.csdn.net/

我的博客:http://blog.csdn.net/sq_zhuyi