[关闭]
@king 2015-10-21T12:01:49.000000Z 字数 26071 阅读 2656

安卓学习笔记

Android


技术文章


Android四大基本组件介绍与生命周期

Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器。


一:了解四大基本组件


Activity

应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。

Activity之间通过Intent进行通信。在Intent 的描述结构中,有两个最重要的部分:动作和动作对应的数据。

典型的动作类型有:MAIN(activity的门户)、VIEW、PICK、EDIT 等。而动作对应的数据则以URI 的形式进行表示。例如:要查看一个人的联系方式,你需要创建一个动作类型为VIEW 的intent,以及一个表示这个人的URI。

Intent:一个有效的做某事的请求
Intentfilter:用于描述一个Activity(或者IntentReceiver)能够操作哪些Intent。IntentFilter 需要在AndroidManifest.xml 中定义。

Activity将会调用startActivity(Intent myIntent)方法。然后,系统会在所有安装的应用程序中定义的IntentFilter 中查找,找到最匹配myIntent 的Intent 对应的activity。新的activity 接收到myIntent 的通知后,开始运行。当startActivity 方法被调用将触发解析myIntent 的动作,这个机制提供了两个关键好处:

  • Activities 能够重复利用从其它组件中以Intent 的形式产生的一个请求;
  • Activities 可以在任何时候被一个具有相同IntentFilter 的新的Activity 取代。

AndroidManifest文件中含有如下过滤器的Activity组件为默认启动类当程序启动时系统自动调用它

  1. <intent-filter>
  2. <action android:name="android.intent.action.MAIN" />
  3. <category android:name="android.intent.category.LAUNCHER" />
  4. </intent-filter>

BroadcastReceive广播接收器:

使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个Activity或Service 来响应它们收到的信息,或者用NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

广播类型:

  • 普通广播,通过Context.sendBroadcast(Intent myIntent)发送的
  • 有序广播,通过Context.sendOrderedBroadcast(intent, receiverPermission)发送的,该方法第2个参数决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 发送的优先级越高;广播接收者接收广播时的级别(可通过intentfilter中的priority进行设置, 设为2147483647时优先级最高),同级别接收的先后是随机的, 再到级别低的收到广播,高级别的或同级别先接收到广播的可以通过abortBroadcast()方法截断广播使其他的接收者无法收到该广播
  • 异步广播,通过Context.sendStickyBroadcast(Intent myIntent)发送的,还有sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras)方法,该方法具有有序广播的特性也有异步广播的特性;发送异步广播要: 权限,接收并处理完Intent后,广播依然存在,直到你调用removeStickyBroadcast(intent)主动把它去掉

注意:发送广播时的intent参数与Contex.startActivity()启动起来的Intent不同,前者可以被多个订阅它的广播接收器调用,后者只能被一个(Activity或service)调用

监听广播Intent步骤:

静态注册,注册的广播,下面的priority表示接收广播的级别"2147483647"为最高优先级

  1. <receiver android:name=".SMSBroadcastReceiver" >
  2.   <intent-filter android:priority = "2147483647" >
  3.     <action android:name="android.provider.Telephony.SMS_RECEIVED" />
  4.   </intent-filter>
  5. </receiver >

动态注册,一般在Activity可交互时onResume()内注册BroadcastReceiver

  1. IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
  2. registerReceiver(mBatteryInfoReceiver, intentFilter);
  3. //反注册
  4. unregisterReceiver(receiver);

注意:

  1. 生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的
  2. 动态注册广播接收器还有一个特点,就是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用

系统常见广播Intent,如开机启动、电池电量变化、时间改变等广播


Service 服务:

一个Service 是一段长生命周期的,没有用户界面的程序,可以用来开发如监控类程序。

比较好的一个例子就是一个正在从播放列表中播放歌曲的媒体播放器。在一个媒体播放器的应用中,应该会有多个activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的activity,因为使用者当然会认为在导航到其它屏幕时音乐应该还在播放的。在这个例子中,媒体播放器这个activity 会使用Context.startService()来启动一个service,从而可以在后台保持音乐的播放。同时,系统也将保持这个service 一直执行,直到这个service 运行结束。另外,我们还可以通过使用Context.bindService()方法,连接到一个service 上(如果这个service 还没有运行将启动它)。当连接到一个service 之后,我们还可以service 提供的接口与它进行通讯。拿媒体播放器这个例子来说,我们还可以进行暂停、重播等操作。

