今天在这里分享一个很实用的小技巧。 我们平时在开发应用的时候,经常会用到NSLog来调试我们的程序,而随着项目越来越大,这些用于调试的日志输出就会变得很难管理。 我们在发布正式版的时候一定要屏蔽掉所有后台输出,因为这些输出还是比较消耗系统资源的。  往往到了这个时候,我们不得不去一行一行的找到NSLog调用,然后注释掉。 这样做在项目小的时候还比较有效,但随着项目规模的增长,就会变得越来越难控制。  下面就给大家介绍一个简单的方法,让我们在生成Release版本时不需要进行任何更改即可屏蔽所有的Log输出。

1. 首先我们先要定义这样一段预处理命令,文件名随便起,例如 CLog.h

#ifdef DEBUG
#define CLog(format, ...) NSLog(format, ## __VA_ARGS__)
#else
#define CLog(format, ...)
#endif

  这里我们判断DEBUG这个宏是否定义,如果有定义我们就将这个CLog宏替换成NSLog调用,而如果没有定义过DEBUG标志我们就直接跳过。这点应该不难理解。

2. 检查DEBUG标志是否正确定义,xcode一般会在debug运行配置项里面已经定义号了DEBUG标志,如果没定义我们就自己写上,以我的xcode 4 为例,如下图:

 

  找到PreProcessor Macros 这个属性,对于Debug配置我们给他写上DEBUG,而在Release配置中把它留空。 这样我们刚才那段预处理命令就可以根据这个标志来判断我们编译的时调试版本还是发布版本,从而控制NSLog的输出。 (因为xcode 4 会把debug/release 两个配置项同时对比展现出来,而3.x版本的只能分别设置, 如果你用的时xcode 3.x 开发工具, 那么就分别对Debug/Release 都检查一下)。

3. 到了这里我们这个判断工作就都进行完了,不过这里还有一点比较麻烦,就是我们如果想实用CLog宏,就必须要导入 CLog.h 这个头文件。 不过xcode为我们提供了一种非常巧妙的解决办法。 我们自己看一下项目里的文件,是不是有一个叫做 xxx-prefix.pch 的文件,只要注意到pch这个扩展名就可以了。 这个文件是做什么用的呢? 下面是一个pch文件的样本:

//
// Prefix header for all source files 
//

#import <Availability.h>

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
#endif

  这里引入了一些头文件, 其实是xcode 的一种预编译机制,我们在编译一个项目的时候,会有很多常用的源文件,并且这些代码文件几乎不被修改,所以xcode对这些文件只在早期进行一次编译,以便我们以后的多次构建过程中反复实用。 例如这里的UIKit和Foundation ,这样的机制可以加快我们每次构建项目的速度。 当然这里我们不必太深究它,知道它的作用后,我们就可以利用它来为我们的开发提供便利。 我们只需要将刚刚建立的CLog.h 也在这里面引入一下,这样我们项目中的所有文件就都能够访问到我们刚刚定义的CLog宏了。 下面是完成后的pch文件:

#import <Availability.h>

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import "CLog.h"
#endif

  这样,我们的CLog就完成了,现在可以在任何一个源文件中实用CLog宏来输出日志,预处理命令会自动判断当前的编译配置,如果是Debug,就会输出日志,反之则什么都不会输出。 

posted @ 2011-05-04 23:38 Springfield 阅读(1226) 评论(0) 编辑

  我们在开发的过程中,往往为了美化界面的需要,会修改按钮的默认外观,而因为Android中的按钮有三种状态—默认,被点击,被选中。所以,如果要改变按钮的外观,需要对这三种情况都做出修改,也许在以往,我们最容易想到的就是,手动监听按钮的选中和点击事件,然后写代码来替换按钮的背景,但是在android中,我们不需要这么麻烦,android早就替我们想好了解决方案,那就是selector资源。如果我们要实现按钮的三种背景,只需在res/drawable目录中建立这样一个XML文件

  selector.xml

 

 

<selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_window_focused="false"
        android:drawable="@drawable/t3"/>
        <item android:state_focused="true"
        android:drawable="@drawable/t1"/>
        <item android:state_pressed="true"
        android:drawable="@drawable/t2"/>
        <item android:drawable="@drawable/t3"/>
