[转载]深入剖析Android消息机制

mikel阅读(762)

[转载]深入剖析Android消息机制 – coolszy – 博客园.

Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统,避免一些低级的错误。在学习Android中消息机制之前,我们先了解与消息有关的几个类:

1.Message

消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:

a.arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放Service的ID。

b.obj:该字段是Object类型,我们可以让该字段传递某个多项到消息的接受者中。

c.what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理Button事件时,通过switch(v.getId())判断是点击了哪个按钮。

在使用Message时,我们可以通过new Message()创建一个Message实例,但是Android更推荐我们通过Message.obtain()或者 Handler.obtainMessage()获取Message对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的 Message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的Message实例,则根据给定的参数new一个新Message对象。 通过分析源码可得知,Android系统默认情况下在消息池中实例化10个Message对象。

2.MessageQueue
消息队列,用来存放Message对象的数据结构,按照“先进先出”的原则存放消息。存放并非实际意义的保存,而是将Message对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。我们可以通过Looper.myQueue()获取当前线程中的MessageQueue。

3.Looper

MessageQueue的管理者,在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在一个Looper对象和一个MessageQueue对象。在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用Looper.loop()方法。典型的用法如下:

class LooperThread extends Thread

{

public Handler mHandler;

public void run()

{

Looper.prepare();

//其它需要处理的操作

Looper.loop();

}

}

倘若我们的线程中存在Looper对象,则我们可以通过Looper.myLooper()获取,此外我们还可以通过Looper.getMainLooper()获取当前应用系统中主线程的Looper对象。在这个地方有一点需要注意,假如Looper对象位于应用程序主线程中,则Looper.myLooper()和Looper.getMainLooper()获取的是同一个对象。

4.Handler

消息的处理者。通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)把Message对象添加到MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的handler对象的handleMessage()方法对其进行处理。由于是在handleMessage()方法中处理消息,因此我们应该编写一个类继承自Handler,然后在handleMessage()处理我们需要的操作。

下面我们通过跟踪代码分析在Android中是如何处理消息。首先贴上测试代码:

/**

*

* @author coolszy

* @blog http://blog.csdn.net/coolszy

*

*/

public class MessageService extends Service

{

private static final String TAG = "MessageService";

private static final int KUKA = 0;

private Looper looper;

private ServiceHandler handler;

/**

* 由于处理消息是在Handler的handleMessage()方法中,因此我们需要自己编写类

* 继承自Handler类,然后在handleMessage()中编写我们所需要的功能代码

* @author coolszy

*

*/

private final class ServiceHandler extends Handler

{

public ServiceHandler(Looper looper)

{

super(looper);

}

@Override

public void handleMessage(Message msg)

{

// 根据what字段判断是哪个消息

switch (msg.what)

{

case KUKA:

//获取msg的obj字段。我们可在此编写我们所需要的功能代码

Log.i(TAG, "The obj field of msg:" + msg.obj);

break;

// other cases

default:

break;

}

// 如果我们Service已完成任务,则停止Service

stopSelf(msg.arg1);

}

}

@Override

public void onCreate()

{

Log.i(TAG, "MessageService-->onCreate()");

// 默认情况下Service是运行在主线程中,而服务一般又十分耗费时间,如果

// 放在主线程中,将会影响程序与用户的交互,因此把Service

// 放在一个单独的线程中执行

HandlerThread thread = new HandlerThread("MessageDemoThread", Process.THREAD_PRIORITY_BACKGROUND);

thread.start();

// 获取当前线程中的looper对象

looper = thread.getLooper();

//创建Handler对象,把looper传递过来使得handler、

//looper和messageQueue三者建立联系

handler = new ServiceHandler(looper);

}

@Override

public int onStartCommand(Intent intent, int flags, int startId)

{

Log.i(TAG, "MessageService-->onStartCommand()");

//从消息池中获取一个Message实例

Message msg = handler.obtainMessage();

// arg1保存线程的ID,在handleMessage()方法中

// 我们可以通过stopSelf(startId)方法,停止服务

msg.arg1 = startId;

// msg的标志

msg.what = KUKA;

// 在这里我创建一个date对象,赋值给obj字段

// 在实际中我们可以通过obj传递我们需要处理的对象

Date date = new Date();

msg.obj = date;

// 把msg添加到MessageQueue中

handler.sendMessage(msg);

return START_STICKY;

}

@Override

public void onDestroy()

{

Log.i(TAG, "MessageService-->onDestroy()");

}

@Override

public IBinder onBind(Intent intent)

{

return null;

}

}

运行结果:

注:在测试代码中我们使用了HandlerThread类,该类是Thread的子类,该类运行时将会创建looper对象,使用该类省去了我们自己编写Thread子类并且创建Looper的麻烦。

下面我们分析下程序的运行过程:

1.onCreate()

首先启动服务时将会调用onCreate()方法,在该方法中我们new了一个HandlerThread对象,提供了线程的名字和优先级。

紧接着我们调用了start()方法,执行该方法将会调用HandlerThread对象的run()方法:

public void run() {

mTid = Process.myTid();

Looper.prepare();

synchronized (this) {

mLooper = Looper.myLooper();

notifyAll();

}

Process.setThreadPriority(mPriority);

onLooperPrepared();

Looper.loop();

mTid = -1;

}

在run()方法中,系统给线程添加的Looper,同时调用了Looper的loop()方法:

public static final void loop() {

Looper me = myLooper();

MessageQueue queue = me.mQueue;

while (true) {

Message msg = queue.next(); // might block

//if (!me.mRun) {

// break;

//}

if (msg != null) {

if (msg.target == null) {

// No target is a magic identifier for the quit message.

return;

}

if (me.mLogging!= null) me.mLogging.println(

">>>>> Dispatching to " + msg.target + " "

+ msg.callback + ": " + msg.what

);

msg.target.dispatchMessage(msg);

if (me.mLogging!= null) me.mLogging.println(

"<<<<< Finished to " + msg.target + " "

+ msg.callback);

msg.recycle();

}

}

}

&#91;/java&#93;
通过源码我们可以看到loop()方法是个死循环,将会不停的从MessageQueue对象中获取Message对象,如果MessageQueue 对象中不存在Message对象,则结束本次循环,然后继续循环;如果存在Message对象,则执行 msg.target.dispatchMessage(msg),但是这个msg的.target字段的值是什么呢?我们先暂时停止跟踪源码,返回到onCreate()方法中。线程执行完start()方法后,我们可以获取线程的Looper对象,然后new一个ServiceHandler对象,我们把Looper对象传到ServiceHandler构造函数中将使handler、looper和messageQueue三者建立联系。

2.onStartCommand()