Service使用步骤如下

<1>继承service类
<2>AndroidManifast.xml配置清单文件中节点里对服务进行配置

服务不能自己运行,需要通过Contex.startService()或Contex.bindService()启动服务。

通过startService()方法启动的服务于调用者没有关系,即使调用者关闭了,服务仍然运行。想停止服务要调用Context.stopService()。此时系统会调用onDestory(),使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onStart(),如果服务已经启动再次调用只会触发onStart()方法。

使用bindService()启动的服务与调用者绑定,只要调用者关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onBind(),如果服务已经启动再次调用不会再触发这2个方法,调用者退出时系统会调用服务的onUnbind()-->onDestory(),想主动解除绑定可使用Contex.unbindService(),系统依次调用onUnbind()-->onDestory();


Content Provider

android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式,其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据(相当于在应用外包了一层壳),只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。

它的好处:统一数据访问方式。

android系统自带的内容提供者(顶级的表示数据库名,非顶级的都是表名)这些内容提供者在SDK文档的android.provider Java包中都有介绍。见:http://developer.android.com/reference/android/provider/package-summary.html

├────Browser
├────CallLog
├────Contacts
│ ├────Groups
│ ├────People
│ ├────Phones
│ └────Photos
├────Images
│ └────Thumbnails
├────MediaStore
│ ├────Albums
│ ├────Artists
│ ├────Audio
│ ├────Genres
│ └────Playlists
├────Settings
└────Video

CallLog:地址和接收到的电话信息
Contact.People.Phones:存储电话号码
Setting.System:系统设置和偏好设置

使用Content Provider对外共享数据的步骤
<1>继承ContentProvider类并根据需求重写以下方法:

  1. public boolean onCreate();//处理初始化操作
  2. /**
  3. * 插入数据到内容提供者(允许其他应用向你的应用中插入数据时重写)
  4. */
  5. public Uri insert(Uri uri, ContentValues initialValues);
  6. /**
  7. * 从内容提供者中删除数据(允许其他应用删除你应用的数据时重写)
  8. */
  9. public int delete(Uri uri, String selection, String[] selectionArgs);
  10. /**
  11. * 更新内容提供者已存在的数据(允许其他应用更新你应用的数据时重写)
  12. */
  13. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs);
  14. /**
  15. * 返回数据给调用者(允许其他应用从你的应用中获取数据时重写)
  16. * projection 列名
  17. */
  18. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) ;
  19. /**
  20. * 用于返回当前Uri所代表数据的MIME类型
  21. * 如果操作的数据为集合类型(多条数据),那么返回的类型字符串应该为vnd.android.cursor.dir/开头
  22. * 例如要得到所有person记录的Uri为content://com.bravestarr.provider.personprovider/person,
  23.     * 那么返回的MIME类型字符串应该为"vnd.android.cursor.dir/person"
  24. * 如果操作的数据为单一数据,那么返回的类型字符串应该为vnd.android.cursor.item/开头
  25. * 例如要得到id为10的person记录的Uri为content://com.bravestarr.provider.personprovider/person/10,
  26.     * 那么返回的MIME类型字符串应该为"vnd.android.cursor.item/person"
  27. */
  28. public String getType(Uri uri)

这些方法中的Uri参数,得到后需要进行解析然后做对应处理,Uri表示要操作的数据,包含两部分信息:

  • 1.需要操作的contentprovider
  • 2.对contentprovider中的什么数据进行操作,一个Uri格式:结构头://authorities(域名)/路径(要操作的数据,根据业务而定)

content://com.bravestarr.provider.personprovider/person/10
说明:contentprovider的结构头已经由android规定为content://
authorities用于唯一标识这个contentprovider程序,外部调用者可以根据这个找到他。
路径表示我们要操作的数据,路径的构建根据业务而定。路径格式如下:

  • 要操作person表行号为10的记录,可以这样构建/person/10
  • 要操作person表的所有记录,可以这样构建/person

<2>在AndroidManifest.xml中使用对ContentProvider进行配置注册(内容提供者注册它自己就像网站注册域名),ContentProvider采用authoritie(原意授权,可理解为域名)作为唯一标识,方便其他应用能找到

  1. <android:authorities="com.bravestarr.provider.personprovider"/>

四大组件总结


四大组件的注册

  1. <activity> name属性指定某Activity 的子类。
  2. <service> 用于声明服务
  3. <receiver> 用于声明广播接收器
  4. <provider> 用于声明内容提供者