</selector>

 

 

 

    正如上面的资源文件所描述的,我们在这里定义了三种行为各自的图片资源,接下来,我们只需要在相应的按钮中,将背景资源指定为drawable/selector,就完成我们需要做的一切了~

 

 

<ImageButton         android:layout_width="100px"        android:layout_height="50px"        android:src="@drawable/selector"        />

 

 

     这一切就这么简单,不用写任何的逻辑代码,android开发是不是很方便呢~,下面是效果图:

 

默认:

 

点击:

 

选中:

posted @ 2010-11-09 19:20 Springfield 阅读(1604) 评论(0) 编辑

  今天再来给大家分享一篇我发表在eoeandroid论坛的帖子,原帖地址:

  http://www.eoeandroid.com/thread-32248-1-1.html

 

  我们在平时做开发的时候,免不了会用到各种各样的对话框,相信有过其他平台开发经验的朋友都会知道,大部分的平台都只提供了几个最简单的实现,如果我们想实现自己特定需求的对话框,大家可能首先会想到,通过继承等方式,重写我们自己的对话框。当然,这也是不失为一个不错的解决方式,但是一般的情况却是这样,我们重写的对话框,也许只在一个特定的地方会用到,为了这一次的使用,而去创建一个新类,往往有点杀鸡用牛刀的感觉,甚至会对我们的程序增加不必要的复杂性,对于这种情形的对话框有没有更优雅的解决方案呢? 
    幸运的是,android提供了这种问题的解决方案,刚开始接触android的时候,我在做一个自定义对话框的时候,也是通过继承的方式来实现,后来随着对文档了解的深入,发现了android起始已经提供了相应的接口Dialog Builder ,下面我就吧相关的内容在这里分享一下,也能让更多的初学者少走弯路。

  首先是一个最简单的应用,就是弹出一个消息框,在android中可以这样实现:

 

 

new AlertDialog.Builder(self)   
                .setTitle("标题")
                .setMessage("简单消息框")
                .setPositiveButton("确定", null)
                .show();

 

效果如下:

 

 

上面的代码中我们新建了一个AlertDialog,并用Builder方法形成了一个对象链,通过一系列的设置方法,构造出我们需要的对话框,然后调用show方法显示出来,注意到Builder方法的参数 self,这个其实是Activity对象的引用,根据你所处的上下文来传入相应的引用就可以了。例如在onCreate方法中调用,只需传入this即可。

 

下面是带确认和取消按钮的对话框:

 

 

new AlertDialog.Builder(self) 
.setTitle("确认")
.setMessage("确定吗?")
.setPositiveButton("是", null)
.setNegativeButton("否", null)
.show();

 

 

 

 

 

注意到,这里有两个null参数,这里要放的其实是这两个按钮点击的监听程序,由于我们这里不需要监听这些动作,所以传入null值简单忽略掉,但是实际开发的时候一般都是需要传入监听器的,用来响应用户的操作。
下面是一个可以输入文本的对话框:

 

 

new AlertDialog.Builder(self)
.setTitle("请输入")
.setIcon(android.R.drawable.ic_dialog_info)
.setView(new EditText(self))
.setPositiveButton("确定", null)
.setNegativeButton("取消", null)
.show();

 

如上代码,我们用setView方法,为我们的对话框传入了一个文本编辑框,当然,你可以传入任何的视图对象,比如图片框,WebView等。。尽情发挥你的想象力吧~
下面是单选框与多选框,也是非常有用的两种对话框:
new AlertDialog.Builder(self)
.setTitle("请选择")
.setIcon(android.R.drawable.ic_dialog_info)                
.setSingleChoiceItems(new String[] {"选项1","选项2","选项3","选项4"}, 0, 
  new DialogInterface.OnClickListener() {
                            
     public void onClick(DialogInterface dialog, int which) {
        dialog.dismiss();
     }
  }
)
.setNegativeButton("取消", null)
.show();
                