执行完onStart()方法后,将执行onStartCommand()方法。首先我们从消息池中获取一个Message实例,然后给Message对象的arg1、what、obj三个字段赋值。紧接着调用sendMessage(msg)方法,我们跟踪源代码,该方法将会调用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又会调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在该方法中我们要注意该句代码msg.target = this,msg的target指向了this,而this就是ServiceHandler对象,因此msg的target字段指向了ServiceHandler对象,同时该方法又调用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:
&#91;java&#93;
final boolean enqueueMessage(Message msg, long when) {

if (msg.when != 0) {

throw new AndroidRuntimeException(msg

+ " This message is already in use.");

}

if (msg.target == null && !mQuitAllowed) {

throw new RuntimeException("Main thread not allowed to quit");

}

synchronized (this) {

if (mQuiting) {

RuntimeException e = new RuntimeException(

msg.target + " sending message to a Handler on a dead thread");

Log.w("MessageQueue", e.getMessage(), e);

return false;

} else if (msg.target == null) {

mQuiting = true;

}



msg.when = when;

//Log.d("MessageQueue", "Enqueing: " + msg);

Message p = mMessages;

if (p == null || when == 0 || when < p.when) {

msg.next = p;

mMessages = msg;

this.notify();

} else {

Message prev = null;

while (p != null && p.when <= when) {

prev = p;

p = p.next;

}

msg.next = prev.next;

prev.next = msg;

this.notify();

}

}

return true;

}

&#91;/java&#93;
该方法主要的任务就是把Message对象的添加到MessageQueue中(数据结构最基础的东西,自己画图理解下)。

handler.sendMessage()-->handler.sendMessageDelayed()-->handler.sendMessageAtTime()-->msg.target = this;queue.enqueueMessage==>把msg添加到消息队列中

3.handleMessage(msg)

onStartCommand()执行完毕后我们的Service中的方法就执行完毕了,那么handleMessage()是怎么调用的呢?在前面分析的loop()方法中,我们当时不知道msg的target字段代码什么,通过上面分析现在我们知道它代表ServiceHandler对象,msg.target.dispatchMessage(msg);则表示执行ServiceHandler对象中的dispatchMessage()方法:

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

该方法首先判断callback是否为空,我们跟踪的过程中未见给其赋值,因此callback字段为空,所以最终将会执行handleMessage()方法,也就是我们ServiceHandler类中复写的方法。在该方法将根据what字段的值判断执行哪段代码。

至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱中。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

代码下载地址:http://u.115.com/file/f1e0a5d5db

[转载]服务器缓存不依赖URL的方法(OutPutCache)及客户端不缓存,完美做法

mikel阅读(749)

[转载]服务器缓存不依赖URL的方法(OutPutCache)及客户端不缓存,完美做法 – 达奇-方向比努力更重要 – 博客园.

可以避免客户端缓存:

<%Response.Cache.SetNoStore();%>

———————————————————————————————————————-

经过测试,下面的方式会清空服务器端的缓存。不适合配合Global.asax使用

<% Response.CacheControl = “no-cache” %>
<% Response.AddHeader “Pragma”, “no-cache” %>
<% Response.Expires = -1 %>s

———————————————————————————————————————-

服务器缓存方式:

Global.asax:

新增函数:

public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (“Student”.Equals(custom))
{
return string.Format(“{0}”, “动态数据”);
}
return base.GetVaryByCustomString(context, custom);
}

页面:

<%@ OutputCache Duration=”180″ VaryByCustom=”Student” VaryByParam=”none”  %>

[转载]用sql写个作业每隔五分钟去读某个文件夹下所有xml文件,并导入到DB中

mikel阅读(701)

[转载]用sql写个作业每隔五分钟去读某个文件夹下所有xml文件,并导入到DB中 – 编程之美 – 博客园.

消息 15281,级别 16,状态 1,过程 xp_cmdshell,第 1 行
SQL Server 阻止了对组件 ‘xp_cmdshell’ 的 过程 ‘sys.xp_cmdshell’ 的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用 sp_configure 启用 ‘xp_cmdshell’。有关启用 ‘xp_cmdshell’ 的详细信息,请参阅 SQL Server 联机丛书中的 “外围应用配置器”。

exec sp_configure 'show advanced options', 1;
reconfigure;
exec sp_configure 'xp_cmdshell', 1; reconfigure;

用sql写个作业每隔五分钟去读某个文件夹下所有xml文件,并把它的数据插入到一张表在把它删除

--create table TEST (
--A char(10),B char(10),C char(10)
--)

DECLARE
@XMLDIR varchar(2000), --XML放置的文件全路径
@DIR varchar(1000), --XML的文件夹
@FileFullName varchar(8000) --游标用,文件名称(全路径)
declare
@FileTable TABLE(x varchar(8000)) --临时表,存储 文件夹所有XML文件名称

SET @DIR='D:\TEST'
SET @XMLDIR=N'DIR '+ @DIR + '\*.xml' --*.xml 只读取是XML的扩展名文件

INSERT @FileTable
exec xp_cmdshell @XMLDIR --将文件夹的内容读取插入临时表中
delete from @FileTable where x not like '%.xml%' or x is null
update @FileTable set x=@DIR+'\'+SUBSTRING(x,37,120)
--select * from @FileTable