四大组件的激活


四大组件的关闭

内容提供者仅在响应ContentResolver 提出请求的时候激活,一个广播接收器仅在响应广播信息的时候激活。所以,没有必要去显式的关闭这些组件。

Activity关闭:可以通过调用它的finish()方法来关闭一个Activity

Service关闭:对于通过startService()方法启动的服务要调用Context.stopService()方法关闭服务,使用bindService()方法启动的服务要调用Contex.unbindService ()方法关闭服务


Activity的生命周期

介绍生命周期之前,先提一下任务的概念。任务其实就是Activity 的栈,它由一个或多个Activity组成的共同完成一个完整的用户体验, 换句话说任务就是【应用程序】。栈中保存的其实是实例对象,栈中的Activity 永远不会重排,只会压入或弹出,所以如果发生了诸如需要多个地图浏览器的情况,就会使得一个任务中出现多个同一Activity 子类的实例同时存在。

任务中的所有activity 是作为一个整体进行移动的。整个的任务(即activity 栈)可以移到前台,或退至后台。

Activity栈:先进后出规则

Android 应用程序的生命周期是由Android框架进行管理,而不是由应用程序直接控制。通常,每一个应用程序(入口一般会是一个Activity 的onCreate 方法),都会产生一个进程(Process)。当系统内存即将不足的时候,会依照优先级自动进行进程(process)的回收。不管是使用者或开发者, 都无法确定的应用程序何时会被回收。


生命周期的四种状态

Activity整个生命周期的4种状态、7个重要方法和3个嵌套循环

内存不足时,Dalvak 虚拟机会根据其内存回收规则来回收内存:

  1. 先回收与其他Activity 或Service/Intent Receiver 无关的进程(即优先回收独立的Activity)。因此建议,一些(耗时)后台操作,最好是作成Service的形式
  2. 不可见(处于Stopped状态的)Activity
  3. Service进程(除非真的没有内存可用时会被销毁)
  4. 非活动的可见的(Paused状态的)Activity
  5. 当前正在运行(Active/Running状态的)Activity

七个重要方法

当Activity从一种状态进入另一状态时系统会自动调用下面相应的方法来通知用户这种变化。


三个嵌套循环

  1. Activity完整的生命周期:从第一次调用onCreate()开始直到调用onDestroy()结束
  2. Activity的可视生命周期:从调用onStart()到相应的调用onStop()
    在这两个方法之间,可以保持显示Activity所需要的资源。如在onStart()中注册一个广播接收者监听影响你的UI的改变,在onStop() 中注销。
  3. Activity的前台生命周期:从调用onResume()到相应的调用onPause()。

举例说明:
例1:有3个Acitivity,分别用One,Two(透明的),Three(全屏)表示,One是应用启动时的主Activity

例2:横竖屏切换时候Activity的生命周期
1、新建一个Activity,并把各个生命周期打印出来
2、运行Activity,得到如下信息

onCreate-->
onStart-->
onResume-->

3、按crtl+f12切换成横屏时

onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->

4、再按crtl+f12切换成竖屏时,发现打印了两次相同的log

onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->

5、修改AndroidManifest.xml,把该Activity添加android:configChanges="orientation",执行步骤3

onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->

6、再执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged

onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onConfigurationChanged-->

7、把步骤5的android:configChanges="orientation"改成 android:configChanges="orientation|keyboardHidden",执行步骤3,就只打印onConfigChanged

onConfigurationChanged-->

8、执行步骤4

onConfigurationChanged-->
onConfigurationChanged-->

总结:

  1. 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  2. 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  3. 设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变

Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState --> onPause --> onStop,再次进入激活状态时: onRestart -->onStart--->onResume

补充:onCreate(Bundle savedInstanceState)onSaveInstanceState(Bundle savedInstanceState)配合使用,见如下代码,达到显示activity被系统杀死前的状态

  1. @Override
  2. public void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. if (null != savedInstanceState) {
  5. String _userid = savedInstanceState.getString("StrUserId");
  6. String _uid = savedInstanceState.getString("StrUid");
  7. String _serverid = savedInstanceState.getString("StrServerId");
  8. String _servername = savedInstanceState.getString("StrServerName");
  9. int _rate = savedInstanceState.getInt("StrRate");
  10. //updateUserId(_userid);
  11. //updateUId(_uid);
  12. //updateServerId(_serverid);
  13. //updateUserServer(_servername);
  14. //updateRate(_rate);
  15. }
  16. }
  17. @Override
  18. protected void onSaveInstanceState(Bundle savedInstanceState) {
  19. super.onSaveInstanceState(savedInstanceState);
  20. savedInstanceState.putString("StrUserId", getUserId());
  21. savedInstanceState.putString("StrUid", getUId());
  22. savedInstanceState.putString("StrServerId", getServerId());
  23. savedInstanceState.putString("StrServerName", getServerName());
  24. savedInstanceState.putInt("StrRate", getRate());
  25. }