new AlertDialog.Builder(self)
.setTitle("多选框")
.setMultiChoiceItems(new String[] {"选项1","选项2","选项3","选项4"}, null, null)
.setPositiveButton("确定", null)                
.setNegativeButton("取消", null)
.show();   
单选和多选对话框应该是我们平时用的非常多的,代码应该很好理解,下面再最后介绍两个
列表对话框:
new AlertDialog.Builder(self)
.setTitle("列表框")
.setItems(new String[] {"列表项1","列表项2","列表项3"}, null)
.setNegativeButton("确定", null)
.show();

最后,在对话框中显示图片:
ImageView img = new ImageView(self);
img.setImageResource(R.drawable.icon);

new AlertDialog.Builder(self)
.setTitle("图片框")
.setView(img)
.setPositiveButton("确定", null)
.show();



      我们传入了一个ImageView来显示图片,这里显示了一个经典的android小绿人图标~ ~,当然这里还可以放上网络图片,具体的实现方法就不介绍了,留给大家来练习吧~
      最后总结一下,android平台为我们开发提供了极大的便利,DialogBuilder能做的不止这些,这里给大家展示的只是冰山一角,我们可以尽情的发挥想象,创造我们自己的对话框。
posted @ 2010-11-08 19:30 Springfield 阅读(4299) 评论(16) 编辑

  相信各位android开发者,对SAX已经并不陌生了,SAX(Simple API for XML),是一个使用非常广泛的XML解析标准,通常使用Handler模式来处理XML文档,这种处理模式和我们平常习惯的理解方式很不同,身边也经常有一些朋友在刚接触SAX的时候会觉得理解起来有些困难。其实SAX并不复杂,只不过是换了一种思维方式,正如它的名字所表示的,为了让我们以更简单的方式来处理XML文档,下面我们就开始吧。
      我们通常的理解方式是,我们给出一个输入(比如xml文档的地址),然后程序返回给我们数据(比如解析后的xml文档结构),我们在返回给我们的结果中进行相应的操作,而SAX以一种更简单的方式来处理XML文档的解析,也就是处理器模式,一个使用SAX的简单示例:

 

1 SAXParserFactory spf = SAXParserFactory.newInstance();
2 SAXParser sp = spf.newSAXParser();
3 XMLReader reader = sp.getXMLReader();
4
5
6 reader.setContentHandler(myHandler);
7 reader.parse(new InputSource(new URL(url).openStream()));

 

 

     正如上面的代码,我们使用一系列工厂方法生成了一个XMLReader对象,随后,最关键的一行就是reader.setContentHandler,这里为这个reader设置了一个处理器,这个处理器的具体内容是要我们来完成的,稍后会详细介绍,最后调用parse方法完成文档的解析。这是SAX的一个基本流程。
     下面我们来详细介绍一下处理器,SAX处理器使用的是一种和我们平时的理解方式不太一样的处理形式,是在遍历文档的同时,让我们来进行文档的处理。     用一个实际的例子来解释更为方便,假如有下面这样一个XML文档:

 

1 <student>
2 <name>张三</name>
3 <age>22</age>
4 <sn>1001</sn>
5  </student>
6  <student>
7 <name>李四</name>
8 <age>21</age>
9 <sn>1002</sn>
10  </student>

 

 

      使用SAX的时候,解析器会对XML文档进行深度优先遍历,在遍历的时候,会根据条件调用处理器中的方法,如上面的XML文档,首先会遍历到第一个student的起始节点,这时我们可以在处理器中进行一些需要的处理,随后会分别遍历name,age,sn起始节点和结束节点,以此类推,这样说起来可能还不够直观,下面我们就来看看一个处理器的基本结构:

 