declare fCursor cursor for
select x from @FileTable
open fCursor
fetch next from fCursor into @FileFullName
while(@@fetch_status=0)
BEGIN
declare
@xml varchar(8000), --XML转换成列的内容
@Pointer INT, --指向位置的变量
@DELFILENAME varchar(2000) --删除文件的CMDSHELL
declare @table TABLE(x varchar(8000))
SET @DELFILENAME= 'DEL '+@FileFullName
insert into @table EXEC ('(SELECT * FROM OPENROWSET(BULK '''+@FileFullName+''',SINGLE_CLOB) as x)')
select @xml=x from @table
EXECUTE sp_xml_preparedocument @Pointer OUTPUT,@xml
INSERT INTO TEST(A,B,C)
SELECT A,B,C
FROM OPENXML (@Pointer,'ROOT/Row')WITH(A varchar(10),B varchar(10),C varchar(10))
EXEC sp_xml_removedocument @Pointer

EXEC xp_cmdshell @DELFILENAME
fetch next from fCursor into @FileFullName
END
close fCursor --关闭游标
deallocate fCursor --删除游标

剩下的就是加个代理了..哈…5分钟执行一次

XML文件如下

<ROOT>
	<Row A='1' B='1' C='1'></Row>
	<Row A='2' B='1' C='1'></Row>
	<Row A='3' B='1' C='1'></Row>
	<Row A='4' B='1' C='1'></Row>
</ROOT>

[转载]ASP.NET中的图片缓存

mikel阅读(1367)

[转载]ASP.NET中的图片缓存 – 心如止水 – 博客园.

原文地址:Caching Images in ASP.NET , 版权归原文作者所有。

引言:
在一个Web应用程序中,可以通过很多种方法来改善其性能,其中最简单但也是最有效的方法就是在客户端缓存图片,这篇文章就是告诉你如何在你的站点中实现图片的缓存。

问题:
我曾经建立过一个站点,在CSS样式表中使用了很多图片来作为菜单项的背景。网站完成之后,我使用Microsoft Network Monitor微软的一款流量分析工具,可从微软下载中心下载网站的流量进行了统计,发现每次对首页的访问都会对20个不同的文件发出请求,其中一半以上都来至于对菜单背景图片的请求。

有两种方法可以解决这个问题,第一种方法是通过IIS实现图片的缓存;第二种方法是直接在ASP.NET实现缓存。

通过IIS缓存图片:
这种方法非常简单,首先选中IIS管理器中选中一个文件或文件夹,右键单击,打开属性对话框。

选中HTTP头选项卡中的“启用内容过期”,并根据需要设定过期时间。这样客户端就会对你设定的文件进行缓存,直到缓存过期才会向服务端发起新的请求。
当你对IIS拥有足够的管理权限,并且网站的图片位置相对比较集中时,这种方法是一种很好的选择。但这样的条件往往得不到满足,这个时候你就需要使用第二种方法了。

通过HttpHandle缓存图片
为了获取对ASP.NET的请求,需要编写一个自定义HttpHandle来对图片文件(*.jpg;*.gif;*.png)进行监听。首先在Visuan Studio中新建一个类库工程,取名为CachingHandler,负责处理对图片的请求。CachingHandler需要实现IHttpHandle接口,在IHttpHandle接口中,IsReusable属性指示其他请求是否可以使用该IHttpHandler实例ProcessRequest()方法负责获取和发送数据。

namespace SoftwareArchitects.Web
{
public class CachingHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}

public void ProcessRequest(HttpContext context)
{
string file = context.Server.MapPath
(context.Request.FilePath.Replace(“.ashx”, “”));
string filename = file.Substring(file.LastIndexOf(‘\\’) + 1);
string extension = file.Substring(file.LastIndexOf(‘.’) + 1);
CachingSection config = (CachingSection)context.GetSection
(“SoftwareArchitects/Caching”);
if (config != null)
{
context.Response.Cache.SetExpires
(DateTime.Now.Add(config.CachingTimeSpan));
context.Response.Cache.SetCacheability
(HttpCacheability.Public);
context.Response.Cache.SetValidUntilExpires(false);

FileExtension fileExtension = config.FileExtensions[extension];
if (fileExtension != null)
{
context.Response.ContentType = fileExtension.ContentType;
}
}
context.Response.AddHeader(“content-disposition”,
“inline; filename=” + filename);
context.Response.WriteFile(file);
}
}
}


配置Web.Config文件
在上面的代码中,我们使用了一个自定义类ConfigurationSection来读写Web.Config的信息,下面是这个类的实现。

namespace SoftwareArchitects.Web.Configuration
{
/// <summary>
/// Configuration for caching
/// </summary>
public class CachingSection : ConfigurationSection
{
[ConfigurationProperty(“CachingTimeSpan”, IsRequired = true)]
public TimeSpan CachingTimeSpan
{
get { return (TimeSpan)base[“CachingTimeSpan”]; }
set { base[“CachingTimeSpan”] = value; }
}

[ConfigurationProperty(“FileExtensions”, IsDefaultCollection = true,
IsRequired = true)]
public FileExtensionCollection FileExtensions
{
get { return ((FileExtensionCollection)base[“FileExtensions”]); }
}
}
/// <summary>
/// List of available file extensions
/// </summary>
public class FileExtensionCollection : ConfigurationElementCollection
{

}
/// <summary>
/// Configuration for a file extension
/// </summary>
public class FileExtension : ConfigurationElement
{
[ConfigurationProperty(“Extension”, IsRequired = true)]
public string Extension
{
get { return (string)base[“Extension”]; }
set { base[“Extension”] = value.Replace(“.”, “”); }
}

[ConfigurationProperty(“ContentType”, IsRequired = true)]
public string ContentType
{
get { return (string)base[“ContentType”]; }
set { base[“ContentType”] = value; }
}
}
}

完整的ConfigurationSection类:CachingSection.cs
最后是在Web.Config文件中添加相关的信息:

<configuration>
<configSections>
<sectionGroup name=”SoftwareArchitects”>
<section name=”Caching” requirePermission=”false”
type=”SoftwareArchitects.Web.Configuration.CachingSection,
SoftwareArchitects.Web.CachingHandler” />
</sectionGroup>
</configSections>

<SoftwareArchitects>
<Caching CachingTimeSpan=”1″>
<FileExtensions>
<add Extension=”gif” ContentType=”image\gif” />
<add Extension=”jpg” ContentType=”image\jpeg” />
<add Extension=”png” ContentType=”image\png” />
</FileExtensions>
</Caching>
</SoftwareArchitects>


<httpHandlers>
<add verb=”*” path=”*.gif.ashx”
type=”SoftwareArchitects.Web.CachingHandler,
SoftwareArchitects.Web.CachingHandler”/>
<add verb=”*” path=”*.jpg.ashx”
type=”SoftwareArchitects.Web.CachingHandler,
SoftwareArchitects.Web.CachingHandler”/>
<add verb=”*” path=”*.png.ashx”
type=”SoftwareArchitects.Web.CachingHandler,
SoftwareArchitects.Web.CachingHandler”/>
</httpHandlers>
</configuration>

在站点中完成以上代码的添加之后,再次使用Microsoft Network Monitor进行测试,第一次访问首页时依然是20个不同的请求,但到了第二次访问请求就变为了7个,因为所有的图片文件都已经缓存到了客户端。

在原文中,作者还提供了一个完整的Solution来测试图片缓存功能,大家可以自由下载.

[转载]网站中的图片缓存

mikel阅读(1134)

[转载]网站中的图片缓存 – MIDI 欢迎你:-) – 博客园.

动态页中存在大量图片而影响速度,是在项目开发中所不可以避免的问题,考虑缓存图片却保持页面的执行是个很不错的选择。
实现一个IhttpHandler对特定的文件格式进行处理,对于图片可以是.jpg也可以是其他的格式,当客户端请求有.jpg后缀的文件时IIS自动将文件交给asp_isapi执行然后实现IhttpHandler 接口中的ProcessRequest方法对页面进行缓存,同时还能对图片进行其他的操作,如添加水印,裁剪图片等操作。
具体操作流程如下:在IIS中配置网站添加一个.jpg映射交给asp_isapi处理然后在Web.Config中进行相关配置:
<httpHandlers>
<!–只处理.jpg文件,可以分目录缓存图片aaa/*.jpg–>
<addpath=*.jpgverb=*type=ImageCache.CachingHandler,ImageCache/>
</httpHandlers>
然后在创建一个类CachingHandler : IhttpHandler继承IhttpHandler接口
实现ProcessRequest方法
public void ProcessRequest(HttpContext context)
{
string imagePath = context.Request.PhysicalPath;//获取服务器路径
Bitmap image = null;
image = new Bitmap(imagePath);
context.Response.Cache.SetCacheability(HttpCacheability.Private);//在客户端缓存
context.Response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));//缓存时间为5秒
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
在页面中添加一个图片并执行这个事件
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(DateTime.Now.ToString());
}
可以看到页面没有被缓存但其中的图片确被缓存了下来。
图片缓存和水印实例 下载地址:http://d.download.csdn.net/down/454222/sunchaohuang

[转载]一个Asp.net mvc下的分页控件MvcPagerX

mikel阅读(844)

[转载]【分享】一个Asp.net mvc下的分页控件MvcPagerX – 哥在博客园 – 博客园.

分页再平常不过的需求,其实也很简单完全可以写百行以内的代码扩展到HtmlHelper上实现,但是为了扩展性,我稍微(这所以是稍微,是因为本 人只稍微了解,还不敢说用得很熟练)用了点面向对象的思想对这个分页控件进行了设计,当然设计之前也参考了一些盆友的作品。吸取了一些思想的精华,下面就 讲讲怎么设计以及为什么要这么设计

接口部分:

using System.Collections.Generic;

namespace MvcPagerx
{
    /// <summary>
    /// 分页接口
    /// </summary>
    public interface IPageAble
    {
        /// <summary>
        /// 一个按钮或者少于一个按钮的时候
        /// </summary>
        /// <returns></returns>
        IList<PageButton> GetBtnLessThanOnePage();
        /// <summary>
        /// 第一页时
        /// </summary>
        /// <returns></returns>
        IList<PageButton> GetBtnWhenFrist();
        /// <summary>
        /// 最后一页时
        /// </summary>
        /// <returns></returns>
        IList<PageButton> GetBtnWhenLast();
        /// <summary>
        /// 默认情况
        /// </summary>
        /// <returns></returns>
        IList<PageButton> GetBtnWhenDefault();
        /// <summary>
        /// 分页设置
        /// </summary>
        PagerSettings PagerSetting { get; set; }
    }
}

此接口主要标明实现类可以被分页,在需要分页的各种情况给出反馈,有

一个按钮或者少于一个按钮的时候

第一页时

最后一页时

默认情况

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.Web.Mvc;

namespace MvcPagerx
{
    /// <summary>
    /// 能够根据分页按钮生成HTML接口
    /// </summary>
    public interface IGeneratePagerHtmlAble
    {
        /// <summary>
        /// PagerSetting
        /// </summary>
        PagerSettings PagerSetting { get; set; }
        /// <summary>
        /// Generate
        /// </summary>
        /// <param name="pageButtons"></param>
        /// <returns></returns>
        MvcHtmlString Generate(IList<PageButton> pageButtons);

        Func<RequestContext, string, int, string> UrlCallback { set; }
    }
}

【分享】一个ASP.NET mvc下的分页控件MvcPagerX
2011-04-21 22:39 by 鲜宏, 1222 visits, 网摘, 收藏, 编辑

分页再平常不过的需求,其实也很简单完全可以写百行以内的代码扩展到HtmlHelper上实现,但是为了扩展性,我稍微(这所以是稍微,是因为本人只稍微了解,还不敢说用得很熟练)用了点面向对象的思想对这个分页控件进行了设计,当然设计之前也参考了一些盆友的作品。吸取了一些思想的精华,下面就讲讲怎么设计以及为什么要这么设计

接口部分:

using System.Collections.Generic;

namespace MvcPagerx
{
///

/// 分页接口
///

public interface IPageAble
{
///

/// 一个按钮或者少于一个按钮的时候
///

///
IList GetBtnLessThanOnePage();
///

/// 第一页时
///

///
IList GetBtnWhenFrist();
///

/// 最后一页时
///

///
IList GetBtnWhenLast();
///

/// 默认情况
///

///
IList GetBtnWhenDefault();
///

/// 分页设置
///

PagerSettings PagerSetting { get; set; }
}
}

此接口主要标明实现类可以被分页,在需要分页的各种情况给出反馈,有

一个按钮或者少于一个按钮的时候

第一页时

最后一页时

默认情况

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.Web.Mvc;

namespace MvcPagerx
{
///

/// 能够根据分页按钮生成HTML接口
///

public interface IGeneratePagerHtmlAble
{
///

/// PagerSetting
///

PagerSettings PagerSetting { get; set; }
///

/// Generate
///

/// ///
MvcHtmlString Generate(IList pageButtons);

Func UrlCallback { set; }
}
}

这个接口呢用于标明实现类具有生成HTML代码的功能,在这里我们可以看到,传入的参数是IList,返回的是MvcHtmlString,为什么要传入IList,因为这里给这个接口定位很明确就是只是完成将PageButton这种实体列表转换为相应的HTML代码就可以了。那么谁负责生成IList呢,看看上面的IpageAble接口便知道,那就是实现了IpageAble的类,请看下面

实现类部分:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
namespace MvcPagerx
{
    /// <summary>
    /// 普通的分页类
    /// </summary>
    public class NormalPageProc : IPageAble
    {

        #region fields
        private int _StartPageIndex;
        private int _EndPageIndex;
        public PagerSettings PagerSetting { get; set; }
        #endregion

        #region Private Method

        
        private void Init()
        {
            _StartPageIndex = PagerSetting.CurrentPageIndex - (PagerSetting.NumericPagerCount / 2);
            if (_StartPageIndex + PagerSetting.NumericPagerCount > PagerSetting.PageCount)
                _StartPageIndex = PagerSetting.PageCount + 1 - PagerSetting.NumericPagerCount;
            if (_StartPageIndex < 1)
                _StartPageIndex = 1;

            _EndPageIndex = _StartPageIndex + PagerSetting.NumericPagerCount - 1;
            if (_EndPageIndex > PagerSetting.PageCount)
                _EndPageIndex = PagerSetting.PageCount;
        }

        private void AddFristButton(IList<PageButton> pageBtnList)
        {
            PageButton fristitem = new PageButton(PagerSetting.FirstPageText, 1, PagerSetting.CurrentPageIndex == 1, PageButtonType.FirstPageButton);
            pageBtnList.Add(fristitem);
        }

        private void AddPrevButton(IList<PageButton> pageBtnList)
        {
            var previtem = new PageButton(PagerSetting.PrevPageText, PagerSetting.CurrentPageIndex - 1, PagerSetting.CurrentPageIndex == 1, PageButtonType.PrevPageButton);
            pageBtnList.Add(previtem);
        }

        private void AddMoreButtonBefore(IList<PageButton> pageBtnList)
        {
            if (_StartPageIndex > 1 && PagerSetting.ShowMorePagerItems)
            {
                var index = _StartPageIndex - 1;
                if (index < 1) index = 1;
                PageButton item = new PageButton(PagerSetting.MorePageText, index, false, PageButtonType.MorePageButton);
                pageBtnList.Add(item);
            }
        }

        private void AddNumberButton(IList<PageButton> pageBtnList)
        {
            for (var pageIndex = _StartPageIndex; pageIndex <= _EndPageIndex; pageIndex++)
            {
                var text = pageIndex.ToString();
                if (pageIndex == PagerSetting.CurrentPageIndex && !string.IsNullOrEmpty(PagerSetting.CurrentPageNumberFormatString))
                    text = String.Format(PagerSetting.CurrentPageNumberFormatString, text);
                else if (!string.IsNullOrEmpty(PagerSetting.PageNumberFormatString))
                    text = String.Format(PagerSetting.PageNumberFormatString, text);
                var item = new PageButton(text, pageIndex, false, PageButtonType.NumericPageButton);
                pageBtnList.Add(item);
            }
        }

        private void AddMoreButtonAfter(IList<PageButton> pageBtnList)
        {
            if (_EndPageIndex < PagerSetting.PageCount)
            {
                var index = _StartPageIndex + PagerSetting.NumericPagerCount;
                if (index > PagerSetting.PageCount) index = PagerSetting.PageCount;
                var item = new PageButton(PagerSetting.MorePageText, index, false, PageButtonType.MorePageButton);
                pageBtnList.Add(item);
            }
        }

        private void AddNextButton(IList<PageButton> pageBtnList)
        {
            var nextitem = new PageButton(PagerSetting.NextPageText, PagerSetting.CurrentPageIndex + 1, PagerSetting.CurrentPageIndex >= PagerSetting.PageCount, PageButtonType.NextPageButton);
            pageBtnList.Add(nextitem);
        }

        private void AddLastButton(IList<PageButton> pageBtnList)
        {
            var lastitem = new PageButton(PagerSetting.LastPageText, PagerSetting.PageCount, PagerSetting.CurrentPageIndex >= PagerSetting.PageCount, PageButtonType.LastPageButton);
            pageBtnList.Add(lastitem);
        }


        private IList<PageButton> AddButtons()
        {
            IList<PageButton> pageBtnList = new List<PageButton>();
            AddFristButton(pageBtnList);        //<---添加第一页
            AddPrevButton(pageBtnList);         //<---添加前一页
            AddMoreButtonBefore(pageBtnList);   //<---添加更多按钮(前置)
            AddNumberButton(pageBtnList);       //<---添加数字分页按钮
            AddMoreButtonAfter(pageBtnList);    //<---添加更多按钮(后置)
            AddNextButton(pageBtnList);         //<---添加下一页
            AddLastButton(pageBtnList);         //<---添加最后一页
            IEnumerable<PageButton> currentPages = pageBtnList.Where(p => p.PageIndex == PagerSetting.CurrentPageIndex);
            foreach (PageButton btn in currentPages)
                btn.Disabled = true;
            return pageBtnList;
        }
        #endregion

        /// <summary>
        /// 一个按钮或者少于一个按钮的时候
        /// </summary>
        /// <returns></returns>
        public IList<PageButton> GetBtnLessThanOnePage()
        {
            return new List<PageButton>() { 
                new PageButton(PagerSetting.FirstPageText,1,true,PageButtonType.FirstPageButton)
            };
        }

        /// <summary>
        /// 第一页时
        /// </summary>
        /// <returns></returns>
        public IList<PageButton> GetBtnWhenFrist()
        {
            IList<PageButton> defaultPageButtons = GetBtnWhenDefault();
            defaultPageButtons.SingleOrDefault(m => m.ButtonType == PageButtonType.PrevPageButton).Hide = true;
            defaultPageButtons.SingleOrDefault(m => m.ButtonType == PageButtonType.FirstPageButton).Hide = true;
            return defaultPageButtons;
        }

        /// <summary>
        /// 最后一页时
        /// </summary>
        /// <returns></returns>
        public IList<PageButton> GetBtnWhenLast()
        {
            IList<PageButton> defaultPageButtons = GetBtnWhenDefault();
            defaultPageButtons.SingleOrDefault(m => m.ButtonType == PageButtonType.NextPageButton).Hide = true;
            defaultPageButtons.SingleOrDefault(m => m.ButtonType == PageButtonType.LastPageButton).Hide = true;
            return defaultPageButtons;
        }

        /// <summary>
        /// 默认情况
        /// </summary>
        /// <returns></returns>
        public IList<PageButton> GetBtnWhenDefault()
        {
            Init();
            return AddButtons();
        }

    }
}

此类就是普通的分页生成器,在此类实现了IpageAble中定义的各种情况下需要生成的PageButton列表,然后返回给IGeneratePagerHtmlAble的实现类

如下的NormalPagerHtmlGeenerate

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.Web.Mvc;

namespace MvcPagerx
{
    /// <summary>
    /// 普通分页HTML代码生成类
    /// </summary>
    internal class NormalPagerHtmlGenerate : IGeneratePagerHtmlAble
    {

        private Func<RequestContext, string, int, string> _GetUrlCallback;

        /// <summary>
        /// 分页设置
        /// </summary>
        public PagerSettings PagerSetting { get; set; }


        private string WrapPageButton(PageButton btn)
        {
            string result = string.Empty;
            if (btn.Disabled)
                return String.Format("<li><a disabled=\"disabled\">{0}</a></li>", btn.Text);
            result = String.Format("<li><a href='{0}'>{1}</a></li>", _GetUrlCallback(PagerSetting.HtmlRequestContext, PagerSetting.PageParameterName, btn.PageIndex), btn.Text);
            return result;
        }

        /// <summary>
        /// 生成HTML代码
        /// </summary>
        /// <param name="pageButtons">按钮列表</param>
        /// <returns></returns>
        public MvcHtmlString Generate(IList<PageButton> pageButtons)
        {
            TagBuilder tagBuilder = new TagBuilder(PagerSetting.TagName);
            tagBuilder.GenerateId(PagerSetting.TagID);
            if (!string.IsNullOrEmpty(PagerSetting.ClassName))
                tagBuilder.AddCssClass(PagerSetting.ClassName);
            StringBuilder sb = new StringBuilder();
            foreach (PageButton btn in pageButtons)
            {
                if (!btn.Hide)
                    sb.Append(WrapPageButton(btn));
            }

            tagBuilder.InnerHtml = sb.ToString();
            return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
        }
        
        public Func<RequestContext, string, int, string> UrlCallback
        {
            set
            {
                _GetUrlCallback = value;
            }
        }
    }
}

交给此类后便能生成HTML代码到前端展示了,还是相当的简单吧,更多的东西直接看代码吧,这里只是提一下大概思路。

MvcPagerx.rar

[转载]第三十六讲:Android手机摄像头编程入门

mikel阅读(897)

[转载]第三十六讲:Android手机摄像头编程入门 « { Android学习指南 }.

本讲内容:Android手机摄像头编程入门

智能手机中的摄像头和普通手机中的摄像头最大的区别在于,智能机上的摄像头可以由程序员写程序控制,做一些有趣的应用譬如,画中画,做一些有用的应用譬如二维码识别,等等。本讲打算通过一个实例,来介绍一下摄像头编程,相关解释都写在代码中了,请注意看代码注释。

实例:窈窈照相机,功能很简单,就是点击程序弹出照相预览界面,点击相机按钮完成照相功能,所照相片会存储在手机存储卡根目录。

1、创建一个项目 Lesson36_Camera ,主程序文件为 MainActivity.java

2、AndroidManifest.xml 中设置屏幕为横屏,并且声明摄像头和存储卡的使用权限,具体代码如下:

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

3、本例中不需要布局文件main.xml,因为本例中的UI组建都是动态添加上去的。

4、最后MainActivity.java的代码如下:

package basic.android.lesson36;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Locale;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

private CameraView cv;
//准备一个相机对象
private Camera mCamera = null;
//准备一个Bitmap对象
private Bitmap mBitmap = null;

//准备一个保存图片的PictureCallback对象
public Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {

public void onPictureTaken(byte[] data, Camera camera) {
Log.i("yao","onPictureTaken");
Toast.makeText(getApplicationContext(), "正在保存……", Toast.LENGTH_LONG).show();
//用BitmapFactory.decodeByteArray()方法可以把相机传回的裸数据转换成Bitmap对象
mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
//接下来的工作就是把Bitmap保存成一个存储卡中的文件
File file = new File("/sdcard/YY"+ new DateFormat().format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg");
try {
file.createNewFile();
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush();
os.close();
Toast.makeText(getApplicationContext(), "图片保存完毕,在存储卡的根目录", Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
}
}

};

//Activity的创建方法
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//窗口去掉标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
//窗口设置为全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
//设置窗口为半透明
getWindow().setFormat(PixelFormat.TRANSLUCENT);

//提供一个帧布局
FrameLayout  fl = new FrameLayout(this);

//创建一个照相预览用的SurfaceView子类,并放在帧布局的底层
cv = new CameraView(this);
fl.addView(cv);

//创建一个文本框添加在帧布局中,我们可以看到,文字自动出现在了SurfaceView的前面,由此你可以在预览窗口做出各种特殊效果
TextView tv = new TextView(this);
tv.setText("请按\"相机\"按钮拍摄");
fl.addView(tv);

//设置Activity的根内容视图
setContentView(fl);

}

//相机按键按下的事件处理方法
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.i("yao","MainActivity.onKeyDown");
if (keyCode == KeyEvent.KEYCODE_CAMERA) {
if (mCamera != null) {
Log.i("yao","mCamera.takePicture");
//当按下相机按钮时,执行相机对象的takePicture()方法,该方法有三个回调对象做入参,不需要的时候可以设null
mCamera.takePicture(null, null, pictureCallback);
}
}
return cv.onKeyDown(keyCode, event);
}

// 照相视图
class CameraView extends SurfaceView {

private SurfaceHolder holder = null;

//构造函数
public CameraView(Context context) {
super(context);
Log.i("yao","CameraView");

// 操作surface的holder
holder = this.getHolder();
// 创建SurfaceHolder.Callback对象
holder.addCallback(new SurfaceHolder.Callback() {

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 停止预览
mCamera.stopPreview();
// 释放相机资源并置空
mCamera.release();
mCamera = null;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
//当预览视图创建的时候开启相机
mCamera = Camera.open();
try {
//设置预览
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
// 释放相机资源并置空
mCamera.release();
mCamera = null;
}

}

//当surface视图数据发生变化时,处理预览信息
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

//获得相机参数对象
Camera.Parameters parameters = mCamera.getParameters();
//设置格式
parameters.setPictureFormat(PixelFormat.JPEG);
//设置预览大小,这里我的测试机是Milsstone所以设置的是854x480
parameters.setPreviewSize(854, 480);
//设置自动对焦
parameters.setFocusMode("auto");
//设置图片保存时的分辨率大小
parameters.setPictureSize(2592, 1456);
//给相机对象设置刚才设定的参数
mCamera.setParameters(parameters);
//开始预览
mCamera.startPreview();
}
});
// 设置Push缓冲类型,说明surface数据由其他来源提供,而不是用自己的Canvas来绘图,在这里是由摄像头来提供数据
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

}

}

5、连接Milestone,编译并运行程序:悲剧的是,截图只能看到黑黑的一片,无法截取到摄像头传输过来SurfaceView信息,而在真机中是能看到预览效果的。

device

还是上一张照好的图片给大家吧,(用了好多年的小黑……)

YY20101208_030141

好了,本讲就到这里,谢谢大家的支持和鼓励,下次再见。

[转载]关于网页静态化及SEO问题的一些补充

mikel阅读(963)

[转载]关于网页*静态化*及SEO问题的一些补充 – 老赵点滴 – 追求编程之美 – 博客园.

前一篇讨论“静态页”的文章反响不错,不少朋友发表了自己的看法,也给老赵更多的想法。虽然也在前一篇文章后面回复了不少内容,但是就以往经验来看,总结为一篇新的文章会让我想表达的内容更为明确,对于“静态化”这一非常容易被人误解的概念来说也是非常重要的。

seo我 们还是先来讨论一下,什么叫做“静态页”。有朋友说,放在硬盘上的htm或html文件便是一种静态页,Web服务器不需要做额外的处理,直接读取文件内 容并输出就可以了,而这样的静态文件对于SEO是有帮助的。至于理由,是搜索引擎会对html结尾的文件给更好的权值(这好像还是结论,不是理由),而这 是“常识”,“了解一点SEO的人都知道这个”,“人们普遍在使用的做法”,因此“它一定是正确的”。不过其实Google并不这么认为,百度倒没有给出专业说法。

当 然,我们已经重复强调,但还是需要不断明确的一点是,即使搜索引擎对于“静态页”有更好的倾向性,那也是因为其“URL样式”,而不是“在硬盘上放置了一 个html文件”。请求方(也就是爬虫)只是向服务器端发送一个URL,并获取服务器端给出的内容。它不会关心,也无法了解服务器端究竟是如何得到页面内 容的,对于客户端来说,世界上没有“静态”或“动态”页面之分。有些朋友可能还是会说“不会啊,html就是静态页面,像aspx之类的就是动态页面,前 者不需要在Web服务器上运算,后者需要”。

真是这样的吗?并非如此,因为html文件也是需要Web服务器来运算的。例如,您请求一个html文件,Web服务器至少做了几件事情:

  • 如果请求包含缓存信息,那么处理缓存状态。
  • 根据URL定位到磁盘上的文件。
  • 进行用户认证和授权(如,是否匿名?)。
  • 判断是否有权限读取。
  • 读取文件。
  • 根据文件类型设置MIME的值。
  • 根据文件最后修改日期设置Last-Modified值。
  • 根据文件内容及其他状态设置其E-Tag值。
  • 如果文件内部有include标记,那么读取另一个文件填充进来。

看 看,处理一个文件需要多少“动态运算”啊,这些可都是在Web服务器(如IIS)加载一个html所做的事情。如果您想要观察这些过程,可以阅读一些 Web服务器的源代码,或者去观察一下ASP.NET中System.Web.StaticFileHandler类所做的事情,它也体现了Web服务器 处理html时的关键之处。事实上,如果您在IIS中将html配置给ASP.NET ISAPI的话,或者使用VS自带的Web服务器,最后便是由StaticFileHandler来输出硬盘上的文件的。

所以,虽然我们 看起来Web服务器只是简单地读取了硬盘上的文件,但其实它还是不如我们想象的那么简单。不过对于客户端来说,这一切都是不可知的。例如 Squid,Nginx这样部署在前端的缓存或反向代理服务器,它们都不会关心后端Web服务器是Windows,Linux还是Unix,也不会关心是 IIS,Apache,Lightted甚至是我们自己写的高效或低劣的Web服务器。对于浏览器,爬虫,或前端负载均衡器来说,它们只知道TCP/IP 协议,它们只知道HTTP协议等东西,其他一概不知。

不过,也有朋友坚持认为“生成静态页”来“进行页面缓存”对SEO有帮助。理由是, “进行页面缓存”能够提高网站性能,爬虫更倾向于访问速度更快的页面。从这个角度看来,这种说法的确有一定道理。只是我还是不喜欢这样的看法,因为这种说 法没有把握事物关键。在这里,SEO的关键在于优化网站性能,而生成静态页只是一种手段之一。这并不是适用性最广的,也并非是最容易实现的。如果您直接把 “生成静态页”与“SEO”联系起来,很有可能会对他人造成误解。

当然,如果您的思路没有问题,“静态页”三个字的指代也足够明确,“静 态页有利于SEO”这个命题毫无疑问是正确的。不过我们现在并没有讨论一个命题的逻辑是否正确,我们也不必纠缠于一个表达形式是否严谨,我们的目的是要说 明道理。也正因为如此,老赵才会一遍一遍地写这么多内容。也就是说,这几篇文章的关键在于“说清道理”,我们把握它既可。

最后,老赵再谈一下对SEO这个工作的看法。

seo从 老赵与各SEO人员的接触感觉来看,他们总是有各种理由来说明“问题所在”,只是如果在改进问题之后还是没有效果的话,他们又可以找出各种理由来告诉你为 什么没有效果——但是要知道SEO是一个实践性工作,它的唯一判断依据便是“效果”,而不是“理论”。SEO的理论很容易掌握,但是如果无法真切提高一个 网站在搜索引擎上的表现,这一切还是白搭。老赵认为,一个好的SEO是需要了解网页制作,或者说网站开发的基本技术的,至少要有常识,否则基本上就是在扯 蛋。老赵曾经接触过一个“专业”的SEO公司,那里的“SEO咨询师”给我留下了深刻的印象——负面印象。其“非专业性”从以下几个事件中便可见一斑:

  1. 还 是“静态页”的问题。由于把URL变为.html结尾之后并没有得到明显的效果,他询问我们的实现方式。在得知我们使用了URL重写,而不是在硬盘上放置 html文件时他“惊呼”这种欺骗搜索引擎的行为是会起到反效果的。他强烈要求我们在硬盘上放置html文件。这个要求自然遭到了我们的拒绝,原因之一是 我们是非常动态的网站,很难实现这个需求,但是更重要的是,懂得一点技术的人就知道,Web服务器的处理方式对于搜索引擎爬虫时完全不可见的,我们是否真 正放置html文件与搜索引擎没有任何关系。
  2. 内容的位置问题。在SEO界有种说法是,搜索引擎会更倾向于把页面靠前的内容看的更 重,而把页面靠后的内容权值放低。因此那位专业SEO咨询师指着我们的某张页面说,这部分内容太靠“下方”,很容易被搜索引擎忽略。请注意,他说的是“内 容在页面显示的时候出现在下方”。您觉得这种说法有道理吗?如今页面布局往往使用XHTML+CSS的方式,而搜索引擎只会关注HTML的内容,而“位 置”很大程度上是由CSS,甚至是由JS来控制的。出现在HTML内容前段的内容,在页面呈现时也可以出现在下方,这也和搜索引擎没有任何关系。可惜这一 点也解释了半天。
  3. 最后一条可以说是最可笑的。因为SEO效果不好,那位SEO咨询师觉得只能“来真的”了,于是向我们索要网站的 IIS日志。分析日志对于SEO有些帮助,因为可以看出爬虫的抓取顺序,频率,甚至结果等等,因此查看日志的做法本没有问题。可惜问题在于,对方从MSN 上给出一个邮箱,让我们把过去几个星期的日志发给他。当看到这个要求的时候,老赵几乎要破口大骂。从这点可以看出,这位SEO咨询师缺少必要的常识,他根 本不知道一个中小型的网站,每天便要生成几百兆到几个G的日志。如此没有常识,为什么会有那么多“成功案例”?

老赵的博客 (也就是您正在看的这个)在搜索引擎上的表现也非常糟糕,即使是老赵经常写作的话题,在Google上也很难找到几篇文章,排名也不太靠前。如果不使用 site:cnblogs.com进行限制的话,几乎没有一篇文章是找到我的blog,都是各种地方的转载。为此我也比较苦恼,咨询了一些专业搞SEO的 朋友,做出一些修改之后还是没有太大改善。不过我相信那只是我没有遇上优秀的SEO人员而已,我的博客的潜力还远没有挖掘到底。

如果您是一个专业的SEO人员,或者是专业的SEO公司,不妨给我一些建议——如果可以的话,我也不介意在这方面进行一点投资。不过,如果是一些“肮脏”的优化方式就不必了,例如去论坛上贴链接,发垃圾邮件。我也知道这些做法很有效果,但是我不想这样做。

[转载]Android OpenGL 学习笔记 --开始篇

mikel阅读(946)

[转载]Android OpenGL 学习笔记 –开始篇 – Terry_龙 – 博客园.

1、什么是 OpenGL?

OpenGL 是个专业的3D程序接口,是一个功能强大,调用方便的底层3D图形库。OpenGL 的前身是 SGI 公司为其图形工作站开的 IRIS GL。IRIS GL 是一个工业标准的3D图形软件接口,功能虽然强大但是移植性不好,于是 SGI 公司便在 IRIS GL 的基础上开发 OpenGL 。具体详细的介绍请 点击这里

2、OpenGL 的发展历程

1992年7月 发布了 OpenGL 1.0 版本,并与微软共同推出 Windows NT 版本的 OpenGL

1995年 OpenGL 1.1 版本面市,加入了新功能,并引入了纹理特性等等。

一直到 2009年8月Khronos小组发布了OpenGL 3.2,这是一年以来OpenGL进行的第三次重要升级。

具体特点及功能、 OpenGL 现状、发展历程、OpenGL 规范、编程入门请 点击这里

3、OpenGL  ES 简介

Android 3D 引擎采用的是OpenGL ESOpenGL ES是一套为手持和嵌入式系统设计的3D引擎API,由Khronos公司维护。在PC领域,一直有两种标准的3D API进行竞争,OpenGL DirectX。一般主流的游戏和显卡都支持这两种渲染方式,DirectXWindows平台上有很大的优势,但是 OpenGL 具有更好的跨平台性。

由于嵌入式系统和PC相比,一般说来,CPU、内存等都比PC差很多,而且对能耗有着特殊的要求,许多嵌入式设备并没有浮点运算协处理器,针对嵌入式系统的以上特点,Khronos对标准的 OpenGL 系统进行了维护和改动,以期望满足嵌入式设备对3D绘图的要求。

4、 Android OpenGL ES 简介

Android系统使用 OpenGL 的标准接口来支持3D图形功能,android 3D 图形系统也分为 java 框架和本地代码两部分。本地代码主要实现的 OpenGL 接口的库,在 Java 框架层,javax.microedition.khronos.opengles java 标准的 OpenGL 包,android.opengl包提供了 OpenGL 系统和 Android GUI 系统之间的联系。

5、Android 支持 OpenGL 列表

  • 1、GL
  • 2、GL 10
  • 3、GL 10 EXT
  • 4、GL 11
  • 5、GL 11 EXT
  • 6、GL 11 ExtensionPack

我们将使用 GL10 这个类开始接触 OpenGL ,探索3D 领域。

6、一步一步实现自己的 Renderer 类

在 Android 中我们使用 GLSurfaceView 来显示 OpenGL 视图,该类位于 android.opengl 包里面。它提供了一个专门用于渲染3D 的接口 Renderer 。接下来我们就来一步步构建自己的 Renderer 类。

1、为 Renderer 类赶回命名空间

import android.opengl.GLSurfaceView.Renderer;

2、新建一个类来实现 Renderer 接口,代码如下:

public class ThreeDGl implements Renderer
{
}

3、如上代码所写,程序实现了 Renderer 类,则必须重写以下方法

public void onDrawFrame(GL10 gl)
{
}
public void onSurfaceChanged(GL10 gl, int width, int height)
{}
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{}

4、当窗口被创建时需要调用 onSurfaceCreate ,我们可以在这里对 OpenGL 做一些初始化工作,例如:

               // 启用阴影平滑
        gl.glShadeModel(GL10.GL_SMOOTH);

        // 黑色背景
        gl.glClearColor(0, 0, 0, 0);

        // 设置深度缓存
        gl.glClearDepthf(1.0f);
        // 启用深度测试
        gl.glEnable(GL10.GL_DEPTH_TEST);
        // 所作深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);

        // 告诉系统对透视进行修正
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

glHint 用于告诉 OpenGL 我们希望进行最好的透视修正,这会轻微地影响性能,但会使得透视图更好看。
glClearColor 设置清除屏幕时所用的颜色,色彩值的范围从 0.0f~1.0f 大小从暗到这的过程。
glShadeModel 用于启用阴影平滑度。阴影平滑通过多边形精细地混合色彩,并对外部光进行平滑。
glDepthFunc 为将深度缓存设想为屏幕后面的层,它不断地对物体进入屏幕内部的深度进行跟踪。
glEnable 启用深度测试。
5、当窗口大小发生改变时系统将调用 onSurfaceChange 方法,可以在该方法中设置 OpenGL 场景大小 ,代码如下:

//设置OpenGL场景的大小
gl.glViewport(0, 0, width, height);

6、场景画出来了,接下来我们就要实现场景里面的内容,比如:设置它的透视图,让它有种越远的东西看起来越小的感觉,代码如下:

//设置投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        //重置投影矩阵
        gl.glLoadIdentity();
        // 设置视口的大小
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
        // 选择模型观察矩阵
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        // 重置模型观察矩阵
        gl.glLoadIdentity();

gl.glMatrixMode(GL10.GL_PROJECTION); 指明接下来的代码将影响 projection matrix (投影矩阵),投影矩阵负责为场景增加透视度。
gl.glLoadIdentity(); 此方法相当于我们手机的重置功能,它将所选择的矩阵状态恢复成原始状态,调用 glLoadIdentity(); 之后为场景设置透视图。
gl.glMatrixMode(GL10.GL_MODELVIEW); 指明任何新的变换将会影响 modelview matrix (模型观察矩阵)。
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); 此方法,前面4个参数用于确定窗口的大小,而后面两个参数分别是在场景中所能绘制深度的起点和终点。
7、了解了上面两个重写方法的作用和功能之后,第三个方法 onDrawFrame 从字面上理解就知道此方法做绘制图操作的。嗯,没错。在绘图之前,需要将屏幕清除成前面所指定的颜色,清除尝试缓存并且重置场景,然后就可以绘图了, 代码如下:

// 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置当前的模型观察矩阵
gl.glLoadIdentity();

8、Renderer 类在实现了上面的三个重写之后,在程序入口中只需要调用

Renderer render=new ThreeDGl(this);
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GLSurfaceView gview=new GLSurfaceView(this);
        gview.setRenderer(render);
        setContentView(gview);
    }

到此基本对 OpenGL 有一些了解,当然OpenGL 还有更多的东西需要我们去探索,努力吧。

[转载]Android 播放Gif 动画

mikel阅读(1609)

[转载]Android 播放Gif 动画 – Terry_龙 – 博客园.

Android 中是不支持直接使用Gif 图片关联播放帧动画,如下动画在Android 中是无法播放的:

Android 提供了另外一种解决的办法,就是使用AnimationDrawable 这一函数使其支持逐帧播放,但是如何把gif 图片打散开来,成为每一帧的图片呢?下面介绍两种比较不错的软件,可以帮我们打散图片。

gifsplitter2.0

下载地址:gif分割

使用方法如下:

这一软件分割图片都是bmp图片,图片比较大,这里不推荐使用,尽量节省不必要的字节,所以这里推荐使用如下 软件

easygifanimator

软件下载:动画分割器

使用方法如下:

点击文件将帧文件导出即可

得到了帧文件后我们可以就编写代码,在res目录下新建anim动画文件夹,写下如下代码

<?xml version=”1.0″ encoding=”UTF-8″?>
<animation-list android:oneshot=”false”
xmlns:android
=”http://schemas.android.com/apk/res/android”>
<item android:duration=”150″ android:drawable=”@drawable/xiu0″ />
<item android:duration=”150″ android:drawable=”@drawable/xiu1″ />
<item android:duration=”150″ android:drawable=”@drawable/xiu2″ />
<item android:duration=”150″ android:drawable=”@drawable/xiu3″ />
</animation-list>

对应的item 为顺序的图片从开始到结束,duration为每张逐帧播放间隔,oneshot 为false 代表循环播放,设置为true 即播放一次即停止。

对应Activity 代码如下编写:


import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

public class animActivity extends Activity implements OnClickListener {
ImageView iv
= null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

iv = (ImageView) findViewById(R.id.ImageView01);
iv.setOnClickListener(
this);
}

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
AnimationDrawable anim = null;
Object ob
= iv.getBackground();
anim
= (AnimationDrawable) ob;
anim.stop();
anim.start();
}
}

使用AnimationDrawable 对象获得图片的图片,然后指定这个AnimationDrawable 开始播放动画

Tip:使用此方法不会默认播放,必须要有事件触发才可播放动画,如上面的通过点击监听触发动画的播放

那么如何使用图片自动播放呢?我们可以联想一下,ProgressBar 是不是默认的时候就会转,那就是那个圆形的进度条,是的。我们可以对它进行改造合它也可以自动播放,在Values 文件下新建一个styles 文件,编写如下代码 :

<?xml version=”1.0″ encoding=”UTF-8″?>
<resources>
<style name=”animStyle” parent=”@android:style/Widget.ProgressBar.Large”>
<item name=”android:indeterminateDrawable”>@anim/test</item>
</style>
</resources>

上面样式文件自Widget.ProgressBar.Large 为其设置动画文件,我们在XML中就可以通过设置它的样式使其为我们工作

<ProgressBar android:id=”@+id/ProgressBar01″ style=”@style/animStyle”
android:layout_width
=”128px” android:layout_height=”128px”></ProgressBar>

OK,就是这么简单,下面看看运行效果:

源码下载:Demo