引发Activity摧毁和重建的其他情形:
除了系统处于内存不足的原因会摧毁Activity之外,某些系统设置的改变也会导致Activity的摧毁和重建. 例如改变屏幕方向(见上例),改变设备语言设定, 键盘弹出等。


BroadcastReceive生命周期:

生命周期只有十秒左右,如果在onReceive()内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息。
它的生命周期为从回调onReceive()方法开始到该方法返回结果后结束。


Service生命周期:

Service完整的生命周期:从调用onCreate()开始直到调用onDestroy()结束

Service有两种使用方法:
1. 以调用Context.startService()启动,而以调用Context.stopService()结束
2. 以调用Context.bindService()方法建立,以调用Context.unbindService()关闭


LayoutInflater

LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化。
findViewById()是找xml布局文件下的具体widget控件(如Button、TextView等)。
具体作用:

  • 1、对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来载入;
  • 2、对于一个已经载入的界面,就可以使用Activiyt.findViewById()方法来获得其中的界面元素。

LayoutInflater 是一个抽象类,在文档中如下声明:
public abstract class LayoutInflater extends Object
获得 LayoutInflater 实例的三种方式
1. LayoutInflater inflater = getLayoutInflater();//调用Activity的getLayoutInflater()
2. LayoutInflater inflater = LayoutInflater.from(context);
3. LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);

其实,这三种方式本质是相同的,从源码中可以看出:
这三种方式最终本质是都是调用的Context.getSystemService()

另外getSystemService()是Android很重要的一个API,它是Activity的一个方法,根据传入的NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。

传入的Name 返回的对象 说明
WINDOW_SERVICE WindowManager 管理打开的窗口程序
LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view
ACTIVITY_SERVICE ActivityManager 管理应用程序的系统状态
POWER_SERVICE PowerManger 电源的服务
ALARM_SERVICE AlarmManager 闹钟的服务
NOTIFICATION_SERVICE NotificationManager 状态栏的服务
KEYGUARD_SERVICE KeyguardManager 键盘锁的服务
LOCATION_SERVICE LocationManager 位置的服务,如GPS
SEARCH_SERVICE SearchManager 搜索的服务
VEBRATOR_SERVICE Vebrator 手机震动的服务
CONNECTIVITY_SERVICE Connectivity 网络连接的服务
WIFI_SERVICE WifiManager Wi-Fi服务
TELEPHONY_SERVICE TeleponyManager 电话服务

inflate 方法
通过 sdk 的 api 文档,可以知道该方法有以下几种重载形式,返回值均是 View 对象,如下:

  1. public View inflate (int resource, ViewGroup root)
  2. public View inflate (XmlPullParser parser, ViewGroup root)
  3. public View inflate (XmlPullParser parser, ViewGroup root, boolean attachToRoot)
  4. public View inflate (int resource, ViewGroup root, boolean attachToRoot)

示意代码:

  1. LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
  2. View view = inflater.inflate(R.layout.custom, (ViewGroup)findViewById(R.id.test));
  3. EditText editText = (EditText)view.findViewById(R.id.content);

对于上面代码,指定了第二个参数 ViewGroup root,当然你也可以设置为 null 值。
注意:

  • inflate 方法与 findViewById 方法不同;
  • inflater 是用来找 res/layout 下的 xml 布局文件,并且实例化;
  • findViewById() 是找具体 xml 布局文件中的具体 widget 控件(如:Button、TextView 等)。

XML 布局属性 收集


颜色 收集


Manager

LocationManager 基于位置的服务

获取Location对象

<1>获取LocationManager实例:

  1. LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

位置提供器:

  • GPS_PROVIDER
  • NETWORK_PROVIDER
  • PASSIVE_PROVIDER