1 public class MyHandler extends DefaultHandler {
2
3 public void startElement(String uri, String localName, String qName,
4 }
5
6 public void endElement(String uri, String localName, String qName)
7 throws SAXException {
8 }
9
10 public void characters(char[] ch, int start, int length)
11 throws SAXException {
12 }
13 }

 

 

      如上面的代码,这里有几个比较重要的方法,startElement是进入到起始节点的时候会调用的方法,例如上面的xml文件,进入到<student>节点时,就会调用startElement方法。     endElement方法,在结束一个节点的时候会调用,例如进入到</student>节点时,该方法会被调用。     characters方法,在进入XML节点的文本节点(TextNode)时会被调用,例如<name>张三</name>,在便利到‘张三’这个文本节点的时候,这个方法会被调用。
     另外还有两个回调方法,分别为startDocument,endDocument,顾名思义,这两个方法为进入文档和离开文档时要调用的方法。

 

     下面我们就来自己写一个处理器来解析上面的XML文档。首先我们需要将每个节点封装成一个实体对象:

 

1 public class Student {
2 private String name;
3
4 private int age;
5
6 private String sn;
7 public String getName() {
8 return name;
9 }
10 public void setName(String name) {
11 this.name = name;
12 }
13 public int getAge() {
14 return age;
15 }
16 public void setAge(int age) {
17 this.age = age;
18 }
19 public String getSn() {
20 return sn;
21 }
22 public void setSn(String sn) {
23 this.sn = sn;
24 }
25
26
27 }
28  

 

     下面再来完成处理器的代码:

 

 

1 public class MyHandler extends DefaultHandler {
2
3 private List<Student> studentList;
4
5 private boolean inStudent = false;
6
7 private boolean studentName = false;
8
9 private boolean studentAge = false;
10
11 private boolean studentSN = false;
12
13 private Student curStudent ;
14
15 public MyHandler() {
16
17 studentList = new ArrayList<Student>();
18 }
19 @Override
20 public void startElement(String uri, String localName, String qName,
21 Attributes attributes) throws SAXException {
22
23 String tagName = localName.length() != 0 ? localName : qName;
24 tagName = tagName.toLowerCase().trim();
25
26 if(tagName.equals("student")) {
27 inStudent = true;
28 curStudent = new Student();
29 }
30
31 if(inStudent) {
32
33 if(tagName.equals("name")) {
34 studentName = true;
35 }else if(tagName.equals("age")) {
36 studentAge = true;
37 }else if(tagName.equals("sn")) {
38 studentSN = true;
39 }
40 }
41
42 }
43
44 @Override
45 public void endElement(String uri, String localName, String qName)
46 throws SAXException {
47
48 String tagName = localName.length() != 0 ? localName : qName;
49 tagName = tagName.toLowerCase().trim();
50
51 if(tagName.equals("student")) {
52 inStudent = true;
53 studentList.add(curStudent);
54 }
55
56 if(inStudent) {
57
58 if(tagName.equals("name")) {
59 studentName = false;
60 }else if(tagName.equals("age")) {
61 studentAge = false;
62 }else if(tagName.equals("sn")) {
63 studentSN = false;
64 }
65 }
66 }
67
68 @Override
69 public void characters(char[] ch, int start, int length)
70 throws SAXException {
71
72 if(studentName) {
73 curStudent.setName(curStudent.getName() + new String(ch,start,length));
74 }else if (studentAge) {
75 curStudent.setAge(Integer.parseInt(new String(ch,start,length)));
76 }else if(studentSN) {
77 curStudent.setSn(curStudent.getSn() + new String(ch, start, length));
78 }
79 }
80 }
81  

 

 

 

 

  如上面的代码,我们使用了一系列的布尔标志变量来保存文档的遍历状态,先从startElement说起,当我们进入到student节点的时候,我们将inStudent状态设置为true,表示我们已经处于student节点之中,同时创建了一个student对象,相应地,在endElement方法中,我们遇到student结束的时候,会把这个对象添加到我们的studentList中,并将inStudent状态设置为false。同样的,在startElement方法中判断instudent状态,如果当前已经处于student节点中,并且遍历到name,age或者sn节点时,我们也将相应的标志设置为true。这样在遍历的文本节点的时候就可以在characters方法中通过判断这些标志位来为Student对象设置相应的属性。
  注意到,这里curStudent.setName(curStudent.getName() + new String(ch,start,length)),我们用以前的值和新的值连接起来,而不是直接设置curStudent.setName(new String(ch,start,length))。这是因为在遍历<name>.....</name>这中间的文本节点的时候,有些时候这对标签中的内容可能会被看做多个文本节点,比如包含Html实体的情况下 <name>张 三</name>,这里相当于包含了两个文本节点,如果不使用连接的方式而采用直接设置的方式,那么我们最终只能得到最后一次设置的值,因为前面设置的被覆盖了。那么我们最终取得到的名字就是‘三’了。
    这个处理器的核心分功能就算完成了,下面我们还需要增加一个方法,用来返回处理后的内容:

 

 

