3 GAndroid Symbian Windows Mobile RIM Black Berry
3 G应用开发之Android
智能手机软件平台有: Symbian, Windows Mobile, RIM Black. Berry, Android, i. Phone, Java/J 2 ME。 2009年市场份额: Symbian 51% RIM Black. Berry 18% i. Phone 13. 3 windows Mobile 9. 3% linux 4. 6% Android 1. 8%
开发第一个Android应用 在项目上右键点击run as Android application,如下图:
电话拔号器 因为应用要使用手机的电话服务,所以要在清单文件 Android. Manifest. xml中添加电话服务权限: <? xml version="1. 0" encoding="utf-8"? > <manifest xmlns: android="http: //schemas. android. com/apk/res/android" package="cn. itcast. action" android: version. Code="1" android: version. Name="1. 0"> 略. . <uses-sdk android: min. Sdk. Version=“ 6" /> <uses-permission android: name="android. permission. CALL_PHONE"/> </manifest>
电话拔号器 界面布局: <? xml version="1. 0" encoding="utf-8"? > <Linear. Layout xmlns: android="http: //schemas. android. com/apk/res/android" android: orientation="vertical" android: layout_width="fill_parent" android: layout_height="fill_parent" > <Text. View android: layout_width="fill_parent" android: layout_height="wrap_content" android: text="@string/inputmobile"/> <Edit. Text android: layout_width="fill_parent" android: layout_height="wrap_content" android: id="@+id/mobile"/> <Button android: layout_width="wrap_content" android: layout_height="wrap_content" android: text="@string/button" android: id="@+id/button"/> </Linear. Layout> Linear. Layout (线性布局)、Absolute. Layout(绝对布局)、Relative. Layout(相对布局)、Table. Layout( 表格布局)、Frame. Layout(帧布局)
电话拔号器 Activity: public class Dialer. Action extends Activity { @Override public void on. Create(Bundle saved. Instance. State) { super. on. Create(saved. Instance. State); set. Content. View(R. layout. main); Button button = (Button)find. View. By. Id(R. id. button); button. set. On. Click. Listener(new View. On. Click. Listener(){ public void on. Click(View v) { Edit. Text edit. Text = (Edit. Text)find. View. By. Id(R. id. mobile); Intent intent = new Intent(Intent. ACTION_CALL, Uri. parse("tel: "+ edit. Text. get. Text())); Dialer. Action. this. start. Activity(intent); } }
短信发送器 因为应用要使用手机的短信服务,所以要在清单文件 Android. Manifest. xml中添加短信服务权限: <? xml version="1. 0" encoding="utf-8"? > <manifest xmlns: android="http: //schemas. android. com/apk/res/android" package="cn. itcast. sms" android: version. Code="1" android: version. Name="1. 0"> 略. . <uses-sdk android: min. Sdk. Version=“ 4" /> <uses-permission android: name="android. permission. SEND_SMS"/> </manifest>
短信发送器 界面布局: <? xml version="1. 0" encoding="utf-8"? > <Linear. Layout xmlns: android="http: //schemas. android. com/apk/res/android" android: orientation="vertical“ android: layout_width="fill_parent“ android: layout_height="fill_parent" > <Text. View android: layout_width="fill_parent" android: layout_height="wrap_content" android: text="@string/inputmobile"/> <Edit. Text android: layout_width="fill_parent" android: layout_height="wrap_content" android: id="@+id/mobile"/> <Text. View android: layout_width="fill_parent" android: layout_height="wrap_content" android: text="@string/content"/> <Edit. Text android: layout_width="fill_parent" android: layout_height="wrap_content" android: min. Lines="3" android: id="@+id/content"/> <Button android: layout_width="wrap_content" android: layout_height="wrap_content" android: text="@string/button" android: id="@+id/button"/> </Linear. Layout>
短信发送器 Activity主要代码: Intent(), 0); String mobile = mobile. View. get. Text(). to. String(); String content = content. View. get. Text(). to. String(); Sms. Manager sms. Manager = Sms. Manager. get. Default(); Pending. Intent sent. Intent = Pending. Intent. get. Broadcast(SMSSender. this, 0, new //如果字数超过70, 需拆分成多条短信发送 List<String> msgs = sms. Manager. divide. Message(content); for(String msg : msgs){ sms. Manager. send. Text. Message(mobile, null, msg, sent. Intent, null); //最后二个参数为短信已发送的广播意图, 最后一个参数为短信对方已收到短信的广播意图 } Toast. make. Text(SMSSender. this, "短信发送完成", Toast. LENGTH_LONG). show();
发送彩信 可以通过调用系统自带的短信程序发送彩信: Intent intent = new Intent(Intent. ACTION_SEND); intent. put. Extra(Intent. EXTRA_STREAM, Uri. parse("file: ///sdcard/cong. png")); intent. put. Extra("address", “ 13677789999”); intent. put. Extra("exit_on_sent", true); intent. put. Extra("subject", "it's subject"); intent. put. Extra("sms_body", "it's content"); intent. set. Type(“image/jpeg”); // 视频:video/mpeg* , 文本:text/plain
对应用进行单元测试 在实际开发中,开发android软件的过程需要不断地进行测试。而使用Junit测试框架,侧是正规 Android开发的必用技术,在Junit中可以得到组件,可以模拟发送事件和检测程序处理的正确性。 第一步:首先在Android. Manifest. xml中加入下面红色代码: <manifest xmlns: android="http: //schemas. android. com/apk/res/android" package="cn. itcast. action“ android: version. Code="1“ android: version. Name="1. 0"> <application android: icon="@drawable/icon" android: label="@string/app_name"> <uses-library android: name="android. test. runner" />. . </application> <uses-sdk android: min. Sdk. Version="6" /> <instrumentation android: name="android. test. Instrumentation. Test. Runner" android: target. Package="cn. itcast. action" android: label="Tests for My App" /> </manifest> 上面target. Package指定的包要和应用的package相同。 第二步:编写单元测试代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” ): import android. test. Android. Test. Case; import android. util. Log; public class XMLTest extends Android. Test. Case { public void test. Something() throws Throwable { Assert. assert. True(1 + 1 == 3); } }
使用文件进行数据存储 首先给大家介绍使用文件如何对数据进行存储,Activity提供了open. File. Output()方法可以用于把数 据输出到文件中,具体的实现过程与在J 2 SE环境中保存数据到文件中是一样的。 public class File. Activity extends Activity { @Override public void on. Create(Bundle saved. Instance. State) {. . . File. Output. Stream out. Stream = this. open. File. Output("itcast. txt", Context. MODE_PRIVATE); out. Stream. write("传智播客". get. Bytes()); out. Stream. close(); } } open. File. Output()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在, Android 会自动创建它。创建的文件保存在/data/<package name>/files目录,如: /data/cn. itcast. action/files/itcast. txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”, 在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开 /data/<package name>/files目录就可以看到该文件。 open. File. Output()方法的第二参数用于指定操作模式,有四种模式,分别为: Context. MODE_PRIVATE = 0 Context. MODE_APPEND = 32768 Context. MODE_WORLD_READABLE = 1 Context. MODE_WORLD_WRITEABLE = 2
读取文件内容 如果要打开存放在/data/<package name>/files目录应用私有的文件,可以使用Activity提供 open. File. Input()方法。 File. Input. Stream in. Stream = this. get. Context(). open. File. Input("itcast. txt"); Log. i("File. Test", read. In. Stream(in. Stream)); read. In. Stream()的方法请看本页下面备注。 或者直接使用文件的绝对路径: File file = new File("/data/cn. itcast. action/files/itcast. txt"); File. Input. Stream in. Stream = new File. Input. Stream(file); Log. i("File. Test", read. In. Stream(in. Stream)); 注意:上面文件路径中的“cn. itcast. action”为应用所在包,当你在编写代码时应替换为你自己应用 使用的包。 对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文 件时,指定Context. MODE_WORLD_READABLE和Context. MODE_WORLD_WRITEABLE权限。 Activity还提供了get. Cache. Dir()和get. Files. Dir()方法: get. Cache. Dir()方法用于获取/data/<package name>/cache目录 get. Files. Dir()方法用于获取/data/<package name>/files目录
把文件存放在SDCard 要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。 注意:访问SDCard必须在Android. Manifest. xml中加入访问SDCard的权限 if(Environment. get. External. Storage. State(). equals(Environment. MEDIA_MOUNTED)){ File sd. Card. Dir = Environment. get. External. Storage. Directory(); //获取SDCard目录 File save. File = new File(sd. Card. Dir, “itcast. txt”); File. Output. Stream out. Stream = new File. Output. Stream(save. File); out. Stream. write("传智播客". get. Bytes()); out. Stream. close(); } Environment. get. External. Storage. State()方法用于获取SDCard的状态,如果手机装有SDCard,并 且可以进行读写,那么方法返回的状态等于Environment. MEDIA_MOUNTED。 Environment. get. External. Storage. Directory()方法用于获取SDCard的目录,当然要获取SDCard的 目录,你也可以这样写: File sd. Card. Dir = new File("/mnt/sdcard"); //获取SDCard目录 File save. File = new File(sd. Card. Dir, "itcast. txt"); //上面两句代码可以合成一句: File save. File = new File("/mnt/sdcard/itcast. txt"); File. Output. Stream out. Stream = new File. Output. Stream(save. File); out. Stream. write("传智播客test". get. Bytes()); out. Stream. close();
使用SAX或者DOM或者pull解析XML文 件 在Android平台上可以使用Simple API for XML(SAX) 、 Document Object Model(DOM)和Android附带的 pull解析器解析XML文件。 下面是本例子要解析的XML文件: 文件名称:itcast. xml <? xml version="1. 0" encoding="UTF-8"? > <persons> <person id="23"> <name>liming</name> <age>30</age> </person> <person id="20"> <name>zhangxiao</name> <age>25</age> </persons> 例子定义了一个javabean用于存放上面解析出来的xml内容, 这个javabean为Person,代码请见本页下面 备注:
使用SAX读取XML文件 只要为SAX提供实现Content. Handler接口的类,那么该类就可以得到通知事件(实际上就是SAX调用了该 类中的回调方法)。因为Content. Handler是一个接口,在使用的时候可能会有些不方便,因此,SAX还为 其制定了一个Helper类:Default. Handler,它实现了Content. Handler接口,但是其所有的方法体都为空,在 实现的时候,你只需要继承这个类,然后重写相应的方法即可。使用SAX解析itcast. xml的代码如下: public static List<Person> read. XML(Input. Stream in. Stream) { try { SAXParser. Factory spf = SAXParser. Factory. new. Instance(); SAXParser sax. Parser = spf. new. SAXParser(); //创建解析器 //设置解析器的相关特性,http: //xml. org/sax/features/namespaces = true 表示开启命名空间特性 //sax. Parser. set. Property("http: //xml. org/sax/features/namespaces", true); XMLContent. Handler handler = new XMLContent. Handler(); sax. Parser. parse(in. Stream, handler); in. Stream. close(); return handler. get. Persons(); } catch (Exception e) { e. print. Stack. Trace(); } return null; } SAX 支持已内置到JDK 1. 5中,你无需添加任何的jar文件。关于XMLContent. Handler的代码实现请看本页下 面备注。
使用Pull解析器生成XML文件 有些时候,我们需要生成一个XML文件,生成XML文件的方法有很多,如:可以只使用一个String. Builder 组拼XML内容,然后把内容写入到文件中;或者使用DOM API生成XML文件,或者也可以使用pull解析器 生成XML文件,这里推荐大家使用Pull解析器。 使用Pull解析器生成一个与itcast. xml文件内容相同的myitcast. xml文件,代码在本页下方备注 使用代码如下(生成XML文件): File xml. File = new File("myitcast. xml"); File. Output. Stream out. Stream = new File. Output. Stream(xml. File); Output. Stream. Writer out. Stream. Writer = new Output. Stream. Writer(out. Stream, "UTF-8"); Buffered. Writer writer = new Buffered. Writer(out. Stream. Writer); write. XML(persons, writer); writer. flush(); writer. close(); 如果只想得到生成的xml字符串内容,可以使用String. Writer: String. Writer writer = new String. Writer(); write. XML(persons, writer); String content = writer. to. String();
访问Shared. Preferences中的数据代码如下: Shared. Preferences shared. Preferences = get. Shared. Preferences("itcast", Context. MODE_PRIVATE); //get. String()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值 String name = shared. Preferences. get. String("name", ""); int age = shared. Preferences. get. Int("age", 1); 如果访问其他应用中的Preference,前提条件是:该preference创建时指定了 Context. MODE_WORLD_READABLE或者Context. MODE_WORLD_WRITEABLE权限。如:有个 <package name>为cn. itcast. action的应用使用下面语句创建了preference。 get. Shared. Preferences("itcast", Context. MODE_WORLD_READABLE); 其他应用要访问上面应用的preference,首先需要创建上面应用的Context,然后通过Context 访问 preference ,访问preference时会在应用所在包下的shared_prefs目录找到preference : Context other. Apps. Context = create. Package. Context("cn. itcast. action", Context. CONTEXT_IGNORE_SECURITY); Shared. Preferences shared. Preferences = other. Apps. Context. get. Shared. Preferences("itcast", Context. MODE_WORLD_READABLE); String name = shared. Preferences. get. String("name", ""); int age = shared. Preferences. get. Int("age", 0); 如果不通过创建Context访问其他应用的preference,也可以以读取xml文件方式直接访问其他应用preference对应的xml 文件,如: File xml. File = new File(“/data/<package name>/shared_prefs/itcast. xml”); //<package name>应替换成应用的包名
使用SQLite. Open. Helper对数据库进行版 本管理 public class Database. Helper extends SQLite. Open. Helper { //类没有实例化, 是不能用作父类构造器的参数, 必须声明为静态 private static final String name = "itcast"; //数据库名称 private static final int version = 1; //数据库版本 public Database. Helper(Context context) { //第三个参数Cursor. Factory指定在执行查询时获得一个游标实例的 厂类, 设置为null, 代表使用系统默认的 厂类 super(context, name, null, version); } @Override public void on. Create(SQLite. Database db) { db. exec. SQL("CREATE TABLE IF NOT EXISTS person (personid integer primary key autoincrement, name varchar(20), age INTEGER)"); } @Override public void on. Upgrade(SQLite. Database db, int old. Version, int new. Version) { db. exec. SQL(" ALTER TABLE person ADD phone VARCHAR(12) NULL "); //往表中增加一列 // DROP TABLE IF EXISTS person 删除表 } } 在实际项目开发中,当数据库表结构发生更新时,应该避免用户存放于数据库中的数据丢失。
使用SQLite. Database操作SQLite数据 库 delete()方法的使用: SQLite. Database db = database. Helper. get. Writable. Database(); db. delete("person", "personid<? ", new String[]{"2"}); db. close(); 上面代码用于从person表中删除personid小于2的记录。 update()方法的使用: SQLite. Database db = database. Helper. get. Writable. Database(); Content. Values values = new Content. Values(); values. put(“name”, “传智播客”); //key为字段名,value为值 db. update("person", values, "personid=? ", new String[]{"1"}); db. close(); 上面代码用于把person表中personid等于1的记录的name字段的值改为“传智播客”。
使用SQLite. Open. Helper获取用于操作数据库的 SQLite. Database实例 public class Database. Helper extends SQLite. Open. Helper { private static final String name = "itcast"; //数据库名称 private static final int version = 1; //数据库版本. . . 略 } public class Hello. Activity extends Activity { @Override public void on. Create(Bundle saved. Instance. State) {. . . Button button =(Button) this. find. View. By. Id(R. id. button); button. set. On. Click. Listener(new View. On. Click. Listener(){ public void on. Click(View v) { Database. Helper database. Helper = new Database. Helper(Hello. Activity. this); SQLite. Database db = database. Helper. get. Writable. Database(); db. exec. SQL("insert into person(name, age) values(? , ? )", new Object[]{"传智播客", 4}); db. close(); }}); } } 第一次调用get. Writable. Database()或get. Readable. Database()方法后,SQLite. Open. Helper会缓存当前的 SQLite. Database实例,SQLite. Database实例正常情况下会维持数据库的打开状态,所以在你不再需要 SQLite. Database实例时,请及时调用close()方法释放资源。一旦SQLite. Database实例被缓存,多次调用 get. Writable. Database()或get. Readable. Database()方法得到的都是同一实例。
使用事务操作SQLite数据库 使用SQLite. Database的begin. Transaction()方法可以开启一个事务,程序执行到end. Transaction() 方法时会 检查事务的标志是否为成功,如果程序执行到end. Transaction()之前调用了set. Transaction. Successful() 方 法设置事务的标志为成功则提交事务,如果没有调用set. Transaction. Successful() 方法则回滚事务。使用例 子如下: SQLite. Database db =. . ; db. begin. Transaction(); //开始事务 try { db. exec. SQL("insert into person(name, age) values(? , ? )", new Object[]{"传智播客", 4}); db. exec. SQL("update person set name=? where personid=? ", new Object[]{"传智", 1}); db. set. Transaction. Successful(); //调用此方法会在执行到end. Transaction() 时提交当前事务,如果不调用此方法会回滚事务 } finally { db. end. Transaction(); //由事务的标志决定是提交事务,还是回滚事务 } db. close(); 上面两条SQL语句在同一个事务中执行。
使用Content. Provider(内容提供者)共 享数据 Content. Provider 在android中的作用是对外共享数据,也就是说你可以通过Content. Provider把应用中的数据共享给其他应用访 问,其他应用可以通过Content. Provider 对你应用中的数据进行添删改查。关于数据共享,以前我们学习过文件操作模式,知 道通过指定文件的操作模式为Context. MODE_WORLD_READABLE 或Context. MODE_WORLD_WRITEABLE同样也可以对 外共享数据。那么,这里为何要使用Content. Provider 对外共享数据呢?是这样的,如果采用文件操作模式对外共享数据,数 据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,如:采用xml文件对外共享数据,需要进行xml解 析才能读取数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。 使用Content. Provider对外共享数据的好处是统一了数据的访问方式。 当应用需要通过Content. Provider对外共享数据时,第一步需要继承Content. Provider并重写下面方法: public class Person. Content. Provider extends Content. Provider{ public boolean on. Create() public Uri insert(Uri uri, Content. Values values) public int delete(Uri uri, String selection, String[] selection. Args) public int update(Uri uri, Content. Values values, String selection, String[] selection. Args) public Cursor query(Uri uri, String[] projection, String selection, String[] selection. Args, String sort. Order) public String get. Type(Uri uri)} 第二步需要在Android. Manifest. xml使用<provider>对该Content. Provider进行配置,为了能让其他应用找到该Content. Provider , Content. Provider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 Content. Provider看作是一个网站(想想,网 站也是提供数据者),authorities 就是他的域名: <manifest. . > <application android: icon="@drawable/icon" android: label="@string/app_name"> <provider android: name=". Person. Content. Provider" android: authorities="cn. itcast. providers. personprovider"/> </application> </manifest>
Uri. Matcher类使用介绍 因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个 用于操作Uri的 具类,分别为Uri. Matcher 和Content. Uris 。掌握它们的使用,会便于我们的开发 作。 Uri. Matcher类用于匹配Uri,它的用法如下: 首先第一步把你需要匹配Uri路径全部给注册上,如下: //常量Uri. Matcher. NO_MATCH表示不匹配任何路径的返回码 Uri. Matcher s. Matcher = new Uri. Matcher(Uri. Matcher. NO_MATCH); //如果match()方法匹配content: //cn. itcast. provider. personprovider/person路径,返回匹配码为 1 s. Matcher. add. URI(“cn. itcast. provider. personprovider”, “person”, 1); //添加需要匹配uri,如果匹配就会返回匹配码 //如果match()方法匹配content: //cn. itcast. provider. personprovider/person/230路径,返回匹配码为 2 s. Matcher. add. URI(“cn. itcast. provider. personprovider”, “person/#”, 2); //#号为通配符 switch (s. Matcher. match(Uri. parse("content: //cn. itcast. provider. personprovider/person/10"))) { case 1 break; case 2 break; default: //不匹配 break; } 注册完需要匹配的Uri后,就可以使用s. Matcher. match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹 配码,匹配码是调用add. URI()方法传入的第三个参数,假设匹配 content: //cn. itcast. provider. personprovider/person路径,返回的匹配码为 1
Content. Uris类使用介绍 Content. Uris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法: with. Appended. Id(uri, id)用于为路径加上ID部分: Uri uri = Uri. parse("content: //cn. itcast. provider. personprovider/person") Uri result. Uri = Content. Uris. with. Appended. Id(uri, 10); //生成后的Uri为:content: //cn. itcast. provider. personprovider/person/10 parse. Id(uri)方法用于从路径中获取ID部分: Uri uri = Uri. parse("content: //cn. itcast. provider. personprovider/person/10") long personid = Content. Uris. parse. Id(uri); //获取的结果为: 10
使用Content. Provider共享数据 Content. Provider类主要方法的作用: public boolean on. Create() 该方法在Content. Provider创建后就会被调用, Android开机后, Content. Provider在其它应用第一次访问 它时才会被创建。 public Uri insert(Uri uri, Content. Values values) 该方法用于供外部应用往Content. Provider添加数据。 public int delete(Uri uri, String selection, String[] selection. Args) 该方法用于供外部应用从Content. Provider删除数据。 public int update(Uri uri, Content. Values values, String selection, String[] selection. Args) 该方法用于供外部应用更新Content. Provider中的数据。 public Cursor query(Uri uri, String[] projection, String selection, String[] selection. Args, String sort. Order) 该方法用于供外部应用从Content. Provider中获取数据。 public String get. Type(Uri uri) 该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串 应该以vnd. android. cursor. dir/开头,例如:要得到所有person记录的Uri为 content: //cn. itcast. provider. personprovider/person,那么返回的MIME类型字符串应该为: “vnd. android. cursor. dir/person”。如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以 vnd. android. cursor. item/开头,例如:得到id为 10的person记录,Uri为 content: //cn. itcast. provider. personprovider/person/10,那么返回的MIME类型字符串应该为: “vnd. android. cursor. item/person”。
使用Content. Resolver操作Content. Provider中 的数据 当外部应用需要对Content. Provider中的数据进行添加、删除、修改和查询操作时,可以使用 Content. Resolver 类来完成,要获取Content. Resolver 对象,可以使用Activity提供的get. Content. Resolver()方 法。 Content. Resolver 类提供了与Content. Provider类相同签名的四个方法: public Uri insert(Uri uri, Content. Values values) 该方法用于往Content. Provider添加数据。 public int delete(Uri uri, String selection, String[] selection. Args) 该方法用于从Content. Provider删除数据。 public int update(Uri uri, Content. Values values, String selection, String[] selection. Args) 该方法用于更新Content. Provider中的数据。 public Cursor query(Uri uri, String[] projection, String selection, String[] selection. Args, String sort. Order) 该方法用于从Content. Provider中获取数据。 这些方法的第一个参数为Uri,代表要操作的Content. Provider和对其中的什么数据进行操作,假设给定的是: Uri. parse(“content: //cn. itcast. providers. personprovider/person/10”),那么将会对主机名为 cn. itcast. providers. personprovider的Content. Provider进行操作,操作的数据为person表中id为 10的记录。
使用Content. Resolver操作Content. Provider中 的数据 使用Content. Resolver对Content. Provider中的数据进行添加、删除、修改和查询操作: Content. Resolver resolver = get. Content. Resolver(); Uri uri = Uri. parse("content: //cn. itcast. provider. personprovider/person"); //添加一条记录 Content. Values values = new Content. Values(); values. put("name", "itcast"); values. put("age", 25); resolver. insert(uri, values); //获取person表中所有记录 Cursor cursor = resolver. query(uri, null, "personid desc"); while(cursor. move. To. Next()){ Log. i("Content. Test", "personid="+ cursor. get. Int(0)+ ", name="+ cursor. get. String(1)); } //把id为 1的记录的name字段值更改新为liming Content. Values update. Values = new Content. Values(); update. Values. put("name", "liming"); Uri update. Id. Uri = Content. Uris. with. Appended. Id(uri, 2); resolver. update(update. Id. Uri, update. Values, null); //删除id为 2的记录 Uri delete. Id. Uri = Content. Uris. with. Appended. Id(uri, 2); resolver. delete(delete. Id. Uri, null);
监听Content. Provider中数据的变化 如果Content. Provider的访问者需要知道Content. Provider中的数据发生了变化,可以在Content. Provider 发生数据变化时调用get. Content. Resolver(). notify. Change(uri, null)来通知注册在此URI上的访问者,例 子如下: public class Person. Content. Provider extends Content. Provider { public Uri insert(Uri uri, Content. Values values) { db. insert("person", "personid", values); get. Context(). get. Content. Resolver(). notify. Change(uri, null); } } 如果Content. Provider的访问者需要得到数据变化通知,必须使用Content. Observer对数据(数据采用uri 描述)进行监听,当监听到数据变化通知时,系统就会调用Content. Observer的on. Change()方法: get. Content. Resolver(). register. Content. Observer(Uri. parse("content: //cn. itcast. providers. personprovider/person"), true, new Person. Observer(new Handler())); public class Person. Observer extends Content. Observer{ public Person. Observer(Handler handler) { super(handler); } public void on. Change(boolean self. Change) { //此处可以进行相应的业务处理 } }
窃听用户发出的短信 用户使用系统自带的短信程序发送短信,程序会把短信通过Content. Provider保存进数据库,并且发出一 个数据变化通知,只要使用Content. Observer对数据变化进行监听,在用户发送短信时,就会被 Content. Observer窃听到: 注册监听: get. Content. Resolver(). register. Content. Observer(Uri. parse("content: //sms"), true, new Sms. Observer(new Handler())); 监听类: private final class Sms. Observer extends Content. Observer{ public Sms. Observer(Handler handler) { super(handler); } public void on. Change(boolean self. Change) {//查询发送箱中的短信(处于正在发送状态的短信放在发送箱) Cursor cursor = get. Content. Resolver(). query(Uri. parse("content: //sms/outbox"), null, null); while(cursor. move. To. Next()){ String. Builder sb = new String. Builder(); sb. append("_id="). append(cursor. get. Int(cursor. get. Column. Index("_id"))); sb. append(", address="). append(cursor. get. String(cursor. get. Column. Index("address"))); sb. append("; body="). append(cursor. get. String(cursor. get. Column. Index("body"))); sb. append("; time="). append(cursor. get. Long(cursor. get. Column. Index("date"))); Log. i("Receive. Send. SMS", sb. to. String()); }} }
通信录操作 使用Content. Resolver对通信录中的数据进行添加、删除、修改和查询操作: 加入读写联系人信息的权限 <uses-permission android: name="android. permission. READ_CONTACTS" /> <uses-permission android: name="android. permission. WRITE_CONTACTS" /> 添加与查询代码请见ppt下方
从Internet获取数据 利用Http. URLConnection对象, 我们可以从网络中获取网页数据. URL url = new URL("http: //www. sohu. com"); Http. URLConnection conn = (Http. URLConnection) url. open. Connection(); conn. set. Connect. Timeout(5* 1000); //设置连接超时 conn. set. Request. Method(“GET”); //以get方式发起请求 if (conn. get. Response. Code() != 200) throw new Runtime. Exception("请求url失败"); Input. Stream is = conn. get. Input. Stream(); //得到网络返回的输入流 String result = read. Data(is, "GBK"); conn. disconnect(); //第一个参数为输入流, 第二个参数为字符集编码 public static String read. Data(Input. Stream in. Sream, String charset. Name) throws Exception{ Byte. Array. Output. Stream out. Stream = new Byte. Array. Output. Stream(); byte[] buffer = new byte[1024]; int len = -1; while( (len = in. Sream. read(buffer)) != -1 ){ out. Stream. write(buffer, 0, len); } byte[] data = out. Stream. to. Byte. Array(); out. Stream. close(); in. Sream. close(); return new String(data, charset. Name); }
从Internet获取数据 利用Http. URLConnection对象, 我们可以从网络中获取文件数据. URL url = new URL("http: //photocdn. sohu. com/20100125/Img 269812337. jpg"); Http. URLConnection conn = (Http. URLConnection) url. open. Connection(); conn. set. Connect. Timeout(5* 1000); conn. set. Request. Method("GET"); if (conn. get. Response. Code() != 200) throw new Runtime. Exception("请求url失败"); Input. Stream is = conn. get. Input. Stream(); read. As. File(is, "Img 269812337. jpg"); public static void read. As. File(Input. Stream in. Sream, File file) throws Exception{ File. Output. Stream out. Stream = new File. Output. Stream(file); byte[] buffer = new byte[1024]; int len = -1; while( (len = in. Sream. read(buffer)) != -1 ){ out. Stream. write(buffer, 0, len); } out. Stream. close(); in. Sream. close(); }
向Internet发送请求参数 利用Http. URLConnection对象, 我们可以向网络发送请求参数. String request. Url = "http: //localhost: 8080/itcast/contanctmanage. do"; Map<String, String> request. Params = new Hash. Map<String, String>(); request. Params. put("age", "12"); request. Params. put("name", "中国"); String. Builder params = new String. Builder(); for(Map. Entry<String, String> entry : request. Params. entry. Set()){ params. append(entry. get. Key()); params. append("="); params. append(URLEncoder. encode(entry. get. Value(), "UTF-8")); params. append("&"); } if (params. length() > 0) params. delete. Char. At(params. length() - 1); byte[] data = params. to. String(). get. Bytes(); URL real. Url = new URL(request. Url); Http. URLConnection conn = (Http. URLConnection) real. Url. open. Connection(); conn. set. Do. Output(true); //发送POST请求必须设置允许输出 conn. set. Use. Caches(false); //不使用Cache conn. set. Request. Method("POST"); conn. set. Request. Property("Connection", "Keep-Alive"); //维持长连接 conn. set. Request. Property("Charset", "UTF-8"); conn. set. Request. Property("Content-Length", String. value. Of(data. length)); conn. set. Request. Property("Content-Type", "application/x-www-form-urlencoded"); Data. Output. Stream out. Stream = new Data. Output. Stream(conn. get. Output. Stream()); out. Stream. write(data); out. Stream. flush(); if( conn. get. Response. Code() == 200 ){ String result = read. As. String(conn. get. Input. Stream(), "UTF-8"); out. Stream. close(); System. out. println(result); }
向Internet发送xml数据 利用Http. URLConnection对象, 我们可以向网络发送xml数据. String. Builder xml = new String. Builder(); xml. append("<? xml version="1. 0" encoding="utf-8" ? >"); xml. append("<M 1 V=10000>"); xml. append("<U I=1 D="N 73">中国</U>"); xml. append("</M 1>"); byte[] xmlbyte = xml. to. String(). get. Bytes("UTF-8"); URL url = new URL("http: //localhost: 8080/itcast/contanctmanage. do? method=readxml"); Http. URLConnection conn = (Http. URLConnection) url. open. Connection(); conn. set. Connect. Timeout(5* 1000); conn. set. Do. Output(true); //允许输出 conn. set. Use. Caches(false); //不使用Cache conn. set. Request. Method("POST"); conn. set. Request. Property("Connection", "Keep-Alive"); //维持长连接 conn. set. Request. Property("Charset", "UTF-8"); conn. set. Request. Property("Content-Length", String. value. Of(xmlbyte. length)); conn. set. Request. Property("Content-Type", "text/xml; charset=UTF-8"); Data. Output. Stream out. Stream = new Data. Output. Stream(conn. get. Output. Stream()); out. Stream. write(xmlbyte); //发送xml数据 out. Stream. flush(); if (conn. get. Response. Code() != 200) throw new Runtime. Exception("请求url失败"); Input. Stream is = conn. get. Input. Stream(); //获取返回数据 String result = read. As. String(is, "UTF-8"); out. Stream. close();
为应用添加新的Activity 第一步:新建一个继承Activity的类,如:New. Activity public class New. Activity extends Activity { @Override protected void on. Create(Bundle saved. Instance. State) { super. on. Create(saved. Instance. State); //这里可以使用set. Content. View(R. layout. xxx)显示某个视图. . } } 第二步:需要在功能清单Android. Manifest. xml文件中添加进上面Activity配置代码(红色部分): <manifest xmlns: android="http: //schemas. android. com/apk/res/android" package="cn. itcast. action" android: version. Code="1" android: version. Name="1. 0"> <application android: icon="@drawable/icon" android: label="@string/app_name">. . . <activity android: name=". New. Activity" android: label="新activity的页面标题"/> </application>. . . </manifest> android: name属性值的前面加了一个点表示New. Activity是当前包cn. itcast. action下的类,如果类 在应用的当前包下,可以省略点符号,如果类在应用的子包下必须加点,如:New. Activity类在 cn. itcast. action. user包下可以这样写:<activity android: name=“. user. New. Activity“ />
打开新的Activity ,不传递参数 在一个Activity中可以使用系统提供的start. Activity(Intent intent)方法打开新的Activity,在打开新的 Activity前,你可以决定是否为新的Activity传递参数: 第一种:打开新的Activity,不传递参数 public class Main. Activity extends Activity { @Override protected void on. Create(Bundle saved. Instance. State) {. . . . Button button =(Button) this. find. View. By. Id(R. id. button); button. set. On. Click. Listener(new View. On. Click. Listener(){//点击该按钮会打开一个新的Activity public void on. Click(View v) { //新建一个显式意图,第一个参数为当前Activity类对象,第二个参数为你要打开的Activity类 start. Activity(new Intent(Main. Activity. this, New. Activity. class)); }}); } }
打开新的Activity,并传递若干个参数给 它 第二种:打开新的Activity,并传递若干个参数给它: public class Main. Activity extends Activity { @Override protected void on. Create(Bundle saved. Instance. State) {. . . . button. set. On. Click. Listener(new View. On. Click. Listener(){//点击该按钮会打开一个新的Activity public void on. Click(View v) { Intent intent = new Intent(Main. Activity. this, New. Activity. class) Bundle bundle = new Bundle(); //该类用作携带数据 bundle. put. String("name", "传智播客"); bundle. put. Int("age", 4); intent. put. Extras(bundle); //附带上额外的数据 start. Activity(intent); }}); } } 在新的Activity中接收前面Activity传递过来的参数: public class New. Activity extends Activity { @Override protected void on. Create(Bundle saved. Instance. State) {. . . . Bundle bundle = this. get. Intent(). get. Extras(); String name = bundle. get. String("name"); int age = bundle. get. Int("age"); } }
Bundle类的作用 Bundle类用作携带数据,它类似于Map,用于存放key-value名值对形式的值。相对于Map,它提 供了各种常用类型的put. Xxx()/get. Xxx()方法,如: put. String()/get. String()和put. Int()/get. Int(), put. Xxx()用于往Bundle对象放入数据,get. Xxx()方法用于从Bundle对象里获取数据。Bundle的内 部实际上是使用了Hash. Map<String, Object>类型的变量来存放put. Xxx()方法放入的值: public final class Bundle implements Parcelable, Cloneable {. . . Map<String, Object> m. Map; public Bundle() { m. Map = new Hash. Map<String, Object>(); . . . } public void put. String(String key, String value) { m. Map. put(key, value); } public String get. String(String key) { Object o = m. Map. get(key); return (String) o; . . . . //类型转换失败后会返回null,这里省略了类型转换失败后的处理代码 } } 在调用Bundle对象的get. Xxx()方法时,方法内部会从该变量中获取数据,然后对数据进行类型转 换,转换成什么类型由方法的Xxx决定,get. Xxx()方法会把转换后的值返回。
为Intent附加数据的两种写法 第一种写法,用于批量添加数据到Intent: Intent intent = new Intent(); Bundle bundle = new Bundle(); //该类用作携带数据 bundle. put. String("name", "传智播客"); intent. put. Extras(bundle); //为意图追加额外的数据,意图原来已经具有的数据不会丢失,但key同名的数据会被替换 第二种写法:这种写法的作用等价于上面的写法,只不过这种写法是把数据一个个地添加进Intent,这种写 法使用起来比较方便,而且只需要编写少量的代码。 Intent intent = new Intent(); intent. put. Extra("name", "传智播客"); Intent提供了各种常用类型重载后的put. Extra()方法,如: put. Extra(String name, String value)、 put. Extra(String name, long value),在put. Extra()方法内部会判断当前Intent对象内部是否已经存在一个 Bundle对象,如果不存在就会新建Bundle对象,以后调用put. Extra()方法传入的值都会存放于该Bundle对象, 下面是Intent的put. Extra(String name, String value)方法代码片断: public class Intent implements Parcelable { private Bundle m. Extras; public Intent put. Extra(String name, String value) { if (m. Extras == null) { m. Extras = new Bundle(); } m. Extras. put. String(name, value); return this; }
得到新打开Activity 关闭后返回的数据 如果你想在Activity中得到新打开Activity 关闭后返回的数据,你需要使用系统提供的 start. Activity. For. Result(Intent intent, int request. Code)方法打开新的Activity,新的Activity 关闭后会向前面的 Activity 传回数据,为了得到传回的数据,你必须在前面的Activity中重写on. Activity. Result(int request. Code, int result. Code, Intent data)方法: public class Main. Activity extends Activity { @Override protected void on. Create(Bundle saved. Instance. State) {. . . . Button button =(Button) this. find. View. By. Id(R. id. button); button. set. On. Click. Listener(new View. On. Click. Listener(){//点击该按钮会打开一个新的Activity public void on. Click(View v) { //第二个参数为请求码,可以根据业务需求自己编号 start. Activity. For. Result (new Intent(Main. Activity. this, New. Activity. class), 1); }}); } //第一个参数为请求码,即调用start. Activity. For. Result()传递过去的值 //第二个参数为结果码,结果码用于标识返回数据来自哪个新Activity @Override protected void on. Activity. Result(int request. Code, int result. Code, Intent data) { String result = data. get. Extras(). get. String(“result”)); //得到新Activity 关闭后返回的数据 } 当新Activity关闭后,新Activity返回的数据通过Intent进行传递,android平台会调用前面Activity 的 on. Activity. Result()方法,把存放了返回数据的Intent作为第三个输入参数传入,在on. Activity. Result()方法中 使用第三个输入参数可以取出新Activity返回的数据。 }
得到新打开Activity 关闭后返回的数据 使用start. Activity. For. Result(Intent intent, int request. Code)方法打开新的Activity,新Activity关闭前需要向前 面的Activity返回数据需要使用系统提供的set. Result(int result. Code, Intent data)方法实现: public class New. Activity extends Activity { @Override protected void on. Create(Bundle saved. Instance. State) {. . . button. set. On. Click. Listener(new View. On. Click. Listener(){ public void on. Click(View v) { Intent intent = new Intent(); //数据是使用Intent返回 intent. put. Extra(“result”, “传智播客的学生很可爱”); //把返回数据存入Intent New. Activity. this. set. Result(RESULT_OK, intent); //设置返回数据 New. Activity. this. finish(); //关闭Activity }}); } } set. Result()方法的第一个参数值可以根据业务需要自己定义,上面代码中使用到的RESULT_OK是系统 Activity类定义的一个常量,值为-1,代码片断如下: public class android. app. Activity extends. . . { public static final int RESULT_CANCELED = 0; public static final int RESULT_OK = -1; public static final int RESULT_FIRST_USER = 1; }
请求码的作用 使用start. Activity. For. Result(Intent intent, int request. Code)方法打开新的Activity,我们需要为start. Activity. For. Result()方法传入 一个请求码(第二个参数)。请求码的值是根据业务需要由自已设定,用于标识请求来源。例如:一个Activity有两个按钮,点 击这两个按钮都会打开同一个Activity,不管是那个按钮打开新Activity,当这个新Activity关闭后,系统都会调用前面Activity 的on. Activity. Result(int request. Code, int result. Code, Intent data)方法。在on. Activity. Result()方法如果需要知道新Activity是由 那个按钮打开的,并且要做出相应的业务处理,这时可以这样做: @Override public void on. Create(Bundle saved. Instance. State) {. . button 1. set. On. Click. Listener(new View. On. Click. Listener(){ public void on. Click(View v) { start. Activity. For. Result (new Intent(Main. Activity. this, New. Activity. class), 1); }}); button 2. set. On. Click. Listener(new View. On. Click. Listener(){ public void on. Click(View v) { start. Activity. For. Result (new Intent(Main. Activity. this, New. Activity. class), 2); }}); @Override protected void on. Activity. Result(int request. Code, int result. Code, Intent data) { switch(request. Code){ case 1: // 来自按钮 1的请求,作相应业务处理 case 2: //来自按钮 2的请求,作相应业务处理 } } }
结果码的作用 在一个Activity中,可能会使用start. Activity. For. Result()方法打开多个不同的Activity处理不同的业务,当这些新Activity关闭后, 系统都会调用前面Activity的on. Activity. Result(int request. Code, int result. Code, Intent data)方法。为了知道返回的数据来自于 哪个新Activity,在on. Activity. Result()方法中可以这样做(Result. Activity和New. Activity为要打开的新Activity): public class Result. Activity extends Activity {. . . Result. Activity. this. set. Result(1, intent); Result. Activity. this. finish(); } public class New. Activity extends Activity {. . . New. Activity. this. set. Result(2, intent); New. Activity. this. finish(); } public class Main. Activity extends Activity { // 在该Activity会打开Result. Activity和New. Activity @Override protected void on. Activity. Result(int request. Code, int result. Code, Intent data) { switch(result. Code){ case 1: // Result. Activity 的返回数据 case 2: // New. Activity的返回数据 } } }
Intent(意图) Android基本的设计理念是鼓励减少组件间的耦合,因此Android提供了Intent (意图) ,Intent提供了一种通用 的消息系统,它允许在你的应用程序与其它的应用程序间传递Intent来执行动作和产生事件。使用Intent可以 激活Android应用的三个核心组件:活动、服务和广播接收器。 Intent可以划分成显式意图和隐式意图。 显式意图:调用Intent. set. Component()或Intent. set. Class()方法明确指定了组件名的Intent为显式意图,显式 意图明确指定了Intent应该传递给哪个组件。 隐式意图:没有明确指定组件名的Intent为隐式意图。 Android系统会根据隐式意图中设置的动作(action)、 类别(category)、数据(URI和数据类型)找到最合适的组件来处理这个意图。查找规则请见ppt下方备注。 <intent-filter> <action android: name="android. intent. action. CALL" /> <category android: name="android. intent. category. DEFAULT" /> <data android: scheme="tel" /> </intent-filter> <action android: name="android. intent. action. CALL" /> <category android: name="android. intent. category. DEFAULT" /> <data android: mime. Type="vnd. android. cursor. item/phone" /> </intent-filter>
Activity生命周期
Activity的on. Save. Instance. State()和 on. Restore. Instance. State()方法 Activity的 on. Save. Instance. State() 和 on. Restore. Instance. State()并不是生命周期方法,它们不 同于 on. Create()、on. Pause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如: 内存不足、用户直接按Home键)由系统销毁一个Activity时,on. Save. Instance. State() 会被调用。 但是当用户主动去销毁一个Activity时,例如在应用中按返回键,on. Save. Instance. State()就不会被 调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常 on. Save. Instance. State()只适合用于保存一些临时性的状态,而on. Pause()适合用于数据的持久化保 存。 另外,当屏幕的方向发生了改变, Activity会被摧毁并且被重新创建,如果你想在Activity被摧毁 前缓存一些数据,并且在Activity被重新创建后恢复缓存的数据。可以重写Activity的 on. Save. Instance. State() 和 on. Restore. Instance. State()方法,如下: public class Preferences. Activity extends Activity { private String name; protected void on. Restore. Instance. State(Bundle saved. Instance. State) { name = saved. Instance. State. get. String("name"); //被重新创建后恢复缓存的数据 super. on. Restore. Instance. State(saved. Instance. State); } protected void on. Save. Instance. State(Bundle out. State) { out. State. put. String("name", "liming"); //被摧毁前缓存一些数据 super. on. Save. Instance. State(out. State); } }
广播接收者--Broadcast. Receiver 广播接收者(Broadcast. Receiver)用于接收广播Intent,广播Intent的发送是通过调用 Context. send. Broadcast()、Context. send. Ordered. Broadcast()来实现的。通常一个广播Intent可 以被订阅了此Intent的多个广播接收者所接收,这个特性跟JMS中的Topic消息接收者类似。要实 现一个广播接收者方法如下: 第一步:继承Broadcast. Receiver,并重写on. Receive()方法。 public class Incoming. SMSReceiver extends Broadcast. Receiver { @Override public void on. Receive(Context context, Intent intent) { } } 第二步:订阅感兴趣的广播Intent,订阅方法有两种: 第一种:使用代码进行订阅 Intent. Filter filter = new Intent. Filter("android. provider. Telephony. SMS_RECEIVED"); Incoming. SMSReceiver receiver = new Incoming. SMSReceiver(); register. Receiver(receiver, filter); 第二种:在Android. Manifest. xml文件中的<application>节点里进行订阅: <receiver android: name=". Incoming. SMSReceiver"> <intent-filter> <action android: name="android. provider. Telephony. SMS_RECEIVED"/> </intent-filter> </receiver>
使用广播接收者窃听短信 如果你想窃听别人接收到的短信,达到你不可告人的目的,那么本节内容可以实现你的需求。 当系统收到短信时,会发出一个广播Intent,Intent的action名称为android. provider. Telephony. SMS_RECEIVED,该Intent存放 了系统接收到的短信内容,我们使用名称“pdus”即可从Intent中获取到短信内容。 public class Incoming. SMSReceiver extends Broadcast. Receiver { private static final String SMS_RECEIVED = "android. provider. Telephony. SMS_RECEIVED"; @Override public void on. Receive(Context context, Intent intent) { if (intent. get. Action(). equals(SMS_RECEIVED)) { Sms. Manager sms = Sms. Manager. get. Default(); Bundle bundle = intent. get. Extras(); if (bundle != null) { Object[] pdus = (Object[]) bundle. get("pdus"); Sms. Message[] messages = new Sms. Message[pdus. length]; for (int i = 0; i < pdus. length; i++) messages[i] = Sms. Message. create. From. Pdu((byte[]) pdus[i]); for (Sms. Message message : messages){ String msg = message. get. Message. Body(); String to = message. get. Originating. Address(); sms. send. Text. Message(to, null, msg, null); }}}}} 在Android. Manifest. xml文件中的<application>节点里对接收到短信的广播Intent进行订阅: <receiver android: name=". Incoming. SMSReceiver"> <intent-filter><action android: name="android. provider. Telephony. SMS_RECEIVED"/></intent-filter></receiver> 在Android. Manifest. xml文件中添加以下权限: <uses-permission android: name="android. permission. RECEIVE_SMS"/><!-- 接收短信权限 --> <uses-permission android: name="android. permission. SEND_SMS"/><!-- 发送短信权限 -->
广播接收者 除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电池电量变化、时间已经改 变等广播Intent。 l 接收电池电量变化广播Intent ,在Android. Manifest. xml文件中的<application>节点里订阅此 Intent: <receiver android: name=". Incoming. SMSReceiver"> <intent-filter> <action android: name="android. intent. action. BATTERY_CHANGED"/> </intent-filter> </receiver> l 接收开机启动广播Intent,在Android. Manifest. xml文件中的<application>节点里订阅此Intent: <receiver android: name=". Incoming. SMSReceiver"> <intent-filter> <action android: name="android. intent. action. BOOT_COMPLETED"/> </intent-filter> </receiver> 并且要进行权限声明: <uses-permission android: name="android. permission. RECEIVE_BOOT_COMPLETED"/>
电话窃听器 要实现电话窃听,需要监听电话的状态,方法如下: /* 取得电话服务 */ Telephony. Manager tel. Manager = (Telephony. Manager) get. System. Service(Context. TELEPHONY_SERVICE); Phone. State. Listener listener = new Phone. State. Listener(){ @Override public void on. Call. State. Changed(int state, String incoming. Number) { switch (state){ case Telephony. Manager. CALL_STATE_IDLE: /* 无任何状态时 */ break; case Telephony. Manager. CALL_STATE_OFFHOOK: /* 接起电话时 */ break; case Telephony. Manager. CALL_STATE_RINGING: /* 电话进来时 */ break; default: break; } super. on. Call. State. Changed(state, incoming. Number); } }; //监听电话的状态 tel. Manager. listen(listener, Phone. State. Listener. LISTEN_CALL_STATE); 在清单文件Android. Manifest. xml中添加权限: <uses-permission android: name="android. permission. READ_PHONE_STATE"/>
音频采集 你可以使用手机进行现场录音,实现步骤如下: 第一步:在功能清单文件Android. Manifest. xml中添加音频刻录权限: <uses-permission android: name="android. permission. RECORD_AUDIO"/> 第二步:编写音频刻录代码: Media. Recorder recorder = new Media. Recorder(); recorder. set. Audio. Source(Media. Recorder. Audio. Source. MIC); //从麦克风采集声音 recorder. set. Output. Format(Media. Recorder. Output. Format. THREE_GPP); //内容输出格式 recorder. set. Audio. Encoder(Media. Recorder. Audio. Encoder. AMR_NB); //音频编码方式 recorder. set. Output. File("/mnt/sdcard/itcast. amr"); recorder. prepare(); //预期准备 recorder. start(); //开始刻录. . . recorder. stop(); //停止刻录 recorder. reset(); //重设 recorder. release(); //刻录完成一定要释放资源
音乐播放 Media. Player media. Player = new Media. Player(); if (media. Player. is. Playing()) { media. Player. reset(); //重置为初始状态 } media. Player. set. Data. Source("/mnt/sdcard/god. mp 3"); media. Player. prepare(); media. Player. start(); //开始或恢复播放 media. Player. pause(); //暂停播放 media. Player. start(); //恢复播放 media. Player. stop(); //停止播放 media. Player. release(); //释放资源 media. Player. set. On. Completion. Listener(new Media. Player. On. Completion. Listener() {//播出完毕事件 @Override public void on. Completion(Media. Player arg 0) { media. Player. release(); } }); media. Player. set. On. Error. Listener(new Media. Player. On. Error. Listener() {// 错误处理事件 @Override public boolean on. Error(Media. Player player, int arg 1, int arg 2) { media. Player. release(); return false; } });
视频播放 在main. xml布局文件添加用于视频画面绘制的Surface. View 控件: <Surface. View android: layout_width="fill_parent" android: layout_height="240 dip" android: id="@+id/surface. View" /> Surface. View surface. View = (Surface. View)this. find. View. By. Id(R. id. surface. View); surface. View. get. Holder(). set. Fixed. Size(176, 144); //设置分辨率 /*下面设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到用户面前*/ surface. View. get. Holder(). set. Type(Surface. Holder. SURFACE_TYPE_PUSH_BUFFERS); Media. Player media. Player = new Media. Player(); media. Player. reset(); //重置为初始状态 media. Player. set. Audio. Stream. Type(Audio. Manager. STREAM_MUSIC); /* 设置Video影片以Surface. Holder播放 */ media. Player. set. Display(surface. View. get. Holder()); media. Player. set. Data. Source("/mnt/sdcard/oppo. mp 4"); media. Player. prepare(); media. Player. start(); //播放 media. Player. pause(); //暂停播放 media. Player. start(); //恢复播放 media. Player. stop(); //停止播放 media. Player. release(); //释放资源
使用摄像头拍照 在main. xml布局文件添加用于显示取景画面的Surface. View 控件: <Surface. View android: layout_width="fill_parent" android: layout_height="240 dip" android: id="@+id/surface. View" /> Surface. View surface. View = (Surface. View)this. find. View. By. Id(R. id. surface. View); surface. View. get. Holder(). set. Fixed. Size(176, 144); //设置分辨率 /*下面设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到用户面前*/ surface. View. get. Holder(). set. Type(Surface. Holder. SURFACE_TYPE_PUSH_BUFFERS); Camera camera = Camera. open(); Window. Manager wm = (Window. Manager) get. System. Service(Context. WINDOW_SERVICE); Display display = wm. get. Default. Display(); Camera. Parameters parameters = camera. get. Parameters(); parameters. set. Preview. Size(display. get. Width(), display. get. Height()); //设置预览照片的大小 parameters. set. Preview. Frame. Rate(3); //每秒 3帧 parameters. set. Picture. Format(Pixel. Format. JPEG); //设置照片的输出格式 parameters. set("jpeg-quality", 85); //照片质量 parameters. set. Picture. Size(display. get. Width(), display. get. Height()); //设置照片的大小 camera. set. Parameters(parameters); camera. set. Preview. Display(surface. View. get. Holder()); //通过Surface. View显示取景画面 camera. start. Preview(); //开始预览 camera. auto. Focus(null); //自动对焦 camera. take. Picture(null, jpeg. Callback); //拍照片 camera. stop. Preview(); //停止预览 camera. release(); //释放摄像头
音视频采集 第一步:在功能清单文件Android. Manifest. xml中添加音频刻录和照相机权限: <uses-permission android: name="android. permission. RECORD_AUDIO"/> <uses-permission android: name="android. permission. CAMERA"/> <uses-permission android: name="android. permission. MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android: name="android. permission. WRITE_EXTERNAL_STORAGE"/> 第二步:编写音频刻录代码: recorder. reset(); recorder. set. Video. Source(Media. Recorder. Video. Source. CAMERA); //从照相机采集视频 recorder. set. Audio. Source(Media. Recorder. Audio. Source. MIC); recorder. set. Output. Format(Media. Recorder. Output. Format. THREE_GPP); recorder. set. Video. Size(320, 240); recorder. set. Video. Frame. Rate(3); //每秒 3帧 recorder. set. Video. Encoder(Media. Recorder. Video. Encoder. H 263); //设置视频编码方式 recorder. set. Audio. Encoder(Media. Recorder. Audio. Encoder. AMR_NB); recorder. set. Output. File("/mnt/sdcard/itcast. 3 gp"); recorder. set. Preview. Display(surface. View. get. Holder(). get. Surface()); recorder. prepare(); //预期准备 recorder. start(); //开始刻录. . . recorder. stop(); //停止刻录 recorder. release(); //刻录完成一定要释放资源
Android的状态栏通知(Notification) 通知用于在状态栏显示消息,消息到来时以图标方式表示,如下: 如果需要查看消息,可以拖动状态栏到屏幕下方即可查看消息。 发送消息的代码如下: //获取通知管理器 Notification. Manager m. Notification. Manager = (Notification. Manager) get. System. Service(Context. NOTIFICATION_SERVICE); int icon = android. R. drawable. stat_notify_chat; long when = System. current. Time. Millis(); //新建一个通知,指定其图标和标题 Notification notification = new Notification(icon, null, when); //第一个参数为图标, 第二个参数为标题, 第三个为通知 时间 notification. defaults = Notification. DEFAULT_SOUND; //发出默认声音 Intent openintent = new Intent(this, Other. Activity. class); Pending. Intent content. Intent = Pending. Intent. get. Activity(this, 0, openintent, 0); //当点击消息时就会向系统发送 openintent意图 notification. set. Latest. Event. Info(this, “标题”, “我是内容", content. Intent); m. Notification. Manager. notify(0, notification); //第一个参数为自定义的通知唯一标识
对话框通知(Dialog Notification) 当你的应用需要显示一个进度条或需要用户对信息进行确认时,可以使用对话框来完成。 下面代码将打开一个如右图所示的对话框: new Alert. Dialog. Builder(context). set. Title("java培训"). set. Cancelable(false) //设置不能通过“后退”按钮关闭对话框. set. Message("浏览传智播客网站? "). set. Positive. Button("确认", new Dialog. Interface. On. Click. Listener(){ public void on. Click(Dialog. Interface dialoginterface, int i){ Uri uri = Uri. parse("http: //www. itcast. cn/"); //打开链接 Intent intent = new Intent(Intent. ACTION_VIEW, uri); start. Activity(intent); } }). set. Negative. Button("取消", new Dialog. Interface. On. Click. Listener() { public void on. Click(Dialog. Interface dialog, int id) { dialog. cancel(); } }). show(); //显示对话框 上面代码采用的是一个链式调用,像set. Title()、set. Message()这些方法,他们的返回值都是当前对话框对 象。
创建带单选项列表的对话框 下面代码将打开一个如右上图所示的选项列表对话框: final String[] items = {"java", ". net", "php"}; new Alert. Dialog. Builder(Sender. Notification. Activity. this). set. Title("选择语言"). set. Items(items, new Dialog. Interface. On. Click. Listener() { public void on. Click(Dialog. Interface dialog, int item) { Toast. make. Text(get. Application. Context(), items[item], Toast. LENGTH_SHORT). show(); } }). show(); //显示对话框 下面代码将打开一个如右下图所示的带单选框的列表对话框: final String[] items = {"java", ". net", "php"}; new Alert. Dialog. Builder(Sender. Notification. Activity. this). set. Title("选择语言"). set. Single. Choice. Items(items, 1, new Dialog. Interface. On. Click. Listener() { public void on. Click(Dialog. Interface dialog, int item) { Toast. make. Text(get. Application. Context(), items[item], Toast. LENGTH_SHORT). show(); dialog. cancel(); } }). show(); //显示对话框 set. Single. Choice. Items()的第二个参数是设置默认选项, 选项索引从0开始,-1代表不选择任何选项。
创建带多选项列表的对话框 下面代码将打开一个如右下图所示的多选项列表对话框: final String[] items = {"java", ". net", "php"}; new Alert. Dialog. Builder(Sender. Notification. Activity. this). set. Cancelable(false). set. Title("选择语言"). set. Multi. Choice. Items(items, new boolean[]{false, true, false}, new Dialog. Interface. On. Multi. Choice. Click. Listener() { @Override public void on. Click(Dialog. Interface dialog, int which, boolean is. Checked) { if(is. Checked){ Toast. make. Text(get. Application. Context(), items[which], Toast. LENGTH_SHORT). show(); } } }). set. Positive. Button("确认", new Dialog. Interface. On. Click. Listener(){ public void on. Click(Dialog. Interface dialoginterface, int i){ dialoginterface. dismiss(); } }). show(); //显示对话框
进度对话框(Progress. Dialog) 效果图: l使用代码Progress. Dialog. show(Progress. Dialog. Activity. this, "请稍等", "数据正在加载中. . . ", true); 创建并显示一个进度对话框。 l调用set. Progress. Style()方法设置进度对话框风格。有两种风格: Progress. Dialog. STYLE_SPINNER 旋体进度条风格 (为默认风格) Progress. Dialog. STYLE_HORIZONTAL 横向进度条风格
下拉列表框(Spinner) 效果图: Spinner. get. Item. At. Position(Spinner. get. Selected. Item. Position()); 获取下拉列表框的值 l 调用set. On. Item. Selected. Listener()方法,处理下拉列表框被选择事件,把 Adapter. View. On. Item. Selected. Listener实例作为参数传入 l
下拉列表框--自定义选项界面样式 效果图: Spinner. get. Item. At. Position(Spinner. get. Selected. Item. Position()); 获取下拉列表框的值 l 调用set. On. Item. Selected. Listener()方法,处理下拉列表框被选择事件,把 Adapter. View. On. Item. Selected. Listener实例作为参数传入 l
拖动条(Seek. Bar) 效果图: Seek. Bar. get. Progress()获取拖动条当前值 l 调用set. On. Seek. Bar. Change. Listener()方法,处理拖动条值变化事件,把 Seek. Bar. On. Seek. Bar. Change. Listener实例作为参数传入 l
进度条(Progress. Bar) 在布局xml文件中添加进度条代码: <Progress. Bar android: layout_width="fill_parent" android: layout_height="20 px" style="? android: attr/progress. Bar. Style. Horizontal" android: id="@+id/downloadbar"/> 在代码中操作进度条: Progress. Bar. set. Max(100); //设置最大刻度 Progress. Bar. set. Progress(0); //设置进度条的当前刻度,如果进度条的最大刻度为 100,当前刻度 为 50,进度条将进行到一半。
输入内容自动完成文本框( Auto. Complete. Text. View ) Auto. Complete. Text. View和Edit. Text组件类似,都可以输入文本。 但Auto. Complete. Text. View组件可以和一个字符串数组或List对象 绑定,当用户输入两个及以上字符时,系统将在 Auto. Complete. Text. View组件下方列出字符串数组中所有以输入 字符开头的字符串,这一点和www. google. com的搜索框非常相似, 当输入某一个要查找的字符串时,google搜索框就会列出以这个 字符串开头的最热门的搜索字符串列表。 <Auto. Complete. Text. View android: layout_width="fill_parent“ android: layout_height="wrap_content“ <!– completion. Threshold 指定至少输入几个字符后才会出现自动提示功能 android: completion. Threshold="1“ android: id="@+id/name" /> public void on. Create(Bundle saved. Instance. State) { super. on. Create(saved. Instance. State); set. Content. View(R. layout. main); String[] names = {"老张", "老方", "老毕", "李明" , "李丽", "陈江", "abc", "acc"}; Auto. Complete. Text. View name. Text = (Auto. Complete. Text. View)this. find. View. By. Id(R. id. name); Array. Adapter<String> adapter = new Array. Adapter<String>(this, android. R. layout. simple_dropdown_item_1 line, names); name. Text. set. Adapter(adapter); }
多次输入-内容自动完成文本框( Multi. Auto. Complete. Text. View) 除了Auto. Complete. Text. View控件外,我们还可以使用Multi. Auto. Complete. Text. View控件来完成连续 输入的功能。也就是说,当输入完一个字符串后,在该字符串后面输入一个逗号(, ),在逗号前后 可以有任意多个空格,然后再输入一个字符串,仍然会显示自动提示列表。 使用Multi. Auto. Complete. Text. View时,需要为它的set. Tokenizer方法指定 Multi. Auto. Complete. Text. View. Comma. Tokenizer类对象实例, 该对象表示采用逗号作为输入多个字符串的分隔符。 < Multi. Auto. Complete. Text. View android: layout_width="fill_parent“ android: layout_height="wrap_content“ <!– completion. Threshold 指定至少输入几个字符后才会出现自动提示功能 android: completion. Threshold="1“ android: id="@+id/name" /> public void on. Create(Bundle saved. Instance. State) { super. on. Create(saved. Instance. State); set. Content. View(R. layout. main); String[] names = {"老张", "老方", "老毕", "李明" , "李丽", "陈江", "abc", "acc"}; Multi. Auto. Complete. Text. View name. Text = (Multi. Auto. Complete. Text. View)this. find. View. By. Id(R. id. name); Array. Adapter<String> adapter = new Array. Adapter<String>(this, android. R. layout. simple_dropdown_item_1 line, names); name. Text. set. Adapter(adapter); name. Text. set. Tokenizer(new Multi. Auto. Complete. Text. View. Comma. Tokenizer()); }
手势识别 第一步:建立手势库 使用SDK自带例子Gesture. Builder建立手势库(位置:android-sdk-windowssamplesandroid 8Gesture. Builder)。使用Gesture. Builder之前,你需要恢复其到开发环境,然后进行编绎并部署到手机上。 此时,就可以使用Gesture. Builder建立手势库,生成的手势库文件在SCDard上,默认文件名称为: gestures 第二步:在应用中加载手势库文件,然后开发手势识别代码。 把手势库文件gestures文件拷贝到项目的res/raw目录下。然后在布局文件中添加用于手势绘制的View: <android. gesture. Gesture. Overlay. View android: id="@+id/gestures" android: layout_width="fill_parent“ android: layout_height="0 dip" android: layout_weight="1. 0" /> 为View添加手势监听事件:gesture. Overlay. View. add. On. Gesture. Performed. Listener(); 得到手势库:m. Library = Gesture. Libraries. from. Raw. Resource(this, R. raw. gestures); 加载手势库:m. Library. load(); List<Prediction> predictions = m. Library. recognize(gesture); //从手势库中查询匹配的内容,匹配的结果可能包括多个相似的 内容,匹配度高的结果放在最前面 大多数情况下,手势都是通过一笔完成。然而有一些特别的需求就需要通过多个笔画来实现,这时可以使 用gesture. Stroke. Type属性进行设置:android: gesture. Stroke. Type="multiple" 手势识别代码见ppt下方
android样式和主题(style&theme) android中的样式和CSS样式作用相似,都是用于为界面元素定义显示风格,它是一个包含一个或 者多个view控件属性的集合。如:需要定义字体的颜色和大小。 在CSS中是这样定义的: <style>. itcast{COLOR: #0000 CC; font-size: 18 px; } </style> 可以像这样使用上面的css样式:<div class="itcast">传智播客</div> 在Android中可以这样定义样式: 在res/values/styles. xml文件中添加以下内容 <? xml version="1. 0" encoding="utf-8"? > <resources> <style name=“itcast”> <!-- 为样式定义一个全局唯一的名字--> <item name=“android: text. Size”>18 px</item> <!-- name属性的值为使用了该样式的View控件的属性 --> <item name="android: text. Color">#0000 CC</item> </style> </resources> 在layout文件中可以像下面这样使用上面的android样式: <? xml version="1. 0" encoding="utf-8"? > <Linear. Layout xmlns: android="http: //schemas. android. com/apk/res/android". . > <Text. View style="@style/itcast". . . /> </Linear. Layout>
android样式和主题(style&theme) <style>元素中有一个parent属性。这个属性可以让当前样式继承一个父样式,并且具有父样式的 值。当然,如果父样式的值不符合你的需求,你也可以对它进行修改,如下: <? xml version="1. 0" encoding="utf-8"? > <resources> <style name="itcast"> <item name="android: text. Size">18 px</item> <!-- name属性为样式要用在的View控件持有的属性 --> <item name="android: text. Color">#0000 CC</item> </style> <style name="subitcast" parent="@style/itcast"> <item name="android: text. Color">#FF 0000</item> </style> </resources>
android样式和主题(style&theme) android中主题也是用于为应用定义显示风格,它的定义和样式的定义相同,如下: <? xml version="1. 0" encoding="utf-8"? > <resources> <style name=“itcast. Theme"> <item name=“android: window. No. Title”>true</item> <!– 没标题 <item name=“android: window. Fullscreen”>? android: window. No. Title</item> <!– 全屏显示 </style> </resources> 上面“? android: window. No. Title”中的问号用于引用在当前主题中定义过的资源的值。下面代码显示在Android. Manifest. xml 中如何为应用设置上面定义的主题: <application android: icon="@drawable/icon" android: label="@string/app_name" android: theme="@style/itcast. Theme">. . . </application> 除了可以在Android. Manifest. xml中设置主题,同样也可以在代码中设置主题,如下: set. Theme(R. style. itcast. Theme); 尽管在定义上,样式和主题基本相同,但是它们使用的地方不同。样式用在单独的View,如:Edit. Text、Text. View等; 主题通过Android. Manifest. xml中的<application>和<activity>用在整个应用或者某个 Activity,主题对整个应用或某个 Activity进行全局性影响。如果一个应用使用了主题,同时应用下的view也使用了样式,那么当主题和样式属性发生冲突 时,样式的优先级高于主题。 另外android系统也定义了一些主题,例如:<activity android: theme=“@android: style/Theme. Dialog”>,该主题可以让 Activity看起来像一个对话框,还有透明主题:@android: style/Theme. Translucent 。如果需要查阅这些主题,可以在文 档的reference android-->R. style 中查看。
编码实现软件界面 Android除了可以使用xml实现软件界面,还可以通过编码方式实现软件的界面,而且在某种情况 下只能采用编码方式实现软件的界面,例如:软件运行时需要根据运算结果决定显示某些内容。 如果不是必须,建议使用xml,因为这样可以使应用遵守mvc设计模式,具有良好的软件分层结构。 下面代码实现了如Hello. World项目一样的软件界面: public class Main. Activity extends Activity { public void on. Create(Bundle saved. Instance. State) { super. on. Create(saved. Instance. State); Linear. Layout linear. Layout = new Linear. Layout(this); Linear. Layout. Params layout. Params = new Linear. Layout. Params( View. Group. Layout. Params. FILL_PARENT, View. Group. Layout. Params. FILL_PARENT); Text. View text. View = new Text. View(this); text. View. set. Text(R. string. hello); text. View. set. Id(34); Linear. Layout. Params text. Params = new Linear. Layout. Params( View. Group. Layout. Params. FILL_PARENT, View. Group. Layout. Params. WRAP_CONTENT); linear. Layout. add. View(text. View, text. Params); set. Content. View(linear. Layout, layout. Params); } }
安装外部程序 首先需要Android. Manifest. xml中加入安装程序权限: <!-- 安装程序权限 --> <uses-permission android: name="android. permission. INSTALL_PACKAGES"/> 第二步把安装程序添加进SDCard。如把文件名为” sogouinput_android_1. 40_sweb. apk. zip”的 sogou拼音输入法安装文件放进SDCard。可以点击下面按钮: 第三步在程序中添加以下代码: Intent intent = new Intent(); intent. add. Flags(Intent. FLAG_ACTIVITY_NEW_TASK); intent. set. Action(android. content. Intent. ACTION_VIEW); intent. set. Data. And. Type(Uri. from. File(new File(Environment. get. External. Storage. Directory(), "sogouinput_android_1. 40_sweb. apk. zip")), "application/vnd. android. package-archive"); start. Activity(intent);
关闭应用 当应用不再使用时,通常需要关闭应用,可以使用以下两种方法关闭android应用: 第一种方法:首先获取当前进程的id,然后杀死该进程。 (建议使用) android. os. Process. kill. Process(android. os. Process. my. Pid()) 第二种方法:终止当前正在运行的Java虚拟机,导致程序终止 System. exit(0); 第三种方法:强制关闭与该包有关联的一切执行 Activity. Manager manager = (Activity. Manager) get. System. Service(Context. ACTIVITY_SERVICE); manager. restart. Package(get. Package. Name()); <uses-permission android: name="android. permission. RESTART_PACKAGES" />
判断SIM卡属于哪个移动运营商 见备注栏: 在文件Android. Manifest. xml中添加权限 <uses-permission android: name="android. permission. READ_PHONE_STATE"/>
从SIM卡中获取联系人信息 Uri uri = Uri. parse("content: //icc/adn"); String[] projection = {"_id", "name", "number"}; Cursor cursor = managed. Query(uri, projection, null, "name"); if(cursor!=null){ while(cursor. move. To. Next()){ String name = cursor. get. String(cursor. get. Column. Index("name")); String phone = cursor. get. String(cursor. get. Column. Index("number")); } } 在文件Android. Manifest. xml中添加权限 <uses-permission android: name="android. permission. READ_PHONE_STATE"/> Android系统内部通过Contentprovider对外共享Sim卡存放的联系人等信息,你可以通过操作 Contentprovider来实现Sim卡信息的添删改查操作。 内部实现源代码参见备注栏:
- Slides: 125