<2>获取Location对象:

  1. String provider = LocationManager.NETWORK_PROVIDER;
  2. Location location = locationManager.getLastKnownLocation(provider);

这个location对象中包含了经度、纬度、海拔等信息。

eg:
需要权限<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

  1. package com.example.locationtest;
  2. import java.util.List;
  3. import android.app.Activity;
  4. import android.content.Context;
  5. import android.location.Location;
  6. import android.location.LocationListener;
  7. import android.location.LocationManager;
  8. import android.os.Bundle;
  9. import android.widget.TextView;
  10. import android.widget.Toast;
  11. public class MainActivity extends Activity {
  12. private TextView positionTextView;
  13. private LocationManager locationManager;
  14. private String provider;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_main);
  19. positionTextView = (TextView)findViewById(R.id.position_text_view);
  20. locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
  21. // 获取所有可用的位置提供器
  22. List<String> providerList = locationManager.getProviders(true);
  23. if (providerList.contains(LocationManager.GPS_PROVIDER)){
  24. provider = LocationManager.GPS_PROVIDER;
  25. } else if (providerList.contains(LocationManager.NETWORK_PROVIDER)){
  26. provider = LocationManager.NETWORK_PROVIDER;
  27. } else {
  28. // 无可用位置提供器
  29. Toast.makeText(this, "No location provider to use", Toast.LENGTH_SHORT).show();
  30. return;
  31. }
  32. Location location = locationManager.getLastKnownLocation(provider);
  33. if (location != null) {
  34. // 显示当前设置的位置信息
  35. showLocation(location);
  36. }
  37. locationManager.requestLocationUpdates(provider, 5000, 1, locationListener);
  38. }
  39. @Override
  40. protected void onDestroy() {
  41. super.onDestroy();
  42. if (locationManager != null){
  43. // 关闭程序时将监听器移除
  44. locationManager.removeUpdates(locationListener);
  45. }
  46. }
  47. LocationListener locationListener = new LocationListener() {
  48. @Override
  49. public void onStatusChanged(String provider, int status, Bundle extras) {
  50. }
  51. @Override
  52. public void onProviderEnabled(String provider) {
  53. }
  54. @Override
  55. public void onProviderDisabled(String provider) {
  56. }
  57. @Override
  58. public void onLocationChanged(Location location) {
  59. // 更新当前设备的位置信息
  60. showLocation(location);
  61. }
  62. };
  63. private void showLocation(Location location){
  64. String currentPosition = "latitude is " + location.getLatitude() + "\n"
  65. + "longitude is " + location.getLongitude();
  66. positionTextView.setText(currentPosition);
  67. }
  68. }

反向地理编码

Geocoding API:向Google服务器发起一条HTTP请求:

  1. http://maps.googleapis.com/maps/api/geocode/json?latlng=30.280505,120.147231&sensor=false

json可换成xml
latlng后是经纬度
sensor通常用false

联网需要权限:
<uses-permission android:name="android.permission.INTERNET"/>

解析地理位置的简单方法:

  1. private void showLocation(final Location location){
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. try{
  6. // 组装反向地理编码的接口地址
  7. StringBuilder url = new StringBuilder();
  8. url.append("http://maps.googleapis.com/maps/api/geocode/json?latlng=")
  9. .append(location.getLatitude())
  10. .append(".")
  11. .append(location.getLongitude())
  12. .append("&sensor=false");
  13. HttpClient httpClient = new DefaultHttpClient();
  14. HttpGet httpGet = new HttpGet(url.toString());
  15. // 在消息请求头中指定语言,保证服务器会返回中文数据
  16. httpGet.addHeader("Accept-Language", "zh-CN");
  17. HttpResponse httpResponse = httpClient.execute(httpGet);
  18. if (httpResponse.getStatusLine().getStatusCode() == 200) {
  19. HttpEntity entity = httpResponse.getEntity();
  20. String response = EntityUtils.toString(entity, "utf-8");
  21. JSONObject jsonObject = new JSONObject(response);
  22. JSONArray resultArray = jsonObject.getJSONArray("results");
  23. if (resultArray.length() > 0) {
  24. JSONObject subObject = resultArray.getJSONObject(0);
  25. // 取出格式化后的位置信息
  26. String address = subObject.getString("formatted_address");
  27. Message message = new Message();
  28. message.what = SHOW_LOCATION;
  29. message.obj = address;
  30. handler.sendMessage(message);
  31. }
  32. }
  33. } catch (Exception e){
  34. e.printStackTrace();
  35. }
  36. }
  37. }).start();
  38. }
  39. private Handler handler = new Handler(){
  40. public void handleMessage(Message msg){
  41. switch (msg.what){
  42. case SHOW_LOCATION:
  43. String currentPosistion = (String)msg.obj;
  44. positionTextView.setText(currentPosistion);
  45. break;
  46. default:
  47. break;
  48. }
  49. }
  50. };