1 public List<Student> getStudentList() {
2 return studentList;
3 }

 

 

 

   完成了处理器之后,我们就可以用刚开始介绍的方法来解析XML文档了:

 

 

1 SAXParserFactory spf = SAXParserFactory.newInstance();
2 SAXParser sp = spf.newSAXParser();
3 XMLReader reader = sp.getXMLReader();
4
5 List<Student> list;
6 reader.setContentHandler(myHandler);
7 reader.parse(new InputSource(new URL(url).openStream()));
8
9 list = myHandler.getStudentList();

 

 

 

     可以看到,解析完XML文档之后,我们就可以用处理器重的getStudentList方法取得解析后的数据了。
     最后总结一下,SAX并不复杂,只要理解了它的思维方式,我们就可以游刃有余,使它成为我们开发的利器,这篇文章向大家介绍了SAX的一些基本知识,希望能起到一个抛砖引玉的作用,大家能够使用它来创造出更多好的应用,当然可能有一些地方解释的还不是十分完美,如果有一些不好理解的地方,还望大家指出。:lol 
     另外下面是SAX的一个官方网站,里面有一些介绍和代码示例,英文不错的童鞋可以来这里参考一下:

 

  http://www.saxproject.org

posted @ 2010-11-07 18:11 Springfield 阅读(1432) 评论(1) 编辑

  

  这篇文章是我发表在eoeandroid论坛上的帖子,今天也来这里和园友们分享一下,希望对大家有所帮助~  原帖子地址:

 

       http://www.eoeandroid.com/thread-34700-1-1.html

 

  大家平时在开发的时候,应该经常会有这样的需求,向用户提示一些信息,比如程序的状态,或者完成某个操作,而不希望打搅用户当前正在进行的操作——不获取任何焦点。我们一般很容易想到的一个方法就是使用类似对话框的机制,但即使是非模态的对话框,也会打扰用户当前的操作,那么有没有更简单的方法呢?

 

      答案当然是肯定的啦,android已经为我们提供了一个非常简便的方法,那就是Toast,直译为吐司~ 很可爱的名字吧~ 用起来像它的名字一样,非常方便。例如我们要想用户显示一些文本信息,可以使用下面的方法~

 

Toast.makeText(self, "Hello World!", Toast.LENGTH_SHORT).show();

 

 

非常简单的一句话,第一个参数self是当前上下文Context对象,接着是我们要显示的消息文字,最后一个参数是显示的时间,可以在这两个值里面取其一:

       Toast.LENGTH_SHORT
       Toast.LENGTH_LONG

下面为运行效果 

 

 

 

 

大家看看是不是很熟悉呢,很多应用中都可以看到类似的提示信息,这条信息不会影响用户任何正在进行的操作,当时间到了之后,他会自动消失。

那么Toast是不是除了显示文本消息,就没有其他功能了呢?当然不是了,Toast远不只是显示文本这么简单,我们可以定制它的界面,只需使用它的setView传入任何符合View规范的对象,就可以演变出各种口味的吐司~~

例如,我们的手机上都有调节音量的按钮,其实这个功能的界面就是一个Toast,例如下面这幅图:

 


 

 我们每点击Increase Volume 这个按钮一次,这个进度条就会浮现出来,并且增加一格,如果几秒内没有任何操作,这个进度条就会消失,直到我们再次点击Increase Volume按钮。这个操作就和我们平时调节音量的两个按钮非常像了,下面是主干代码:


我们首先需要定义一个Layout,用来显示Toast中的进度条和提示文字:

 

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout
 3   xmlns:android="http://schemas.android.com/apk/res/android"
 4   android:layout_width="280dp"
 5   android:layout_height="wrap_content"
 6   android:gravity="center_horizontal"
 7   android:orientation="vertical"
 8   
 9   >
