android:process=":messengerService" />
1.2.2 Activity管理机制
android的管理是通过Activity栈和Task来进行的。
1. Activity栈
android的管理主要通过android栈来进行的。当一个activity启动时,系统会根据配置或调用方式,将activity压入一个特定的栈中,系统处于运行状态,当按下Back键或触发finish()方法时,activity会从栈中被压出,进而被销毁,到那个有新的activity压入栈时,如果原activity仍可见,则原activity的状态变为暂停状态,如果activity完全被遮挡,则其状态变为停止。
2.Task
Task与activity栈有密切的关系。一个Task对应一个activity栈,Task是根据用户体验组成的运行期逻辑单元,其与应用的区别在于,Task中的activity可以由不同的应用组成。在实际的终端使用中,在主界面长按Home键弹出一个网格界面即是当前运行的Task而非应用。
Task的定义为与 framework/base/services/java/com/androd/server/am目录下的TaskRecord.java中,一个Task由tasked , affinity, clearOnBackground, intent , affinityIntent, origActiivty, realAcitvity, numActivities, lastActiveTime, rootWasReset, stringName等属性构成。
在activity中,有不少属性与Task 相关,如 android:allowTaskReparenting, android:taskAffinity等。
(1)Task间移动配置
android:allowTaskReparenting 属性用来配置是否允许activity从启动它的Task移动到和该Activity设置的TaskI亲和性相同的Task中,
(2)Task状态设置
android:alwaysRetainTaskState 属性用于配置是否保留Activity所在的Task状态, 默认为false
clearTaskOnLaunch 当Task从主界面重新启动时,是否需要清除除根activity外的的所有activity,默认为false
finishOnTaskLaunch 当Task从主界面重新启动时,特定的activity是否需要被销毁,默认为false
(3)Task亲和性
Task亲和性,由android:taskAffinity属性定义。如果希望activity启动时,运行在特定的Task中,必须显式设置这个属性。
注意:只有通过标志为FLAG_ACTIVITY_NEW_TASK的intent启动activity时,该activity的android:taskAffinity属性才有效,系统才会将就要有相同Task亲和性的Task 切换到前台,然后启动该activity,否则该activity忍让运行咋启动它的task中。
1.2.3 Broadcast机制
广播涉及顺序广播,无序广播,广播接收器等概念。
1. 顺序广播
当广播需要以类似消息链的方式进行时,应采用顺序广播,顺序广播的接收器可以抛弃或继续传递消息。具体事例如下:
Intent LlcpLinkIntent = new Intent();
LlcpLinkIntent.setAction(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED);
LlcpLinkIntent.putExtra(NfcAdapter.EXTRA_LLCP_LINK_STATE_CHANGED, NfcAdapter.LLCP_LINK_STATE_ACTIVATED);
mContext.sendOrderedBroadcast(LlcpLinkIntent, NFC_PERM );
2. 无序广播
无序广播是异步的,广播接收器除了接收广播外,无法对无序广播的行为产生影响。下面示例:
Intent intent = new Intent(Intent.ACTION_ARIPLANE_MODE_CHANGED);
intent.putExtra("state", enabling);
mContext.sendBroadcast(intent);
3.广播接收器
为了接收广播,必须在AndroidManifest.xml中配置广播接收器或通过java实现广播接收器,配置方法:
代码中配置:
IntentFilter bootFiler = new IntentFilter( Intent.ACTION_BOOT_COMPLETED );
mContext.registerReceiver( monitor, bootFiler );
1.2.4 对话框框架
在android中,目前有4中创建对话框的方式,分别为AlertDialog, ProgressDialog, DatePickerDialog, TimePickerDialog。 其中AlertDialog是最通用的对话框形式, ProgressDialog用于描述进度信息,后两者主要用于日期和时间的场景中。
1. AlertDialog
默认情况下,通过setMessage()来设置显示的文字信息,通过setView()加载视图。创建对话框通过AlertDialog.Builder进行。
创建对话框的一般步骤:
>定义对话框的ID,备用;
>在onCreateDialog()中创建对话框
>通过showDialog()显示对话框
>通过dismissDialog()隐藏对话框
在某场景下,由于正在执行无法中断的计算,弹出的对话框不希望被用户取消,在可使用下列方法设置:
public void setCancelable(boolean flag) //使用于所有对话框
由于Dialog管理机制问题,Dialog具备记忆功能,这在需要数据更新的场景中,稍麻烦,解决方法是在 onPrepareDialog() 方法中进行数据更新。
注意: 由于无法在对话框队列中记忆对话框状态,dismissDialog()方法必须和showDialog()成对出现,若无showDialog()与之配对会发生异常。
2. ProgressDialog
进度对话框,常用于耗时的操作,应将Progress Dialog 放在主线程之外执行,否则极易出现androidANR消息。常见的形式如下:
ProgressDialog mWaitDialog = new ProgressDialog();
mWaitDialog.setMessage(getString(R.string.waiting))
;
mWaitDialog.setIndeterminate(true);
mWaitDialog.setCancelable(false);
完整的进度对话框还需要定义一个ID, showDialog(iD); dismissDialog(ID);
若开发着不希望用户通过Back键手动销毁对话框,可进行设置 mWaitDialog.setCancelable(true);
若希望能监听进度对话框取消的消息,可如下实现:
mWaitDialog.setOnCancelListener( new OnCancelListener() ){
public void onCancel(DialogInterface dialog){ ....... }
}
进度条支持两种风格的进度显示:一种是进度条,一种是环形转动。这两种进度显示对应的风格分贝为 ProgressDialog.STYLE_HORIZONTAL 和 ProgressDialog.STYLE_SPINNER, 默认的风格为后者,设置方法:
mProgressDialog.setProgressStyle( ProgressDialog.STYLE_HORIZONTAL );
ProgressDialog支持的进度复读为 0 ~~~10000
其同时支持主进度和辅进度,其本质是是实现对ProgressBar的封装。
3. DatePickerDialog
日期对话框,创建时通常需要设置初始年,月,日和监控日期变化的回调函数。
DatePickerDialog(this, mDateSetListener, mYear, mMonth, mDay);
下面是日期对话框回调函数的实现:
private DatePickerDialog.onDateSetListener mDateSerListener = new DatePickerDialog.OnDateSerListener(){
public void OnDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth){
mYear = year; mMonth = monthOfYear; mDay = dayOfMonth; updateDisplay();
}
};
代码中更新日期对话框的显示,方法: dialog.updateTime(mYear, mMonth, mDay);
4. TimePickerDialog
方法类似DatePickerDialog
1.2.5 标题栏框架
目前标题栏显示支持进度显示,允许用户隐藏,自定义标题栏。目前android标题栏支持FEATURE_NO_TITLE, FEATURE_PROGRESS, FEATURE_LEFT_ICON, FEATURE_RIGHT_ICON, FEATURE_INDETERMINATE_PROGRESS, 等多种定制。
1. 隐藏标题栏
两种实现方式,在AndroidManifest.xml中和代码中实现
xml中,在activity中添加 android:theme="@android:style/Theme.NoTitleBar"
代码中, requestWindwoFeature(android.view.Window.FEATURE_NO_TITLE);
2. 自定义标题栏
注意标题栏的FEATURE_CUSTOM_TITLE 不能和FEATURE_LEFT_ICON, FEATURE_NO_TITLE等同时使用,下面是设置方法:
requestWindowFeature( Window.FEATURE_CUSTOM_TITLE );
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_1 );
3. 进度显示
android中支持主进度和辅进度两种进度显示,辅进度在本地应用中较少使用。在网络环境中,如在进行流媒体播放时,可以用主进度表示播放进度,用辅进度标示下载进度。二者显示范围为 0~~100。 如下示例:
requestWindowFeature(Window.FEATURE_PROGRESS);
setProgressBarVisibility(true);
setProgress(progressHorizontal.getProgress() * 100);
setSecondaryProgress(progressHorizontal.getSecondaryProgress() * 100);
实际开发中,无法使用明确的进度显示,如向服务器发送请求后,等待反馈的过程,这时就用到了不定进程显示,下面是不定进程显示示例:
requestWindowFeature( Window.FEATURE_INDETERMINATE_PROGRESS );
setProgressBarIndeterminateVisibility(true);
4. 图标显示
标题栏又标题,进度条,左标题,右标题构成。下面以左标题为例介绍:
requestWindowFeature( Window.FEATURE_LEFT_ICON );
getWindow().setFeatureDrawableResource( Window.FEATURE_LEFT_ICON, R.drawable.ic_list_bookmark );
1.2.6 状态栏框架
与传统终端状态栏一样,android状态栏提供电量信息,蜂窝信息,SMS, MMS, 邮件,WIFI信号,蓝牙信号,闹钟等系统的状态信息。另外状态栏还有通知栏的功能。
其中,起主要作用的是 StatusBarPolicy, 它承担着接收系统发来的Intent的信息,更新状态显示的功能,他是服务StatusBarManagerService的客户端。
StatusBarManagerService在创建时,会加载config_statusBarIcons数组。在framework\base\core\res\res\values目录下,config.xml中定义了config_statusBarIcons数组确定了状态图标的加载顺序。
整个状态栏框架是通过StatusBarService来实现。在StatusBarService初始化时,初始化了一个用于显示statusbar的StatusBarView。 在StatusBarView中定义了状态栏的实现布局,而具体的布局问及那是在framework\base\packages\systemui\res\layout\status_bar.xml实现的。
1. 状态栏的隐藏
两种方式: AndroidManifest.xml中实现: android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
代码中实现: getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN );
requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题栏
上述隐藏方式值适合静态场景,在隐藏标题栏后再动态显示状态栏已经超出以上两种方法的能力。 此时可通过鞋面方法动态的隐藏和系那是状态栏:
getWindow(). addFlags ( WindowManager.LayoutParams.FLAG_FULLSCREEN ); //隐藏
getWindow(). clearFlags ( WindowManager.LayoutParams.FLAG_FULLSCREEN ); //显示
2. 电量显示
当StatusBarPolicy 收到Action为ACTION_BATTERY_CHANGED的Intent时,StatusBarPolicy会通知StatusBarManager进行电量图标的更新。同时还响应ACTION_BATTERY_LOW, ACTION_BATTERY_OKAY, ACTION_POWER_CONNECTED的intent。
3. 蜂窝信息
android对蜂窝协议的支持十分充分,目前支持GSM, UMTS, CDMA, 4G等。
4.WIFI信息
对于wifi信号,android可以响应Action为 WifiManager.NETWORK_STATE_CHANGED_ACTION, WifiManager.WIFI_STATE_CHAGED_ACTION, WifiManager.RSSI_CHANGED_ACTION的intent, 相应的更新方法: updateWifi()
5.蓝牙信息
对蓝牙信息,android可以响应BluetoothAdapter, BluetoothHeadset, BluetoothA2dp 和 BluetoothPbap的状态变化,相应的更新方法: updateBluetooth();
1.2.7 通知机制
多种方式向用户反馈系统状态信息,Toast提醒,通知栏提醒,对话框提醒的等。其中Toast常用于页面的显示,通知栏提醒常用于交互事件的通知,一般非常重要的通知以对话框的形式给出。
Toast 和Notification 均由框架层的 NotificationManagerService维护
1.Toast
简单提示用户信息,常只显示文本。也可以自定义。
自定义位置: toast.setGravity( Gravity.TOP|Gravity.LEFT, 0, 0 );
自定义视图: LinearLayout dialog = (LinearLayout)LayoutInflater.from(this).inflate(R.layout.retry_sending_dialog, null);
Toast undeliveredDialog = new Toast(this);
undeliveredDialog.setView(dialog);
undeliveredDialog.setDuration(Toast.LENGTH_LONG);
undeliveredDialog.show();
注意: Toast不能在AsyncTask的doInBackground()方法中运行,如果要实现类似的效果,可在Handler 中进行处理。
2. Notification
适合于交互事件的通知,常用于短消息,即时消息,下载,外围设备的状态变化的场景中。
Notification支持文字显示,振动,三色灯,振铃音等提示形式,默认情况下,仅显示消息标题,消息内容和时间。下面是一个基本实现:
NotificationManager nm = (NotificationManager)getSystemService( NOTIFICATION_SERVICE );
CharSequence from = "Joe";
CharSequence message = "kthx.meet u for dinner. cul";
PendingIntent contentIntent = PendingIntent.getActivity(this, 0 , new Intent(this, IncomingMessageView.class), 0);
String tickerText = getString(R.string.incoming_message_ticker_text, message);
Notification notif = new Notification(R.drawable.stat_sample, tickerText, System.currentTimeMillis());
notif.setLatestEventInfo(this, from, message, contentIntent);
notif.vibrate = new Long[]{100, 250, 100, 500};
nm.notify(R.string.incoming_message_ticker_text, notify);
(1) Notification 管理
android通过标示符来管理Notification,发起一个Notification的方法如下;
notificationManager.notify( notificationId, mNotification );
取消Notification的方法有很多,如果希望用户单击后Notificatino即被清除,则相应的方法如下:
notification.flags |= FLAG_AUTO_CANCEL;
如果希望手动清除某项,相应的方法: mNotificationMgr.cancel ( LOW_MEMORY_NOTIFICATION_ID );
若希望清除所有Notification时,相应的方法: mNotificationMgr.cancelAll(); (2)振动提醒
通常用于比较紧迫的场景,示例如下:
Notification n = new Notification();
n.vibrate = new Long[]{ 0, 700, 500, 1000 } ;//振动方式:延迟0ms,然后振动700ms,接着振动1000ms
mNM.notify( 1, n );
若希望设置为默认的振动方式,相应的方法: notification.defaults |= Notification.DEFAULT_VIBRATE ;
(3)三色灯提醒
只有设置了Notification的标志位为FLAG_SHOW_LIGHTS,才能支持三色灯提醒。创建三色灯提醒的Notification示例如下:
Notification n = new Notification();
n.flags |= Notification.FLAG_SHOW_LIGHTS;
n.ledARGB = 0xff0000ff;
n.ledOnMS = 300;
n.ledOffMS = 300;
mNM.notify( 1, n );
若希望设置默认三色灯提醒,相应的方法: notification.defaluts |= Notification.DEFAULT_LIGHTS;
(4)振铃声提醒
Notification支持默认铃声,自定义铃声,android多媒体数据库等多种提醒方式,相应的配置方法:
notification.defaults |= Notification.DEFAULT_SOUND; //默认铃声
notification.sound = Uri.prase( "file:///sdcard/notification/ringer.mp3" ); //自定义铃声
notification.sound = Uri.withAppendedPath( Audio.Media.INTERNAL_CONTENT_URI, "6" ); //基于android多媒体数据库的提醒方式
(5)提醒标志位
Notification支持FLAG_SHOW_LIGHTS三色灯提醒, FLAG_ONGOING_EVENT发起事件, FLAG_INSISTENT振铃音将持续到Notification取消或Notification窗口打开 , FLAG_ONLY_ALERT_ONCE发起Notification后,振铃音或震动均只执行一次, FLAT_AUTO_CANCEL用户单击后自动消失, FLAG_NO_CLEAR全部清除时,Notification才会清除,FLAG_FOREGROUND_SERVICE表示正运行的服务 等多种标志位提醒
(6)自定义视图
自定义视图的布局文件中,仅支持FrameLayout, LinearLayout, RelativeLayout等布局控件。自定义视图步骤如下:
1》 创建自定义视图, 考虑到通知栏的兼容性,自定义视图需避免复杂的设计
2》 获取远程视图对象, 操作自定义视图,必须获取远程视图对象,并将远程视图对象和Notification关联起来,方法如下:
RemoteViews expandedView = new RemoteViews(Constants.THIS_PACKAGE_NAME, R.layout.status_bar_ongoing_event_progrss_bar);
expandedView.setTextViewText( R.id.descriptioni, item.description );
expandedView .setProgressBar( ...... );
Notification n = new Notification();
expandedView .setImageResource(R.id.appIcon, android.R.drawable.stae_sys_download);
n.flags |= Notification.FLAG_ONGOING_EVENT;
n.contentView = expandedView;
3》设置PendingIntent, 目前Notification支持多种Intent来响应单击事件,清除事件,处理紧急情况的全屏事件等。
为了在Notification被单击时能响应事件,需要设置Notification的contentIntent变量,响应的方法:
Intent intent = new Intent(Contents.ACTION_LIST);
intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
intent.setData(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
n.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
在执行清除全部的Notification 操作时,通过设置Notification的deleteInten变量可以响应这一件事,响应的方法:
Intent deleteIntent = new Intent();
deleteIntent.setClass(context, AlertReceiver.class);
deleteIntent.setAction(DELETE_ACTION);
notification.deleteIntent = PendingIntent.getBroadcast(context, 0 , deleteIntent, 0 );
为了响应紧急实际事件,需要设置Notification的fullScreenIntent 变量,响应的方法:
4》 发起Notification
发起方法: private static final int HELLO_ID = 1;
mNotificationManager.notify(
HELLO_ID, notification );
1.2.8 搜索框架
为了实现搜素,开发者,需要完成的4方面的工作:
实现搜素配置的文件; 实现显示搜索结果的activity; 实现执行搜素的算法; 发起搜素
1. 实现搜素的配置文件
搜素配置文件存储于res\xml\目录下,并命名为 searchable.xml,搜素配置文件的属性定义 如下:
http://www.cnblogs.com/over140/archive/2011/12/29/2305650.html 含义解释链接
目前android支持的搜素模式包括 showSearchLabelAsBadge, showSearchIconAsBadge(已被弃用), queryRewriteFromData, queryRewriteFromText, 。根据需要进行属性设置。如下为android的一个属性配置文件:
android:label="@string/search_label"
android:hint="#string/search_hint"
android:searchMode="showSearchLabelAsBadge"
android:voiceLanguageModel="free_form"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:voicePromptText="@string/search_invoke"
android:searchSuggestAuthority="com.example.android.apis.SuggestionProvider"
android:searchSuggestSelection="?"
/>
2. 实现显示搜索结果的Activity
为了接收搜索管理器返回的搜素关键字,在AndroidManifest.xml中必须配置相应的Intent过滤器和元数据。实例如下:
android:label="@string/search_query_results">
android:resource="@xml/searchable" />
如果应用中的所有Activity都支持搜素,那么可以在AndroidManifest.xml中进行如下配置:
android:value="SearchQueryResult" />
接收数据的方法如下:
Intent queryIntent = getIntent();
String queryAction = queryIntent.getAction();
if( Intent.ACTION_SEARCH.equals(queryAction) ){
String queryString = queryIntent.getStringExtra(SearchManager.QUERY); //获取关键字
}
3. 实现执行搜素的算法
在执行搜素时,由于搜素的目标和环境不同很难规范,故在Android中,提供了一个支持数据库搜索的内容提供器,即SearchRecentSuggestionsProvider。
如果希望android执行全局搜索时,数据能够被扫描到,需要配置相关的阅读权限。下面是彩信中的一个实现:
android:readPermission="android.permission.READ_SMS"
android:authorities="com.android.mms.SuggestionsProvider" >
android:readPermission="android.permission.GLOBAL_SEARCH" />
android:readPermission="android.permission.GLOBAL_SEARCH" />
4. 发起搜素
可以通过搜素按键或菜单等入口发起搜素。默认情况下,
调用onSearchRequested()方法即可发起搜素 。
希望自定义搜素请求,可重载onSearchRequested()方法接口即可。
为了调用搜素对话框,可使用startSearch()方法 。
1.2.9 ActionBar框架
actionbar的布局:
Action项的执行和普通菜单一样,也是
通过onOptionsItemSelected()方法进行 的。还引入了
通过setDisplayHomeAsUpEnable()方法可以激活ActionBar中应用图标对单击事件的响应 ,响应的方法如下:
actionbar.setDisplayHomeAsUpEnabled(true);
通过ActionBar还可以设置自定义的视图,即ActionView,本质是菜单的一种实现。
在ActionBar中,应用图标对单击事件的响应也是通过onOptionsItemSelected()方法进行的,其对应的ID为 android.R.id.home. ActionBar通常需要和Fragment交互。
1. 隐藏ActionBar
两种方式: AndroidManifest.xml中设置activity的theme属性可隐藏ActionBar,方法如下:
代码中实现; ActionBar actionbar = getActionBar(); actionbar.hide(); actionbar.show();
2. action项管理
Acion本质是特殊的菜单项,有图标和文字组成。Action项有4中属性可配置,分别为:
SHOW_AS_ACTION_ALWAYS总作为action项显示 SHOW_AS_ACTION_NEVER永远不作为action项显示
SHOW_AS_ACTION_IF_ROOM控件足够时显示 SHOW_AS_ACTION_WITH_TEST显示action项的文字部分
(1)利用配置文件配置action项
Action项在菜单配置文件中的配置和普通菜单项的区别在于需要设置showAsAction属性:
-
android:icon="@drawable/ic_menu_save"
android:title="@string/menu_save"
android:showAsAction="ifRoom|withText" />
(2)利用java代码配置action项
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
menu.add("Menu la").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
3. ActionView
ActionView即在ActionBar上出现的Widget,用于实现一些快捷的操作,实现有两种方式:
无论是加载布局文件还是加载视图类,均可通过配置文件实现。下面是加载布局文件的实例:
-
android:title="Search"
andrid:showAsAction="ifRoom"
android:actionLayout="@layout/searchview"
/>
下面是加载视图类的实例:
其他属性同上,将 actionLayout 属性去掉,添加:
android;actionViewClass="android.widget.SearchView"
为了操作ActionView,先获得其句柄: SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
4. 添加Tab页
ActionBar可以显示Tab页,在Activity中进行Fragment间切换,每个Tab可以包含的元素有标题和图标,类似于TabWidget,向ActionBar中添加Tab页的步骤为:
(1)创建ActionBar.TabListener, 并实现其方法
(2)设置ActionBar的导航模式为 NAVIGATION_MODE_TABS
(3)创建Tab页
(4)添加Tab页
下面是设置导航模式,创建并添加Tab页的示例:
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS );
Fragment artistsFragment = new ArtistsFragment();
//创建Fragment
//创建并添加Tab页,以及设置TabListener
actionBar.addTab(actionBar.newTab().setText(R.string.tab_artists).setTabListener(new TabListener(artistsFragment)));
为了监听Tab的选择,创建TabListener并实现其方法如下:
private class MyTabListener implements ActionsBar.TabListener{
private TabContentFragment mFragment;
public MyTabListener(
TabContentFragment fragment ){
mFragment = fragment;
}
public void onTabSelected(Tab tab, FragmentTransaction ft){
ft.add(R.id.fragment_content, mFragment, null);
}
public void onTabUnselected(Tab tab, FragmentTransaction ft){
ft.remove(
mFragment );
}
public void onTabReselected(Tab tab, FragmentTransaction ft){ .........
}
}
通过ActionBar的 getSelectedNavigationIndex()方法和getSelectedTab()方法可以获知当前选中的是哪个Tab页。
5. 下拉菜单
下拉菜单主要是基于SpinnerAdapter来处理数据的。实现的步骤如下:
(1)设置导航模式 (2)实现并加载资源文件 (3)创建并设置onNavigationListener
设置导航模式的方法,调用setNavigationMode()方法将导航模式设置为NAVIGATION_MODE_LIST即可,具体方法:
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.
NAVIGATION_MODE_LIST );
资源的实现方式有数组和资源文件两种,下面是资源文件的实现示例:
- Mercury
- Venus
加载资源文件的方法:
SpinnerAdapter mSpinnerAdapter = ArrayAdapter.cresteFromResource(this, R.array.action_list, android.R.layout.simple_spinner_dropdown_item);
为监听选择,需创建并设置OnNavigationListener 。创建OnNavigationListener的方法如下:
mOnNavigationListener = new OnNavigationListener(){
String[] strings = getResources().getStringArray(R.array.action_list);
public boolean onNavigationItemSelected(int position, long itemId){
..... return true;
}
}
设置OnNavigationListener 是通过setListNavigationCallbacks()方法实现的,示例如下:
actionBar.setListenerNavigationCallbacks(mSpinnerAdapter, mOnNavigationListener);
第二章 资源框架详解
2.1 布局文件
1. 加载布局
可通过setContentView()隐式加载,若希望显式加载,可铜鼓getLayoutInflater()方法来获取LAYOUT_INFLATER_SERVICE服务将布局文件实例化,也可以获取已有的LayoutInflater对象的副本来实例化布局文件。布局文件实例化的方法:
》通过getLayoutInFlater()方法,具体如下: View demo = getLayoutInflater().Inflater(R.layout.demo , null);
》通过系统服务,具体如下: LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View demo = inflater.Inflater(R.layout.demo, null);
》通过已有LayoutInflater对象的副本,具体如下: LayoutInflater inflater = from(conext);
View demo = inflater.Inflater(R.layout.demo, null);
2. 密度的逻辑
三种,dp, px, dip ,其中dip最常用,dip的设置与分辨率无关,与屏幕密度有关
3. 特殊标签
在android布局文件中,除了普通的UI空间标签外,还有几种特殊的标签:viewStub, requestFocus, merge 和 include
(1)viewStub标签,实际是一种特殊的控件,默认情况下,其所包含的控件是不可见的,并不占任何内存空间,开发者可通过setVisibility()和 inflate()加载viewStub标签所包含的布局,实例如下:
android:layout=”@layout/otacall_card“
android:layout_width="" android:layout_height="" />
其布局文件是通过android:layout属性引用外部布局文件。加载其所含的布局的实例如下:
ViewStub otaCallCardStub = (ViewStub)mInCallScreen.findViewById(R.id.otaCallCardStub);
otaCallCardStub.inflate();
(2)requestFocus标签, 可以使相应的UI控件获得焦点,使用时在UI控件的内部。
android:text="..." >
(3)merge 标签
merge主要用于优化根布局文件或在使用viewStub标签和include标签时,减少引用冗余。注意, merge标签仅能作为布局文件的根布局, 如果希望布局加载器(LayoutInflater)加载以merge标签为跟布局的布局文件,需要使用View inflater()指定一个ViewGroup作为其容器,并设置attachToRoot 为ture。
》优化根布局
考虑到Activity 的根布局为FrameLayout控件(ID为content),如果期望的布局文件的根布局也为
FrameLayout,则可利用merge标签直接将相应的子控件放置在Activity的根布局中,示例如下:
》减少冗余
下面以启动器为例,是通过include标签应用外部布局文件all_apps.xml的情况如下:
布局文件all_apps.xml的实现,如下:
xmlns:android="http://schemas.android.com/apk/res/android"
>
(4)include标签
作用类似于C语言中的include关键字,用于引用外部布局文件,其复用布局文件的作用。
2.2 值文件
包括多种类型的布局文件,存在于res\values目录下,简体中文目录为 res\vlaue-zh-rCN, 繁体中文目录为res\values-zh-rTW , 命名格式为 values-<语言>-<方言>
1. 字符串文件
translatable="false" >2
其中translatable="flase" ,表示字符串资源不必本地化,即不需要翻译成其他的语言,全部使用默认的。
字符串资源的加载:
(1)字符串资源的引用
在android中,分系统资源和应用资源,两种资源引用方式不同,系统资源并不全部对应用层开放,
对应用层开放的系统资源在framework\base\core\res\res\vlaues\目录下的public.xml中定义 。不对应用层开放的系统资源加载方式如下:
getString(com.android.internal.R.string.using); //采用”@ com.android.internal.R./ “方式
其他资源与字符串加载类似。应用本身的字符串资源在xml 文件中直接加载,或 R.string.***
引用的框架层的字符串资源在xml配置文件中的加载方式:
android:text="@android>:string/cancel"
java文件中加载框架层字符串资源方式: android.R.string.cancel
(2)数据交换
对于复杂的字符串数据,android支持XLIFF和通配符。
在一个字符串标签中,android仅支持一个通配符,其应用方法如下:
text "%1"
在java中,加载通配符的方法: text.setText(getString(R.string.hello).replace("%1", String.values(curPage))) ;
android对XLIFF的支持如下:
Process id="process"> %1$s is not responding.
XLIFF的命名空间为 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"
在java中加载字符串的方法: res.getString(com.android.internal.R.string.anr_process, name1.toString(), name2.toString());
(3)下划线的实现
android中的文字特效目前只支持加粗和斜体两种,如果要实现下划线,可借助HTML语法。 如下:
testView.setText(Html.fromHtml(""+helloStr+" "));
(4)语言切换的实现
语言切换时通过资源管理器进行的,方法如下;
Resources resources = getResources();
Configuration config = resources.getConfiguration();
DisplayMetrics dm = resources.getDisplayMetrics();//通过此类得到屏幕的一下显示信息,如分辨率,字体,大小
config.locale = Locale.SIMPLIFIED_CHINESE;
resources.updateConfiguration(config, dm);
通过类似的设置,开发者可切换屏幕的密度,MCC,MNC,屏幕方向等。
2. 字符串数组文件
字符串数组长用于下拉框等场景,如不希望字符串数组随语言环境的变化而变化,可将其 translatable 属性设为 false
在xml文件中的加载
android:entryValues="array/preferences_alert_type_values"
代码中的加载: ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.colors , android.R.layout.simple_spinner_item);
3. 配置文件
常用于应用的设置,实现如下:
false
代码中加载: mLimitedAlphaCompositing = context.getResources().getBoolean(com.android.internal.R.bool.config_sf_limitedAlpha);
4. 属性文件
属性文件通常由多个标签组成。 declar-styleable标签用于声明一个属性组,一个属性组可以由多个属性组成。如下
<declar-styleable name="Animation" >
declar-styleable >
加载属性组的方法:
TypedArray a = context.obtainStyledAttributes(attris, com.android.internal.R.styleable.Animation);
...
a.recycle();//用于随后的复用
eat-comment标签用于声明标签上的内容为注释
另外,attr标签用于声明属性。一个属性包括属性名和属性格式两部分。声明属性的实例如下;
2.3 创建菜单
根据应用场景的不同,可以分为选项菜单(OptionsMenu),上下文菜单(ContextMenu),子菜单(SubMenu)等。
2.3.1 选项菜单
两种实现方式,基于xml资源文件实现和java代码实现。基于xml资源实现时,当加载的菜单项较多时,android会自动将不能完全显示的菜单放置在More扩展选项菜单中,注意,在More扩展菜单中,无法显示图标。
1. 加载xml资源文件实现菜单
在res\menu\目录下创建菜单资源文件,实例alarm_list_menu.xml 如下:
........
在代码中加载:
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.alarm_list_menu, menu);
return super.onCreateOptionsMenu(menu);
}
菜单执行过程:
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
...... }
}
若希望在菜单显示之前做些处理,那么需要关注onPrepareOptionsMenu()方法。下面是一个实现过程。
public boolean onPrepareOptionsMenu(Menu menu){
menu.findItem(DISPLAY_MODE_LAUNCH).setVisible(mDisplayMode != DISPLAY_MODE_LAUNCH);
return true;//若希望显示菜单的内容,需要返回true,否者返回false。
}
2. 代码中实现
通过Menu类来管理菜单,添加菜单:(在onCreateOptionsMenuz()方法中添加)
public MenuItem add(CharSequence title);
public MenuItem add(int titleRes);
public MenuItem add(int groupId, int itemId, int order, CharSequence title);
为菜单设置图标:
public MenuItem setIcon(Drawable icon);
public MenuItem setIcon(int iconRes); 移除菜单方法: removeItem(), 清除所有菜单项: clear()
执行菜单的隐藏的方法: menu.findItem(R.id.clear_history_menu_id).setVisible(Browser.canClearHistory(this.getContentResolver()));
是菜单失效的方法: menu.findItem(R.id.transfer_menu_clear_all).setEnabled(false);
将菜单与Intent关联起来:
menu.findItem(R.id.dial_context_menu_id).setIntent(new Intent(Intent.ACTION_VIEW, Uri.prase(WebView.SCHEME_TEL + extra)));
对菜单项的操作可通过MenuItem类进行。
3. 菜单组
添加菜单组时,默认的菜单组的ID为NONE,移除菜单组的方法: removeGroup(),还支持菜单组的显示,隐藏,检查等,方法如下:
public void
setGroupCheckable
(int group, boolean checkable, boolean exclusive);
public void
setGroupVisible
(int group, boolean visible);
public void
setGroupEnabled
(int group, boolean enabled);
2.3.2 上下文菜单
上下文菜单不支持快捷键和图标,上下文菜单可用于多种视图,但通常用于列表。通过
registerForContextMenu()
注册上下文菜单或直接为视图安装监听器可以是吸纳视图和上下文菜单的关联,以列表为例,具体如下:
registerForContextMenu(getListView());//注册上下文
getListView().OnCreateContextMenuListener()//安装监听器
同样有两种方法实现,在创建上下文菜单时,需特别注意ContextMenuInfo。 ContextMenuInfo提供了菜单的附加信息,如选中的菜单项在整个上下文菜单中的位置等。
还可为上下文菜单设置标题,上下文菜单的标题支持图标形式。常用方法如下:
public ContextMenu setHeaderTitle(int titleRes);
public ContextMenu setHeaderIcon(int iconRes);
public void clearHeader();
1. 代码实现
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo){
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}
2. 通过资源文件实现上下文菜单
通过Menu Inflater来实现的。通常需要在创建上下文菜单时获取当前视图的信息,这些信息一般通过ContextMenuInfo 传递给开发者,其没有类似onPreparOptionsMenu(), 但也可以实现,一定要在菜单资源你文件加载完成(即 执行 getMenuInflater().inflater() )后进行。如下
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo){
menu.setHeaderTitle(R.string.ontext_menu_title); //菜单标题
getMenuInflater().inflater(R.menu.context_menu, menu); //加载资源文件
AdatperContextMenuInfo info = (AdatperContextMenuInfo ) menuInfo ;
Cursor c = (Cursor) mAlarmsList.getAdapter().getItem((int) info.position); //获取光标 。。。。。。
}
执行与选项菜单类似,在 onContextItemSelected ()方法中实现。
2.3.3 子菜单
子菜单不支持图标和二级子菜单。子菜单的执行在其父菜单的执行方法中执行,对于上下文菜单的子菜单,执行方法为 onContextItemSelected (), 对于选项菜单的子菜单,实现方法为: onOptionsItemSelected ().
1. 资源文件
android:title="Emotions">
...
2. 代码实现
也是通过Menu类实现:
SubMenu addSubMenu(final CharSequence title);
创建子菜单:(在onCreateContextMenu()中实现)
menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLSIT, 0, R.string.add_to_playlist);
判断子菜单的存在及获取方法如下: public boolean
hasSubMenu
() l; public SubMenu
getSubMneu
();
2.3.4 弹出菜单
弹出菜单(PopupMenu),实现如下:
对一个button键,设置弹出菜单,代码实现:
public class PopupMenu1 extends Activity{
protected void onCreate(Bundle savedInstanceState){
supter.onCreate(savedInstanceState );
setContextView(R.layout.popup_menu_1);//加载包含此button的布局文件
}
public void onPopupButtonClick(View button){//button事件点击方法
PopupMenu popup = new PopupMenu(this, button);
popup.getMenuInflater().inflate(R.menu.popup, popup.getMenu());//加载菜单
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickLisener(){
public boolean onMenuItemClick(MenuItem item){
....... ; return true;
}
});
popup.show();
}
}
菜单被销毁时,通过Popupmenu.OnDismissListener可以监听到菜单被销毁的信息。
2.4 断言的处理
在断言中,可以加载字体,HTML文件,图片等,断言中限制文件大小为1MB, 同样在res , raw下的资源文件也有这样的限制。对于超出限制的资源,系统会在运行时提出警告:
android中,断言的管理通过AssetManager进行,获得断言的方法:
AssetManager am = getAssets();// 在Activity中
断言的加载模式有ACCESS_BUFFER, ACCESS_RANDOM, ACCESS_STREAMING, ACCESS_UNKNOWN几种。打开断言文件时,可以设置断言的加载模式,方法如下:
public final InputStream open(String fileName , int accessMode)
1. 加载字体
android自带了sans, serif, 和 monospace等3中字体,系统默认为sans,在一个TextView中设置字体方式:
android:typeface="serif"
当系统引进新字体,放置在assets目录下,通过断言加载字体:
TextView tv = (TextView)findViewById(R.id.custom);
Typeface face = Typeface.createFromAsset(getAssets(), "fonts/Clockopia.ttf");
tv.setTypeface(face);
2 . 加载文件
支持多种文件的加载,场景不同,加载方式也不同。断言中,加载文件的一个常见的场景是基于JSP的HTML等进行的,主要用于基于WebView的应用,其Uri为 file:///android_asset/*.*
2.5 jar包和共享库
1. jar包的加载
源码中,jar包的加载如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := libarity
LOCAL_SRC_FILES :=$(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := Calculator
include $(BUILD_PACKAGE)
########################################
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := libarity:arity-2.1.2.jar
include $(BUILD_MULTI_PREBUILT)
#Use the following include to make our test apk
inlcude $(call all-makefiles-under, $(LOCAL_PATH))
2.共享库
对于基于源码进行编译的场景,需要现在android.mk中进行共享库的配置,方法如下:
LOCAL_JNI_SHARED_LIBRARIES := gl2jni
为了使用相应的共享库,在java文件中还需要进行相应的加载,方法如下:
public class GL2JNILib{
static {
System.loadLibrary("gl2jni");
}
public static native void init(int width, int height);
public static native void step();
}
2.6 系统资源
系统资源定义主要分布在framework\base\core\res\res目录下。
风格和主题是框架层与应用层在UI设计上的接口,其中应用层能引用的部分在Android.mk中定义在
public.xml 中。为使应用层能引用系统UI资源,需要在public.xml中增加对该资源的声明。定义风格文件styles.xml,定义主题的文件为 themes.xml。
在android中,资源采用统一的标示符定义,对于属性资源,其标示符ID从0x01010000开始定义; 对于ID资源,其标示符ID从0x01020000开始定义;对于风格资源,其标示符从0x01030000开始定义; 对于字符串资源,其从0x01040000开始定义;对于维度资源,从0x01050000开始定义; 对于颜色资源,从0x01060000开始定义;对于数组资源,从0x01070000开始定义;对图片资源,从0x01080000开始定义;对于动画资源,从0x010a0000开始。
在启动其他应用前,需要在zygote进程中,将先加载的图片资源定义在arrays.xml中的preloaded_drawables数组中,将颜色资源定义在arrays.xml中的preloaded_color_state_lists数组中,状态栏加载的顺序定义在arrays.xml中的status_bar_icon_order数组中。
注意:
图标的前缀,对于普通图标,其命名为ic_*.png; 对于应用图标,其命名为 ic_launcher_*.png; 对菜单图标,命名为 ic_menu_*.png ;对状态栏图标,命名为 ic_stat_notify_*.png ; 对Tab标签图标,其命名为 ic_tab_*.png ; 对对话框图标,命名为 ic_dialog_*.png
android图片主要使用PNG格式的,
如果需要可拉伸的场景,需要通过draw9path来制作 *.9.png 的图片
对于定义略缩图,应用图标,状态栏,快速导航等布局的一些宽高信息,定义在frameworks\base\core\res\res\vallues\目录下的dimens.xml中。