传感器

<1> 获取SensorManager的实例

  1. SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);

<2>调用getDefaultSensor()得到任意传感器类型

  1. Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

上例使用了光照传感器

<3>对传感器的输出信号进行监听
创建SensorEventListener实例

  1. SensorEventListener listener = new SensorEventListener(){
  2. @Override
  3. public void onAccuracyChanged(Sensor sensor, int accuracy){
  4. }
  5. @Override
  6. public void onSensorChanged(SensorEvent event){
  7. }

使用SensorManager的registerListener()方法注册。

  1. sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);

<4>传感器使用完毕时要unregistenrListener()

光照传感器


数据库SQLite

<1>先创建一个类继承SQLiteOpenHelper

  1. public class KingWeatherOpenHelper extends SQLiteOpenHelper {
  2. /**
  3. * Province建表语句
  4. */
  5. public static final String CREATE_PROVINCE = "create table Province ("
  6. + "id integer primary key autoincrement, "
  7. + "province_name text, "
  8. + "province_code text)";
  9. public KingWeatherOpenHelper(Context context, String name,
  10. CursorFactory factory, int version) {
  11. super(context, name, factory, version);
  12. }
  13. @Override
  14. public void onCreate(SQLiteDatabase db) {
  15. db.execSQL(CREATE_PROVINCE);
  16. db.execSQL(CREATE_CITY);
  17. db.execSQL(CREATE_COUNTY);
  18. }
  19. @Override
  20. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  21. }
  22. }

<2>创建数据库

  1. KingWeatherOpenHelper dbHelper = new KingWeatherOpenHelper(context, databaseName, null, databaseVersion);
  2. SQLiteDatabase db = dbHelper.getWritableDatabase();
  3. // 或者 getReadableDatabase();

UI


常用控件


ListView

……

  1. public View getView(int position, View convertView, ViewGroup parent)