10           <TextView           
11                   android:layout_width="fill_parent"
12                   android:layout_height="wrap_content"
13                   android:text="Volume"
14           />
15           <ProgressBar 
16                   android:id="@+id/progress"
17                   android:layout_width="280dp"
18                   android:layout_height="wrap_content"
19                   android:progress="50"
20                   android:max="100"
21                   style="?android:attr/progressBarStyleHorizontal"
22           />
23 </LinearLayout>

 

 

随后,我们就可以在按钮的点击事件里面添加下面的代码:

 

 1 ((ProgressBar)volumeView.findViewById(R.id.progress)).setProgress(
 2   ((ProgressBar)volumeView.findViewById(R.id.progress)).getProgress() + 5        
 3 );
 4                 
 5                 
 6 if(volumeToast == null ) {
 7    volumeToast = new Toast(self);
 8    volumeToast.setGravity(Gravity.TOP, 0150);
 9    volumeToast.setView(volumeView);
10    volumeToast.setDuration(Toast.LENGTH_LONG);
11 }
12                 
13 volumeToast.show();

 

 

 上面这段代码也非常简单吧~虽然实现起来很容易,但却是很实用的功能,我们还可以利用Toast组合中各种新奇的好东西~ 下面就由大家来尽情的发挥想象吧~

 

    源代码下载

 

posted @ 2010-11-06 20:47 Springfield 阅读(1229) 评论(1) 编辑
摘要: 大家在使用JavaScript的时候,DOM操作是最平常不过的了, 随着Web前端技术的发展,我们越来越多的使用JS来操作DOM元素,比如通过ajax请求获取到数据,然后更新页面上的元素,一般情况下,这种操作我们会用类似node.appendChild()这中方式来完成。这个方法是无缓冲的,也就是说我们每次调用appendChild方法的时候,浏览器都会重新渲染页面。当然,使用这种方法也没有什么不...阅读全文
posted @ 2010-06-27 10:38 Springfield 阅读(1769) 评论(8) 编辑
摘要: 上篇文章中我们介绍了Trie树结构,并使用Trie树解决了一个算法问题,具体问题请参看这里:    http://www.cnblogs.com/springfield/archive/2010/06/16/1758450.html  这篇文章中我们来介绍这个算法的具体实现:    首先我们要定义Trie树的节点:  [代码]    在定义好节点之后,我们需要初始化该节点,所以下面的初始化函数用来...阅读全文
posted @ 2010-06-20 19:53 Springfield 阅读(1620) 评论(2) 编辑
摘要: 首先给大家看一下题目,题目出自 http://www.bianchengla.com/  原文如下:     Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another.     Let’s say the phone ...阅读全文
posted @ 2010-06-16 20:21 Springfield 阅读(1777) 评论(1) 编辑
摘要: 随着JQuery的流行, 出现了越来越多的JQuery插件,这些插件中JQuery的基础之上提供了更多定制化的功能,相信在我们平时做JS开发的时候,很多时候都会用到对话框,而最简单的办法就是调用浏览器自带的alert函数。使用这种方式虽然非常方便,但它呈现出的界面非常单一,不美观。所以现在我们看到很多网站上都有一些自己实现对话框的方法,使得用户体验大大提升。当然我们也不必自己去写一个这样的东西,现...阅读全文
posted @ 2010-05-27 22:53 Springfield 阅读(6783) 评论(9) 编辑
摘要: 题目来源:http://www.bianchengla.com/problem/1000题目如下:描述 计算 a+b.输入 本题有多组测试数据。第一行为测试数据的个数。每组数据有两个整数。范围在 -231 ~ +231-1 之内。输出 对每组数据,输出对应的a+b的和。范围在 -231 ~ +231-1 之内。样例输入 21 23 1样例输出 34 这是一道比较简单的算法题,只是读取出总记录条...阅读全文
posted @ 2010-05-26 00:45 Springfield 阅读(1450) 评论(13) 编辑