ArrayAdapter

  • getView()`方法中有一个converView参数,用于将之前加载好的布局进行缓存,以便复用。
  • notifyDataSetChanged():刷新ListView中的显示

View中的setTag(Object o)表示给View添加一个格外的数据,以后可以用getTag()将这个数据取出来。


单位和尺寸

  • px:像素
  • pt:磅数。1 pt = 1/72 英寸,一般作为字体单位
  • dp:又称dip,密度无关像素。160 dpi 的屏幕上,1 dp 等于 1 px。即 px = dp*(dpi/160);
  • sp:可伸缩像素。原理和dp一样,用于字体。

Nine-Patch 图片

被特殊处理过的png图片,能够指定哪些区域可以被拉伸而哪些区域不可以。
sdk目录下tools文件夹中的draw9patch.bat文件。
上、左:拉伸区域
下、右:内容区域


状态开头按钮ToggleButton与开关Switch

ToggleButton 与 Switch 都是由Button派生出来。

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical" >
  5. <!-- 定义一个ToggleButton按钮 -->
  6. <ToggleButton
  7. android:id="@+id/toggle"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:textOff="横向排列"
  11. android:textOn="纵向排列"
  12. android:checked="true"
  13. />
  14. <Switch
  15. android:id="@+id/switcher"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:textOff="横向排列"
  19. android:textOn="纵向排列"
  20. android:checked="true"
  21. />
  22. <LinearLayout
  23. android:id="@+id/test"
  24. android:layout_width="match_parent"
  25. android:layout_height="match_parent"
  26. android:layout_gravity="center_vertical"
  27. android:orientation="vertical"
  28. >
  29. <Button
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. android:text="测试按钮一"
  33. android:textSize="16sp"
  34. />
  35. <Button
  36. android:layout_width="wrap_content"
  37. android:layout_height="wrap_content"
  38. android:text="测试按钮二"
  39. android:textSize="16sp"
  40. />
  41. <Button
  42. android:layout_width="wrap_content"
  43. android:layout_height="wrap_content"
  44. android:text="测试按钮三"
  45. android:textSize="16sp"
  46. />
  47. </LinearLayout>
  48. </LinearLayout>
  1. package com.example.test;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.widget.CompoundButton.OnCheckedChangeListener;
  5. import android.widget.CompoundButton;
  6. import android.widget.LinearLayout;
  7. import android.widget.Switch;
  8. import android.widget.ToggleButton;
  9. public class MainActivity extends Activity {
  10. ToggleButton toggle;
  11. Switch switcher;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_main);
  16. toggle = (ToggleButton)findViewById(R.id.toggle);
  17. switcher = (Switch)findViewById(R.id.switcher);
  18. final LinearLayout test = (LinearLayout)findViewById(R.id.test);
  19. OnCheckedChangeListener listener = new OnCheckedChangeListener() {
  20. @Override
  21. public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
  22. if (isChecked) {
  23. // 垂直布局
  24. test.setOrientation(1);
  25. } else {
  26. // 水平布局
  27. test.setOrientation(0);
  28. }
  29. }
  30. };
  31. toggle.setOnCheckedChangeListener(listener);
  32. switcher.setOnCheckedChangeListener(listener);
  33. }
  34. }

时钟AnalogClock和DigitalClock

都会显示当前时间。

数字时钟DigitalClock继承了TextView,可以显示秒数
模拟时钟AnalogClock继承了View,重写了onDraw方法

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:gravity="center_horizontal"
  5. android:orientation="vertical" >
  6. <!-- 定义模拟时钟 -->
  7. <AnalogClock
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. />
  11. <!-- 定义数字时钟 -->
  12. <DigitalClock
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:textSize="14sp"
  16. android:textColor="#f0f"
  17. android:drawableRight="@drawable/ic_launcher"
  18. />
  19. </LinearLayout>

计时器Chronometer

与DigitalClock一样都继承自TextView。
区别是Chronometer显示从某个起始时间开始,一共过去了多长时间。

android:format属性用于指定计时器的计时格式。

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical" >
  5. <Chronometer
  6. android:id="@+id/test"
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:layout_gravity="center_horizontal"
  10. android:textSize="24sp"
  11. />
  12. <Button
  13. android:id="@+id/start"
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:layout_gravity="center_horizontal"
  17. android:text="开始计时"
  18. android:textSize="24sp"
  19. />
  20. </LinearLayout>
  1. public class MainActivity extends Activity {
  2. Chronometer ch;
  3. Button start;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. ch = (Chronometer) findViewById(R.id.test);
  9. start = (Button) findViewById(R.id.start);
  10. start.setOnClickListener(new OnClickListener() {
  11. @Override
  12. public void onClick(View source) {
  13. // 设置开始时间
  14. ch.setBase(SystemClock.elapsedRealtime());
  15. // 启动计时器
  16. ch.start();
  17. start.setEnabled(false);
  18. start.setText("计时中...");
  19. }
  20. });
  21. ch.setOnChronometerTickListener(new OnChronometerTickListener() {
  22. @Override
  23. public void onChronometerTick(Chronometer chronometer) {
  24. // 如果从开始计时到现在超过了20s
  25. if ((SystemClock.elapsedRealtime() - ch.getBase() ) > 20 * 1000){
  26. ch.stop();
  27. start.setEnabled(true);
  28. start.setText("开始计时");
  29. }
  30. }
  31. });
  32. }
  33. }

ImageView及其子类

ImageView继承自View,用于显示图片和Drawable对象,派生了ImageButton、ZoomButton等组件


ImageButton

Button按钮上显示文字
ImageButton上则显示图片,为它指定android:text属性没用。可以指定android:src属性,可以使用静止图片或Drawable对象。

ImageButton派出了一个ZoomButton,可以代表放大、缩小两个按钮。默认提供了btn_minus、btn_plus两个Drawable资源。

Android还提供了一个ZoomControls组件,相当于同时组合了放大、缩小两个按钮,并允许分别为两个按钮绑定不同的事件监听器。

QuickContactBadge 继承了 ImageView ,额外增加的功能是该图片可以关联到手机中指定联系人,当用户单击该图片时,系统将打开相应联系人的联系方式界面。
调用如下方法进行关联:

  • assignContactFromEmail(String emailAddress, boolean lazyLookup)
  • assignContactFromPhone(String phontNumber, boolean lazyLookup)
  • assignContactUri(Uri contactUri)

《疯狂安卓讲义》PDF P105

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注