wiktionarysu插件怎么用用

在阅读epub时使用在线字典-维基-google-firefox-插件
在阅读epub时使用在线字典-维基-google-firefox-插件
  前一文提到了用lucifox阅读epub电子书时可以使用在线字典,这个功能epubreader没有。不过使用起来总是打开一个新的网页或标签页,不太方便。
  昨天升级firefox时,发现一款翻译插件,也许不少人都已经在使用了。虽然它并不只是一款在阅读epub书时专用的插件,不过这里我只说一下配合epubreader或者lucifox使用时的感受。
  象以前一样在firefox的附加组件菜单中打开攻取附加组件,输入“translate”,可以查到有中文的“维基词典+google翻译5.13”组件,点击安装后,会下载并安装。然后重启firefox。在菜单中打开“附加组件”-“扩展”,找到“维基词典+google翻译5.13”,点中选项,进行必要设置。
选择你的语言,我喜欢双语翻译,所以选中了同时查找英文。翻译方式选择了即指即译,这个比较方便,和金山词霸不同,它要求鼠标移上单词的同时,还要按下一些键的组合,为免多键麻烦,我设定了单键ctrl。这比较好,金山词霸经常在不需要翻译时捣乱,现在可以自主控制了。你也可以选择其它选词方式,比如选中翻译,双击翻译等。我选择了鼠标移开就关闭窗口,这样不影响阅读。也选上了右键菜单中的两个选项,只是为了某些时候更加方便。其它选项自己确定一下就行。
它还能通过样式表(css)来改变显示的方式,只要你懂一点样式表的知识,就可以自己进行修改,不会就用默认的也不错了。
ok,一切搞定了。
现在打开一本英文的epub书。把鼠标移动到不认识的单词上,按下ctrl键,看到了吧,一个小的弹出窗口出现了,是这个单词的维基词典的解释,有中文的解释和英文的解释(同果你选择了同时显示英文的话)。太方便了。但请大家切记,这时你必须能链上网,不能是离线的,因为维基词典是在线词典,不是离线词典。不过多数情况下这不是问题。大家维护就是好,许多摩登词汇也有。不用象lucifox那样总在一个新的标签中打开翻译结果,移开鼠标时窗口消失,很好。
你还可以在必要时翻译整个网页,在新窗口中打开查询结果。前者只在浏览网页时有效,阅读epub时无效,(url不能在网上打开)。当然这不是一个专用的词典,所以有其它的阅读器也能使用。你用firefox浏览网站时也能用,而且可能用的更勤,更方便。我没试过其它文字的翻译,不过维基字典是支持多国文字的,你可以试一下,当然翻译的目标语言你已经设置过了。
当然,用这款插件时,你不能自定义词典,即使是其它的在线词典,这是有点遗憾。不过已经很满意了,能边阅读边查字典,读英语书就方便了。你觉得呢?
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。博客分类:
关键字:应用小插件 小部件 小工具
应用程序窗口小部件App Widgets
应用程序窗口小部件(Widget)是微小的应用程序视图,可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。你可以通过一个App Widget provider来发布一个Widget。可以容纳其它App Widget的应用程序组件被称为App Widget宿主。下面的截屏显示了一个音乐App Widget。
这篇文章描述了如何使用App Widget Provider发布一个App Widget。
基础知识The Basics
为了创建一个App Widget,你需要下面这些:
AppWidgetProviderInfo 对象
描述一个App Widget元数据,比如App Widget的布局,更新频率,以及AppWidgetProvider 类。这应该在XML里定义。
AppWidgetProvider 类的实现
定义基本方法以允许你编程来和App Widget连接,这基于广播事件。通过它,当这个App Widget被更新,启用,禁用和删除的时候,你都将接收到广播通知。
为这个App Widget定义初始布局,在XML中。
另外,你可以实现一个App Widget配置活动。这是一个可选的活动Activity,当用户添加App Widget时加载并允许他在创建时来修改App Widget的设置。
下面的章节描述了如何建立这些组件:
在清单中声明一个应用小部件
首先,在应用程序AndroidManifest.xml文件中声明AppWidgetProvider 类,比如:
&receiver android:name="ExampleAppWidgetProvider" &
&intent-filter&
&action android:name="android.appwidget.action.APPWIDGET_UPDATE" /&
&/intent-filter&
&meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info & src="http://hi.images.csdn.net/js/blog/tiny_mce/themes/advanced/langs/zh.js" type="text/javascript"& & src="http://hi.images.csdn.net/js/blog/tiny_mce/plugins/syntaxhl/langs/zh.js" type="text/javascript"& " /&
&/receiver&
&receiver&元素需要android:name属性,它指定了App Widget使用的AppWidgetProvider 。
&intent-filter& 元素必须包括一个含有android:name属性的&action&元素。该元素指定AppWidgetProvider接受ACTION_APPWIDGET_UPDATE 广播。这是唯一你必须显式声明的广播。当需要的时候,AppWidgetManager 会自动发送所有其他App Widget广播给AppWidgetProvider。
&meta-data& 元素指定了AppWidgetProviderInfo 资源并需要以下属性:
·&&&&&&&& android:name – 指定元数据名称。
·&&&&&&&& android:resource – 指定AppWidgetProviderInfo 资源路径。
增加AppWidgetProviderInfo元数据
AppWidgetProviderInfo定义一个App Widget的基本特性,比如最小布局尺寸,初始布局资源,刷新频率,以及(可选的)创建时加载的一个配置活动。使用单独的一个&appwidget-provider&元素在XML资源里定义AppWidgetProviderInfo 对象并保存到项目的res/xml/目录下。
&appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp" &!-- density-independent pixels --&
android:minHeight="72dp"
android:updatePeriodMillis="" &!-- once per day --&
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure" &
&/appwidget-provider&
下面是&appwidget-provider&属性的总结:
·&&&&&&&& minWidth 和minHeight 属性的值指定了这个App Widget布局需要的最小区域。
缺省的App Widgets所在窗口的桌面位置基于有确切高度和宽度的单元网格。如果App Widget的最小长宽和这些网格单元的尺寸不匹配,那么这个App Widget将收缩到最接近的单元尺寸。(参见App Widget Design Guidelines 以获取更多关于桌面单元尺寸的信息)
因为桌面布局方向(由此,单元的尺寸)可以变化,按照拇指规则,你应该假设最坏情况单元尺寸是74像素高和宽。不过,你必须从最后的尺寸中减去2以把像素计算过程中产生的任何的整数舍入误差考虑在内。要找到像素密度无关的最小宽度和高度,使用这个公式:
(number of cells * 74) - 2
遵循这个公式,你应该使用72dp为每一个单元高度,294dp为四个单元宽度。
·&&&&&&&& updatePerdiodMillis 属性定义了App Widget框架调用onUpdate()方法来从AppWidgetProvider请求一次更新的频度。实际更新时间并不那么精确,而且我们建议更新频率越低越好-也许每小时不超过一次以节省电源。你也许还会允许用户在配置中调整这个频率-一些人可能想每15分钟一次股票报价,或者一天只要四次。
·&&&&&&&& initialLayout属性指向定义App Widget布局的资源。
·&&&&&&&& configure属性定义了Activity ,当用户添加App Widget时启动,以为他或她配置App Widget特性。这是可选的(阅读下面的Creating an App Widget Configuration Activity)。
参见AppWidgetProviderInfo 类以获取更多可以被&appwidget-provider&元素接受的属性信息。
创建App Widget布局
你必须在XML中为你的App Widget定义一个初始布局并保存到项目的res/layout/ 目录下。你可以使用如下所列的视图对象来设计你的App Widget,但是在此之前,请先阅读并理解App Widget Design Guidelines.
如果你熟悉在XML中声明布局,那么创建这个App Widget布局是很简单的。但是,你必须意识到那个App Widget布局是基于RemoteViews, 这并不支持所有类型的布局或视图小部件。
一个RemoteViews对象(以及,相应的,一个App Widget)可以支持下面这个布局类:
·&&&&&&&& FrameLayout
·&&&&&&&& LinearLayout
·&&&&&&&& RelativeLayout
以及下面的小部件类:
·&&&&&&&& AnalogClock
·&&&&&&&& Button
·&&&&&&&& Chronometer
·&&&&&&&& ImageButton
·&&&&&&&& ImageView
·&&&&&&&& ProgressBar
·&&&&&&&& TextView
不支持这些类的派生。
使用AppWidgetProvider类
你必须通过在清单文件中使用&receiver&元素来声明你的AppWidgetProvider 类实现为一个广播接收器(参见上面的Declaring an App Widget in the Manifest)。
AppWidgetProvider 类扩展BroadcastReceiver 为一个简便类来处理App Widget广播。AppWidgetProvider只接收和这个App Widget相关的事件广播,比如这个App Widget被更新,删除,启用,以及禁用。当这些广播事件发生时,AppWidgetProvider 将接收到下面的方法调用:
onUpdate(Context, AppWidgetManager, int[])
这个方法调用来间隔性的更新App Widget,间隔时间用AppWidgetProviderInfo 里的updatePeriodMillis属性定义(参见添加AppWidgetProviderInfo元数据)。这个方法也会在用户添加App Widget时被调用,因此它应该执行基础的设置,比如为视图定义事件处理器并启动一个临时的服务Service,如果需要的话。但是,如果你已经声明了一个配置活动,这个方法在用户添加App Widget时将不会被调用,而只在后续更新时被调用。配置活动应该在配置完成时负责执行第一次更新。(参见下面的创建一个App Widget配置活动Creating an App Widget Configuration Activity。)
onDeleted(Context, int[])
当App Widget从宿主中删除时被调用。
onEnabled(Context)
当一个App Widget实例第一次创建时被调用。比如,如果用户添加两个你的App Widget实例,只在第一次被调用。如果你需要打开一个新的数据库或者执行其他对于所有的App Widget实例只需要发生一次的设置,那么这里是完成这个工作的好地方。
onDisabled(Context)
当你的App Widget的最后一个实例被从宿主中删除时被调用。你应该在onEnabled(Context)中做一些清理工作,比如删除一个临时的数据库。
onReceive(Context, Intent)
这个接收到每个广播时都会被调用,而且在上面的回调函数之前。你通常不需要实现这个方法,因为缺省的AppWidgetProvider 实现过滤所有App Widget 广播并恰当的调用上述方法。
注意: 在Android 1.5中, 有一个已知问题,onDeleted()方法在该调用时不被调用。为了规避这个问题,你可以像Group post中描述的那样实现onReceive() 来接收这个onDeleted()回调。
最重要的AppWidgetProvider 回调函数是onUpdated(), 因为它是在每个App Widget添加进宿主时被调用的(除非你使用一个配置活动)。如果你的App Widget 要接受任何用户交互事件,那么你需要在这个回调函数中注册事件处理器。如果你的App Widget不创建临时文件或数据库,或者执行其它需要清理的工作,那么onUpdated() 可能是你需要定义的唯一的回调函数。比如,如果你想要一个带一个按钮的App Widget,当点击时启动一个活动,你可以使用下面的AppWidgetProvider实现:
public class ExampleAppWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i&N; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current App Widget
appWidgetManager.updateAppWidget(appWidgetId, views);
这个AppWidgetProvider 仅定义了onUpdated() 方法,为了定义一个PendingIntent,来启动一个活动并使用setOnClickPendingIntent(int, PendingIntent)方法把它附着到这个App Widget的按钮上。注意它包含了一个遍历appWidgetIds中所有项的循环,这是一个IDs数组,每个ID用来标识由这个Provider创建的一个App Widget。这样,如果用户创建多于一个这个App Widget的实例,那么它们将被同步更新。不过,对于所有的App Widget实例,只有一个updatePeriodMillis 时间表被管理。比如,如果这个更新时间表被定义为每隔两个小时,而且App Widget的第二个实例是在第一个后面一小时添加的,那么它们将按照第一个所定义的周期来更新而第二个被忽略(它们将都是每2个小时进行更新,而不是每小时)。
注意: 因为这个AppWidgetProvider 是一个广播接收器BroadcastReceiver,不能保证你的进程在回调函数返回后仍然继续运行(参见应用程序基础&广播接收器的生命周期Application Fundamentals & Broadcast Receiver Lifecycle以获取更多信息)。如果你的App Widget设置过程能持续几秒钟(也许当执行网页请求时)而且你要求你的进程继续,考虑在onUpdated()方法里启动一个服务Service 。从这个服务里,你可以执行自己的App Widget更新,而不必担心AppWidgetProvider 由于一个应用程序无响应错误Application Not Responding (ANR)而关闭。参见Wiktionary sample's AppWidgetProvider,这是个App Widget运行一个Service的例子。
同样参见ExampleAppWidgetProvider.java 例子类。
接收App Widget广播意图
AppWidgetProvider 只是一个简便类。如果你想直接接收App Widget 广播,你可以实现自己的BroadcastReceiver 或者重写 onReceive(Context, Intent) 回调函数。你需要注意的4个意图如下:
·&&&&&&&& ACTION_APPWIDGET_UPDATE
·&&&&&&&& ACTION_APPWIDGET_DELETED
·&&&&&&&& ACTION_APPWIDGET_ENABLED
·&&&&&&&& ACTION_APPWIDGET_DISABLED
创建一个App Widget 配置活动
如果你想让用户在添加一个新的App Widget时调整设置,你可以创建一个App Widget配置活动。这个活动将被App Widget宿主自动启动并允许用户在创建时配置可用的设置,比如App Widget颜色,尺寸,更新周期或者其它功能设置。
这个配置活动应该在Android清单文件中声明为一个通用活动。不过,它将被通过ACTION_APPWIDGET_CONFIGURE活动而被App Widget宿主启动,因此这个活动需要接受这个意图。比如:
&activity android:name=".ExampleAppWidgetConfigure"&
&intent-filter&
&action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" /&
&/intent-filter&
&/activity&
同样的,活动必须在AppWidgetProviderInfo XML 文件中声明,通过android:configure属性(参见上面的添加AppWidgetProviderInfo元数据Adding the AppWidgetProviderInfo Metadata)。比如,配置活动可以声明如下:
&appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:configure="com.example.android.ExampleAppWidgetConfigure"
&/appwidget-provider&
注意这个活动是用全名声明的,因为它将从你的程序包外被引用。
这就是所有关于配置活动你一开始需要了解的。现在你需要一个真实的活动。这儿就有,不过,当你实现这个活动时记住两件重要的事情:
o& App Widget 宿主调用配置活动而且配置活动应该总是返回一个结果.这个结果应该包含这个通过启动该活动的意图传递的App Widget ID(以EXTRA_APPWIDGET_ID保存在意图的附加段Intent extras中)
o& 当这个 App Widget 被创建时将不会调用onUpdate() 方法(当一个配置活动启动时,系统将不会发送ACTION_APPWIDGET_UPDATE广播).配置活动应该在 App Widget 第一次被创建时负责从AppWidgetManager请求一个更新.不过, onUpdate() 将在后续更新中被调用-只忽略第一次.
参见下面章节的代码片断,该示例说明了如何从配置中返回一个结果并更新这个App Widget.
从配置活动中更新一个App Widget
当一个App Widget使用一个配置活动,那么当配置结束时,就应该由这个活动来更新这个App Widget.你可以直接AppWidgetManager里请求一个更新来这么做.
下面是恰当的更新App Widget 以及关闭配置活动这个过程的一个概要描述:
&& 1. 首先,从启动这个活动的意图中获取App Widget ID:
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
&& 2. 实施你的App Widget 配置。
&& 3. 当配置完成后,通过调用getInstance(Context)获取一个AppWidgetManager实例:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
&& 4. 以一个RemoteViews布局调用updateAppWidget(int, RemoteViews)更新App Widget:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
&& 5. 最后,创建返回意图,设置活动结果,并结束这个活动:
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
提示: 当你的配置活动第一次打开时,设置活动结果为RESULT_CANCELED。这样,如果用户在结束之前从活动外返回,这个App Widget 宿主会接收到配置取消通知而不会添加这个App Widget。
参见ApiDemos里面的ExampleAppWidgetConfigure.java 例子。
浏览 19746
JasonShieh
浏览: 389464 次
来自: 上海
天天向上1989 写道不是有OnDoubleTapListen ...
nielong123 写道onCheckedChanged(M ...
onCheckedChanged(MultiRadioGrou ...
zk_Ming 写道我用了你的,但是radiogroup 点击 ...
chaozhidan 写道有木有源码啊,谢谢大神,646869 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'当前位置: >>
android资料整理
1. android 单实例运行方法我们都知道 Android 平台没有任务管理器, 而内部 App 维护者一个 Activity history stack 来实现窗口显示和销毁,对于常规从快捷方式运行来看都是 startActivity 可能会使 用 FLAG_ACTIVITY_NEW_TASK 标记来打开一个新窗口; 比如 Launcher,所以考虑单任务的实现方法比较简单,首先纠正下大家一种错误的方 法就是直接在 androidmanifest.xml 的 application 节点中加入 android:launchMode=&singleInstance&这句,其实这样将不会起到任何作用,Apps 内部维 护的历史栈作用于 Activity,我们必须在 activity 节点中加入 android:launchMode=&singleInstance& 这句才能保证单实例, 当然一般均加在主程序启动 窗口的 Activity。2. px 像素如何转为 dip 设备独立像素最近有网友问如何将 px 像素转为 dip 独立设备像素, 由于 Android 的设备分辨率众多, 目前主流的为 而很多老的设备为 hvga 甚至低端的 qvga, 对于兼容性来说使用 dip 无非是比较方便的, 由于他和分辨率无关和屏幕的密度大小有关,所以推荐使用。 px= (int) (dip*density+0.5f) // 这里 android 开发网提示大家很多网友获取 density(密度)的方法存在问题, 从资源中获取的是静态定义的, 一般为 1.0 对于 HVGA 是正 好的,而对于 wvga 这样的应该从 WindowsManager 中获取,WVGA 为 1.5 这里可以再补充一 下 dip,sip 的知识3. Android 中动态改变 ImageView 大小很多网友可能发现在 layout.xml 文件中定义了 ImageView 的绝对大小后,无法动态修 改以后的大小显示,其实 Android 平台在设计 UI 控件时考虑到这个问题; 为了适应不同的 Drawable 可以通过在 xml 的相关 ImageView 中加入 : android:scaleType=&fitXY& 这行即可,但因为使用了缩放可能会造成当前 UI 有所变形。 使用的前提是限制 ImageView 所在的层,可以使用一个内嵌的方法限制显示。4. 如何判断 Android 手机当前是否联网?如果拟开发一个网络应用的程序, 首先考虑是否接入网络, Android 手机中判断是否 在 联网可以通过 ConnectivityManager 类的 isAvailable()方法判断,首先获取网络通讯类 的实例 ConnectivityManager cwjManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); 使用 cwjManager.getActiveNetworkInfo().isAvailable(); 来返回是否有效: 如果为 True 则表示当前 Android 手机已经联网, 可能是 WiFi 或 GPRS、 HSDPA 等等; 具体的可以通过 ConnectivityManager 类的 getActiveNetworkInfo() 方法判断详细 的接入方式; 需要注意的是有关调用需要加入权限:&uses-permission android:name=&android.permission.ACCESS_NETWORK_STATE&&&/uses-permission&; android 开发网提醒大家在真机上 Market 和 Browser 程序都使用了这个方法,来判断 是否继续, 同时在一些网络超时的时候也可以检查下网络连接是否存在, 以免浪费手机上的 电力资源。5. Drawable、Bitmap、Canvas 和 Paint 的关系很多网友刚刚开始学习 Android 平台,对于 Drawable、Bitmap、Canvas 和 Paint 它们 之间的概念不是很清楚,其实它们除了 Drawable 外早在 Sun 的 J2ME 中就已经出现了,但 是在 Android 平台中,Bitmap、Canvas 相关的都有所变化。 首先让我们理解下 Android 平台中的显示类是 View,但是还提供了底层图形类 android.graphics,今天所说的这些均为 graphics 底层图形接口。 Bitmap - 称作位图,一般位图的文件格式后缀为 bmp,当然编码器也有很多如 RGB565、 RGB888。作为一种逐像素的显示对象执行效率高,但是缺点也很明显存储效率低。我们理解 为一种存储对象比较好。 Drawable - 作为 Android 平下通用的图形对象, 它可以装载常用格式的图像, 比如 GIF、 PNG、JPG,当然也支持 BMP,当然还提供一些高级的可视化对象,比如渐变、图形等。 Canvas - 名为画布,我们可以看作是一种处理过程,使用各种方法来管理 Bitmap、GL 或者 Path 路径,同时它可以配合 Matrix 矩阵类给图像做旋转、缩放等操作,同时 Canvas 类还提供了裁剪、选取等操作。 Paint - 我们可以把它看做一个画图工具,比如画笔、画刷。他管理了每个画图工具的 字体、颜色、样式。6. Activity 切换导致的 onCreate 重复执行部分网友会发现 Activity 在切换到后台或布局从横屏 LANDSCAPE 切换到 PORTRAIT, 重新切换 Activity 会触发一次 onCreate 方法,我们可以在 androidmanifest.xml 中的 activit 元素加入这个属性 : android:configChanges=&orientation|keyboardHidden& 即可,比如 :&activity android:name=&.android123& android:configChanges=&orientation|keyboardHidden& android:label=&@string/app_name&& 同时在 Activity 的 Java 文件中重载 onConfigurationChanged(Configuration newConfig)这个方法; 这样就不会在布局切换或窗口切换时重载 onCreate 等方法。代码如下: @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { //land } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { //port } }7. Android 的 ImageButton 问题很多网友对 Android 提供的 ImageButton 有个疑问,当显示 Drawable 图片时就不会再 显示文字了,其实解决的方法有两种,第一种就是图片中就写入文字,但是这样解决会增加 程序体积,同时硬编码方式会影响多国语言的发布。第二种解决方法很简单,通过分析可以 看到 ImageButton 的 layout,我们可以直接直接继承,添加一个 TextView,对齐方式为右 侧即可实现 ImageButton 支持文字右侧显示。8. Android 代码优化技术1.Java 内存控制 对于字符串操作而言如果需要连加这样的操作建议使用 StringBuilder, 经过调试不难 发现如果你的字符串每次连加,使用 String 需要的内存开销会远大于 StringBuilder,然 后 Android 手机常规的运行内存大约在 128MB 左右, 对于运行多任务就需要考虑了, Android 开发网提示因为 Java 有 GC 不需要手动释放那么分配的时候就要格外的小心,频繁的 GC 操 作仍然是很影响性能的,在调试时我们可以通过 logcat 查看内存释放情况。 2.循环使用 平时在访问一个属性的时候效率远比一个固定变量低, 如果你的循环估计次数常常大于 5; 假设 xxx.GetLength()方法的值一般大于 5,推荐这样写: 比如 :for(int i=0;i&xxx.GetLength();i++) // 这里 xxx.GetLength 在每次循 环都要调用,必然会影响程序效率,在游戏开发中显得更为明显; 改进的方法应该为:int j=xxx.GetLength();for(int i=0;i&j;i++); 3.图片的优化 在 Android 平台中 2 维图像处理库 BitmapFactory 做的比较智能, 为了减少文件体积和 效率,常常不用很多资源文件,而把很多小图片放在一个图片中,有切片的方式来完成,在 J2ME 中我们这样是为了将少文件头而解决 MIDP 这些设备的问题, Android 中虽然机型硬 而 件配置都比较高,有关 Android G1 硬件配置可以参考 G1 手机参数以及评测,但是当资源 多时这样的运行效率还是令人满意的,至少 Dalvik 优化的还不是很够。9. Android 开发进阶之 NIO 非阻塞包(一)对于 Android 的网络通讯性能的提高, 我们可以使用 Java 上高性能的 NIO (New I/O) 技 术进行处理,NIO 是从 JDK 1.4 开始引入的,NIO 的 N 我们可以理解为 Noblocking 即非阻塞 的意思,相对应传统的 I/O,比如 Socket 的 accpet()、 read()这些方法而言都是阻塞的。 NIO 主要使用了 Channel 和 Selector 来实现, Java 的 Selector 类似 Winsock 的 Select 模式,是一种基于事件驱动的,整个处理方法使用了轮训的状态机,如果你过去开发过 Symbian 应用的话这种方式有点像活动对象; NIO 的好处:单线程更节省系统开销,可以很好的处理并发,对于 Android 网游开发来 说比较关键,对于多点 Socket 连接而言使用 NIO 可以大大减少线程使用,降低了线程死锁 的概率,毕竟手机游戏有 UI 线程,音乐线程,网络线程,管理的难度可想而知,同时 I/O 这种低速设备将影响游戏的体验。 NIO 作为一种中高负载的 I/O 模型,相对于传统的 BIO (Blocking I/O)来说有了很大的 提高,处理并发不用太多的线程,省去了创建销毁的时间,如果线程过多调度是问题,同时 很多线程可能处于空闲状态,大大浪费了 CPU 时间,同时过多的线程可能是性能大幅下降, 一般的解决方案中可能使用线程池来管理调度但这种方法治标不治本。 使用 NIO 可以使并发 的效率大大提高。 当然 NIO 和 JDK 7 中的 AIO 还存在一些区别,AIO 作为一种更新的当然这是对于 Java 而言,如果你开发过 Winsock 服务器,那么 IOCP 这样的 I/O 完成端口可以解决更高级的负 载,当然了今天 Android123 主要给大家讲解下为什么使用 NIO 在 Android 中有哪些用处。 NIO 我们分为几个类型分别描述,作为 Java 的特性之一,我们需要了解一些新的概念, 比如 ByteBuffer 类,Channel,SocketChannel,ServerSocketChannel,Selector 和 SelectionKey。10. Android Theme 和 Styles 内部定义解析在一个工程的 res/values/theme.xml 中我们可以方便的定义自己的风格主题; 比如下面的 cwjTheme 中我们使用了基于 android 内部的白色调的背景 Theme.Light,设置 windowsNoTitle 为 true 代表没有标题,背景颜色我们使用了 android 内部定义的透明,同 时设置 listView 控件的样式为 cwjListView: xml 样式代码如下: &?xml version=&1.0& encoding=&utf-8&?& &resources& &style name=&cwjTheme& parent=&android:Theme.Light&& &item name=&android:windowNoTitle&&true&/item& &item name=&android:windowBackground&&@android:color/transparent&/item& &item name=&android:listViewStyle&&@style/cwjListView&/item& &/style& 有关 ListView 控件我们自定义的风格就是修改下系统 listview 这个控件的每行分隔符样 式,这里我们在工程下 res/drawable 文件夹下放一个图片名为 list_selector 图片,这样 我们的 cwjListView 的代码可以这样写 : &style name=&cwjListView& parent=&@android:style/Widget.ListView&& &item name=&android:listSelector&&@drawable/list_selector&/item& &/style& &/resources& 通过定义 style 可以设置更多,比如让 cwjListView 的字体颜色就加入 textAppearance 属 性,比如 &item name=&textAppearance&&@android:style/TextAppearance&/item& 等等。11.Android JSON 解析示例代码 来自 Google 官方的有关 Android 平台的 JSON 解析示例,如果远程服务器使用了 json 而不是 xml 的数据提供,在 Android 平台上已经内置的 org.json 包可以很方便的实现手机 客户端的解析处理。下面 Android123 一起分析下这个例子,帮助 Android 开发者需要有关 HTTP 通讯、正则表达式、JSON 解析、appWidget 开发的一些知识。 public class WordWidget extends AppWidgetProvider { //appWidget @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) { context.startService(new Intent(context, UpdateService.class)); //避免 ANR,所以 Widget 中开了个服务 } public static class UpdateService extends Service { @Override public void onStart(Intent intent, int startId) { // Build the widget update for today RemoteViews updateViews = buildUpdate(this); ComponentName thisWidget = new ComponentName(this, WordWidget.class); AppWidgetManager manager = AppWidgetManager.getInstance(this); manager.updateAppWidget(thisWidget, updateViews); } public RemoteViews buildUpdate(Context context) { // Pick out month names from resources Resources res = context.getResources(); String[] monthNames = res.getStringArray(R.array.month_names); Time today = new Time(); today.setToNow(); String pageName = res.getString(R.string.template_wotd_title, monthNames[today.month],today.monthDay); RemoteViews updateViews = String pageContent = &&; try { SimpleWikiHelper.prepareUserAgent(context); pageContent = SimpleWikiHelper.getPageContent(pageName, false); } catch (ApiException e) { Log.e(&WordWidget&, &Couldn't contact API&, e); } catch (ParseException e) { Log.e(&WordWidget&, &Couldn't parse API response&, e); } Pattern pattern = Pattern.compile(SimpleWikiHelper.WORD_OF_DAY_REGEX); //正则表达式处理,有关定义见下面的 SimpleWikiHelper 类 Matcher matcher = pattern.matcher(pageContent); if (matcher.find()) { updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_word); String wordTitle = matcher.group(1); updateViews.setTextViewText(R.id.word_title, wordTitle); updateViews.setTextViewText(R.id.word_type, matcher.group(2)); updateViews.setTextViewText(R.id.definition, matcher.group(3).trim()); String definePage = res.getString(R.string.template_define_url,Uri.encode(wordTitle)); Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); //这里是打开相应的网页, 所以 Uri 是 http 的 url, action 是 view 即打开 web 浏览器 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 /* no requestCode */, defineIntent, 0 /* no flags */); updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent); //单击 Widget 打开 Activity } else { updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_message); CharSequence errorMessage = context.getText(R.string.widget_error); updateViews.setTextViewText(R.id.message, errorMessage); } return updateV } @Override public IBinder onBind(Intent intent) { // We don't need to bind to this } } } 有关网络通讯的实体类,以及一些常量定义如下: public class SimpleWikiHelper { private static final String TAG = &SimpleWikiHelper&; public static final String WORD_OF_DAY_REGEX = &(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}&; private static final String WIKTIONARY_PAGE = &http://en.wiktionary.org/w/api.php?action=query&prop=revisions&titles= %s&& + &rvprop=content&format=json%s&; private static final StringWIKTIONARY_EXPAND_TEMPLATES=&&rvexpandtemplates=true&; private static final int HTTP_STATUS_OK = 200; private static byte[] sBuffer = new byte[512]; private static String sUserAgent = public static class ApiException extends Exception { public ApiException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); } public ApiException(String detailMessage) { super(detailMessage); } } public static class ParseException extends Exception { public ParseException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); } } public static void prepareUserAgent(Context context) { try { // Read package name and version number from manifest PackageManager manager = context.getPackageManager(); PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); sUserAgent = String.format(context.getString(R.string.template_user_agent), info.packageName, info.versionName); } catch(NameNotFoundException e) { Log.e(TAG, &Couldn't find package information in PackageManager&, e); } } public static String getPageContent(String title, boolean expandTemplates) throws ApiException, ParseException { String encodedTitle = Uri.encode(title); String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : &&; String content = getUrlContent(String.format(WIKTIONARY_PAGE, encodedTitle, expandClause)); try { JSONObject response = new JSONObject(content); JSONObject query = response.getJSONObject(&query&); JSONObject pages = query.getJSONObject(&pages&); JSONObject page = pages.getJSONObject((String) pages.keys().next()); JSONArray revisions = page.getJSONArray(&revisions&); JSONObject revision = revisions.getJSONObject(0); return revision.getString(&*&); } catch (JSONException e) { throw new ParseException(&Problem parsing API response&, e); } } protected static synchronized String getUrlContent(String url) throws ApiException { if (sUserAgent == null) { throw new ApiException(&User-Agent string must be prepared&); } HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(url); request.setHeader(&User-Agent&, sUserAgent); //设置客户端标识 try { HttpResponse response = client.execute(request); StatusLine status = response.getStatusLine(); if (status.getStatusCode() != HTTP_STATUS_OK) { throw new ApiException(&Invalid response from server: & + status.toString()); } HttpEntity entity = response.getEntity(); InputStream inputStream = entity.getContent(); //获取 HTTP 返回的数据 流 ByteArrayOutputStream content = new ByteArrayOutputStream(); int readBytes = 0; while ((readBytes = inputStream.read(sBuffer)) != -1) { content.write(sBuffer, 0, readBytes); //转化为字节数组流 } return new String(content.toByteArray()); //从字节数组构建 String } catch (IOException e) { throw new ApiException(&Problem communicating with API&, e); } } } 有关整个每日维基的 widget 例子比较简单,主要是帮助大家积累常用代码,了解 Android 平台 JSON 的处理方式,毕竟很多 Server 还是 Java 的。12.Android 中使用定时器 TimerTask 类介绍在 Android 平台中需要反复按周期执行方法可以使用 Java 上自带的 TimerTask 类, TimerTask 相对于 Thread 来说对于资源消耗的更低,除了使用 Android 自带的 AlarmManager 使用 Timer 定时器是一种更好的解决方法。 我们需要引入 import java.util.T 和 import java.util.TimerT private Timer mTimer = new Timer(true); private TimerTask mTimerT mTimerTask = new TimerTask(){ public void run(){ Log.v(&android123&,&cwj&); } }; mTimer.schedule(mTimerTask, ); //在 1 秒后每 5 秒执行一次定时器中 的方法,比如本文为调用 log.v 打印输出。 如果想取消可以调用下面方法,取消定时器的执行:while(!mTimerTask.cancel()); mTimer.cancel(); 最后 Android123 提示大家,如果处理的东西比较耗时还是开个线程比较好,Timer 还是会阻塞主线程的执行,更像是一种消息的执行方式。当然比 Handler 的 postDelay 等 方法更适合处理计划任务。13.Android 应用 Icon 大小在不同分辨率下定义对于 Android 平台来说,不同分辨率下 Icon 的大小设计有着不同的要求,对于目前主 流的 HDPI 即 WVGA 级别来说,通常 hdpi 的应用 icon 大小为 72x72,而标准的 mdpi 即 hvga 为 48x48,对于目前 HTC 和 Motorola 推出的一些 QVGA 的使用了 ldpi,图标为 32x32,常见 的 Android 图标大小设计规范如下表所示: Launcher 36 x 36 px 48 x 48 px 72 x 72 px Menu 36 x 36 px 48 x 48 px 72 x 72 px Status Bar 24 x 24 px 32 x 32 px 48 x 48 px Tab 24 x 24 px 32 x 32 px 48 x 48 px Dialog 24 x 24 px 32 x 32 px 48 x 48 px List View 24 x 24 px 32 x 32 px 48 x 48 px14.Android 控件美化 Shape 你会用吗?如果你对 Android 系统自带的 UI 控件感觉不够满意,可以尝试下自定义控件,我们就 以 Button 为例,很早以前 Android123 就写到过 Android Button 按钮控件美化方法里面提 到了 xml 的 selector 构造。 当然除了使用 drawable 这样的图片外今天 Android 开发网谈下 自定义图形 shape 的方法, 对于 Button 控件 Android 上支持以下几种属性 shape、 gradient、 stroke、corners 等。 我们就以目前系统的 Button 的 selector 为例说下: &shape& &gradient android:startColor=&#ff8c00& android:endColor=&#FFFFFF& android:angle=&270& /& &stroke android:width=&2dp& android:color=&#dcdcdc& /& &corners android:radius=&2dp& /& &padding android:left=&10dp& android:top=&10dp& android:right=&10dp& android:bottom=&10dp& /& &/shape& 对于上面,这条 shape 的定义,分别为渐变,在 gradient 中 startColor 属性为开始的 颜色,endColor 为渐变结束的颜色,下面的 angle 是角度。接下来是 stroke 可以理解为边 缘,corners 为拐角这里 radius 属性为半径,最后是相对位置属性 padding。 对于一个 Button 完整的定义可以为 : &?xml version=&1.0& encoding=&utf-8&?& &selector xmlns:android=&http://schemas.android.com/apk/res/android&& &item android:state_pressed=&true& & &shape& &gradient android:startColor=&#ff8c00& android:endColor=&#FFFFFF& android:angle=&270& /& &stroke android:width=&2dp& android:color=&#dcdcdc& /& &corners android:radius=&2dp& /& &padding android:left=&10dp& android:top=&10dp& android:right=&10dp& android:bottom=&10dp& /& &/shape& &/item& &item android:state_focused=&true& & &shape& &gradient android:startColor=&#ffc2b7& android:endColor=&#ffc2b7& android:angle=&270& /& &stroke android:width=&2dp& android:color=&#dcdcdc& /& &corners android:radius=&2dp& /& &padding android:left=&10dp& android:top=&10dp& android:right=&10dp& android:bottom=&10dp& /& &/shape& &/item& &item& &shape& &gradient android:startColor=&#ff9d77& android:endColor=&#ff9d77& android:angle=&270& /& &stroke android:width=&2dp& android:color=&#fad3cf& /& &corners android:radius=&2dp& /& &padding android:left=&10dp& android:top=&10dp& android:right=&10dp& android:bottom=&10dp& /& &/shape& &/item& &/selector& 注意 Android123 提示大家,以上几个 item 的区别主要是体现在 state_pressed 按下或 state_focused 获得焦点时,当当来判断显示什么类型,而没有 state_xxx 属性的 item 可 以看作是常规状态下。15. Android 开发者应该保持以下特质Android123 推荐新手应该遵循 1. 深读 SDK 文档 2. 深读 SDK 的 APIDemo 和 Samples 3. 掌握 GIT 开源代码 4. 多了解 Android 开源项目, 学习别人的手法写程序。16. Android 数组排序常见方法 Android 的数组排序方式基本上使用了 Sun 原生的 Java API 实现, 常用的有 Comparator 接口实现 compare 方法和 Comparable 接口的 compareTo 方法,我们对于一个数组列表比如 ArrayList 可以通过这两个接口进行排序和比较, 这里 Android123 给大家一个例子: private final Comparator cwjComparator = new Comparator() { private final Collator collator = Collator.getInstance(); public final int compare(Object a, Object b) { CharSequence a = ((Item) a).sN CharSequence b = ((Item) b).sID; return collator.compare(a, b); } }; 我们的 ArrayList 对象名为 mList,则执行排序可以调用方法: Collections.sort(mList, cwjComparator);17.Android 控件 TextProgressBar 进度条上显文字Android 系统的进度条控件默认的设计的不是很周全,比如没有包含文字的显示,那么 如何在 Android 进度条控件上显示文字呢? 来自 Google 内部的代码来了解下,主要使用的 addView 这样的方法通过覆盖一层 Chronometer 秒表控件来实现,整个代码如下: public class TextProgressBar extends RelativeLayout implements OnChronometerTickListener { public static final String TAG = &TextProgressBar&; static final int CHRONOMETER_ID = android.R.id.text1; static final int PROGRESSBAR_ID = android.R.id. Chronometer mChronometer = ProgressBar mProgressBar = long mDurationBase = -1; int mDuration = -1; boolean mChronometerFollow = int mChronometerGravity = Gravity.NO_GRAVITY;public TextProgressBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public TextProgressBar(Context context, AttributeSet attrs) { super(context, attrs); } public TextProgressBar(Context context) { super(context); } //Android 开发网提示关键部分在这里 @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { super.addView(child, index, params); int childId = child.getId();if (childId == CHRONOMETER_ID && child instanceof Chronometer) { mChronometer = (Chronometer) mChronometer.setOnChronometerTickListener(this); // Check if Chronometer should move with with ProgressBarmChronometerFollow=(params.width==ViewGroup.LayoutParams.WRAP_CONTENT); // 结果为 true 或者 false mChronometerGravity = (mChronometer.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);} else if (childId == PROGRESSBAR_ID && child instanceof ProgressBar) { mProgressBar = (ProgressBar) } }@android.view.RemotableViewMethodpublic void setDurationBase(long durationBase) { mDurationBase = durationB if (mProgressBar == null || mChronometer == null) { throw new RuntimeException(&Expecting child ProgressBar with id & + &'android.R.id.progress' and Chronometer id 'android.R.id.text1'&); }// Update the ProgressBar maximum relative to Chronometer base mDuration = (int) (durationBase - mChronometer.getBase());if (mDuration &= 0) { mDuration = 1; } mProgressBar.setMax(mDuration); } public void onChronometerTick(Chronometer chronometer) { if (mProgressBar == null) { throw new RuntimeException( &Expecting child ProgressBar with id 'android.R.id.progress'&); } // Stop Chronometer if we're past duration long now = SystemClock.elapsedRealtime(); if (now &= mDurationBase) { mChronometer.stop(); } int remaining = (int) (mDurationBase - now); mProgressBar.setProgress(mDuration - remaining); if (mChronometerFollow) { RelativeLayout.LayoutP params = (RelativeLayout.LayoutParams) mProgressBar.getLayoutParams(); int contentWidth = mProgressBar.getWidth() - (params.leftMargin + params.rightMargin); int leadingEdge = ((contentWidth * mProgressBar.getProgress()) / mProgressBar.getMax()) + params.leftM int adjustLeft = 0; int textWidth = mChronometer.getWidth(); if (mChronometerGravity == Gravity.RIGHT) { adjustLeft = -textW } else if (mChronometerGravity == Gravity.CENTER_HORIZONTAL) { adjustLeft = -(textWidth / 2); } leadingEdge += adjustL int rightLimit = contentWidth - params.rightMargin - textW if (leadingEdge & params.leftMargin) { leadingEdge = params.leftM } else if (leadingEdge & rightLimit) { leadingEdge = rightL } params =(RelativeLayout.LayoutParams)mChronometer.getLayoutParams(); params.leftMargin = leadingE mChronometer.requestLayout(); } } }18. Android 内存管理-SoftReference 的使用很多时候我们需要考虑 Android 平台上的内存管理问题,Dalvik VM 给每个进程都分 配了一定量的可用堆内存,当我们处理一些耗费资源的操作时可能会产生 OOM 错误 (OutOfMemoryError)这样的异常,Android123 观察了下国内的类似 Market 客户端设计,基 本上都没有采用很好的内存管理机制和缓存处理。 如果细心的网友可能发现 Android Market 客户端载入时,每个列表项的图标是异步刷 新显示的,但当我们快速的往下滚动到一定数量比如 50 个,再往回滚动时可能我们看到了 部分 App 的图标又重新开始加载,当然这一过程可能是从 SQLite 数据库中缓存的,但是在 内存中已经通过类似 SoftReference 的方式管理内存。 在 java 中内存管理, 引用分为四大类, 强引用 HardReference、 弱引用 WeakReference、 软引用 SoftReference 和虚引用 PhantomReference。 它们的区别也很明显,HardReference 对象是即使虚拟机内存吃紧抛出 OOM 也不会导致 这一引用的对象被回收; 而 weakReference 等更适合于一些数量不多, 但体积稍微庞大的对象, 在这四个引用中, 它是最容易被垃圾回收的, 而我们对于显示类似 Android Market 中每个应用的 App Icon 时可以考虑使用 SoftReference 来解决内存不至于快速回收, 同时当内存短缺面临 Java VM 崩溃抛出 OOM 前时,软引用将会强制回收内存,最后的虚 引用一般没有实际意义,仅仅观察 GC 的活动状态,对于测试比较实用同时必须和 ReferenceQueue 一起使用。 对于一组数据, 我们可以通过 HashMap 的方式来添加一组 SoftReference 对象来临时保 留一些数据, 同时对于需要反复通过网络获取的不经常改变的内容, 可以通过本地的文件系 统或数据库来存储缓存,希望给国内做 App Store 这样的客户端一些改进建议。19. 反射在 Android 开发中的利弊由于 Android 2.2 的推出,很多新的 API 加入导致很多项目移植需要考虑使用 Java 的 反射机制 Reflection 来动态调用, 动态调用的好处就是不需要使用引用文件, 直接通过 JDK 中声明好的方法直接调用,本身原理基于 JVM 的,从 Java 1.5 开始支持,原理上就是根据类 名而不实例化对象的情况下,获得对象的方法或属性而直接调用。Android 开发时反射能帮助我们多少? 1.有些网友可能发现 Android 的 SDK 比较封闭, 很多敏感的方法常规的用户无法编译, 我们 如果翻看了代码直接在反射中声明动态调用即可。比如很多 internal 或 I 开头的 AIDL 接 口均可以通过反射轻松调用。 2.反射对于 Android123 来说更重要的是考虑到应用的兼容性, 我们目前主要兼容从 Android 1.5 到 2.2 的项目,API Level 从 3 到 8 可以方便的扩充,调用前我们预留一个标志位声明 该 API 的最低以及最高的 API Level 为多少可以调用。 3.对于调试 Java 的反射是功臣了,在 Logcat 中我们可以看到出错的地方肯定有类似 java.lang.reflect.XXX 的字样, 这种自检机制可以帮助我们方便的调试 Android 应用程序。反射的缺点有哪些? 1. 因为是动态执行的,效率自然没有预编译时引用现有的库效率高,就像平时我们 Win32 开发时,可以不用 h 文件,直接通过 GetProcAddress 一样去动态获取方法的地址。当然效 率要根据复杂程度而决定,一般稍微复杂的处理性能损失可能超过 20%,对于一些复杂的涉 及 Java 自动类型转换判断,执行时间可能是直接引用的上千倍,所以最终我们调试时必须 考虑性能问题。 2.因为反射是动态的,所以需要处理很多异常,不然 Dalvik 崩溃出 Force Close 的概率会 大很多,很简单的一个反射就需要至少 3 个异常捕获,本身 try-catch 效率就不是很高,自 然进一步影响运行效率,对于 Android 开发我们必须考虑这些问题。 3.反射因为导致代码臃肿, 自然稍微复杂的几个方法实用反射将会导致代码可读性和维护性 降低,如果很抽象的调用 Android 开发网强烈不推荐这种方法。 4.最后要说的是 Reflection 并不是 Java 的专利,微软的.Net 也同样支持,同时更多的动 态语言如 Ruby 等均支持这一特性。20.AsyncTask 对比 Thread 加 Handler很多网友可能发现 Android 平台很多应用使用的都是 AsyncTask,而并非 Thread 和 Handler 去更新 UI,这里 Android123 给大家说下他们到底有什么区别,我们平时应该使用 哪种解决方案。 从 Android 1.5 开始系统将 AsyncTask 引入到 android.os 包中, 过去在很早 1.1 和 1.0 SDK 时其实官方将其命名为 UserTask,其内部是 JDK 1.5 开始新增的 concurrent 库,做过 J2EE 的网友可能明白并发库效率和强大性,比 Java 原始的 Thread 更灵活和强大,但对于 轻量级的使用更为占用系统资源。 Thread 是 Java 早期为实现多线程而设计的,比较简单不支持 concurrent 中很多特性 在同步和线程池类中需要自己去实现很多的东西, 对于分布式应用来说更需要自己写调度代 码; 而为了 Android UI 的刷新 Google 引入了 Handler 和 Looper 机制,它们均基于消息实 现,有事可能消息队列阻塞或其他原因无法准确的使用。 Android 开发网推荐大家使用 AsyncTask 代替 Thread+Handler 的方式,不仅调用上更 为简单,经过实测更可靠一些; Google 在 browser 中大量使用了异步任务作为处理耗时的 I/O 操作,比如下载文件、 读写数据库等等,它们在本质上都离不开消息; 但是 AsyncTask 相比 Thread 加 Handler 更为可靠,更易于维护, 但 AsyncTask 缺点也是有的:比如一旦线程开启即 dobackground 方法执行后无法给线 程发送消息, 仅能通过预先设置好的标记来控制逻辑, 当然可以通过线程的挂起等待标志位 的改变来通讯,对于某些应用 Thread 和 Handler 以及 Looper 可能更灵活。21. Android Drawable 叠加处理方法大家可能知道 Bitmap 的叠加处理在 Android 平台中可以通过 Canvas 一层一层的画就 行了,而 Drawable 中如何处理呢? 除了使用 BitmapDrawable 的 getBitmap 方法将 Drawable 转换为 Bitmap 外,今天 Android123 给大家说下好用简单的 LayerDrawable 类; LayerDrawable 顾名思义就是层图形对象。下面直接用一个简单的代码表示: Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.cwj); Drawable[] array = new Drawable[3]; array[0] = new PaintDrawable(Color.BLACK); //黑色 array[1] = new PaintDrawable(Color.WHITE); //白色 array[2] = new BitmapDrawable(bm); //位图资源 LayerDrawable ld = new LayerDrawable(array); //参数为上面的 Drawable 数组 ld.setLayerInset(1, 1, 1, 1, 1); //第一个参数 1 代表数组的第二个元素,为白色 ld.setLayerInset(2, 2, 2, 2, 2); //第一个参数 2 代表数组的第三个元素,为位图资源 mImageView.setImageDrawable(ld); 上面的方法中 LayerDrawable 是关键; Android 开发网提示 setLayerInset 方法原型为: public void setLayerInset (int index, int l, int t, int r, int b) ; //其中第一个参数为层的索引号, 后面的四个参数分别为 left、 top、 right 和 bottom。 对于简单的图片合成我们可以将第一和第二层的 PaintDrawable 换成 BitmapDrawable 即可实现简单的图片合成。22. onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance很多网友可能知道: Android 横竖屏切换时会触发 onSaveInstanceS 而还原时会产生 onRestoreInstanceS 但是 Android 的 Activity 类还有: onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance 这两个方法。 我们可以通过:onRetainNonConfigurationInstance 代替 onSaveInstanceS 比如: @Override public Object onRetainNonConfigurationInstance() { // 这里是需要保存的内容, 在切换时不是 bundle 了, 我们可以直接通过 Object 来代替 } 在恢复窗口时,我们可以不使用 onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。 我们可以直接在 onCreate 中使用, 比如: Object obj=getLastNonConfigurationInstance(); 最终 obj 的内容就是上次切换时的内容; *每次 Activity 横竖屏切换时 onCreate 方法都会被触发。23. Android 中 String 资源文件的 format 方法很多时候我们感性 Google 在设计 Android 时遵守了大量 MVC 架构方式,可以让写公共 代码、美工和具体逻辑开发人员独立出来。 有关 Android 的资源文件 values/strings.xml 中如何实现格式化字符串呢? 这里举个简单的例子,以及最终可能会用到哪些地方: &?xml version=&1.0& encoding=&utf-8&?& &resources& &string name=&app_name&&cwj_Demo&/string& &string name=&hello&&android 开发网&/string& &/resources& 上面是一段简单的字符串资源文件,没有用到格式化,因为比较简单直接描述了意思, 当我们设计一个类似 Delete xxx File ? 的时候, 我们可能需要在 Java 中动态获取 xxx 的 名称, 所以定义资源时使用格式化可以轻松解决, 不需要一堆 String 去拼接或 StringBuffer 一个一个 append 这样的愚蠢方法, 例: &string name=&alert&&Delete %1$s File&/string& // 这里%1$s 代表这是一 个字符串型的,如果是整数型可以写为%1$d,类似 printf 这样的格式化字符串函数,当然 如果包含了多个需要格式化的内容,则第二个可以写为%2$s 或%2$d 了, 那么最终在 Java 中如何调用呢? 看下面的例子: 例一: 整数型的 &string name=&alert&&I am %1$d years old&/string& 定义的 是这样的; 当然,我们杜绝意外情况,比如冒出个 secret 这样的 string 类型的,注意上 面是%1$d 不是%1$s,所以默认标准的合并成为 : int nAge=23; String sAgeFormat = getResources().getString(R.string.alert); String sFinalAge = String.format(sAgeFormat, nAge); 这样执行完后,就组成了 I am 23 years old,是不是很方便啊! 当然了,下面看下 String 字符串时的情况. 例二: 字符串型的 : String sName=&cwj& String sCity=&Shanghai& 资源定义为: &string name=&alert2&&My name is %1$s , I am form %2$s&/string& 则 java 中只需要: String sInfoFormat = getResources().getString(R.string.alert2); String sFinalInfo=String.format(sInfoFormat, sName, sCity); 我们看到了整个,整个定义类似 MFC 的 CString::Format 或 Mac Os 中的 NSLog,但是需要 显示类似 C#中那样显示的标出参数的数字,比如%1 或%n,这里数字代表参数的第 n 个。 本行最终 sFinalInfo 显示的内容为: My name is cwj , I am form Shanghai 。24. Android 工程内嵌资源文件的两种方法Android 软件一般处理大的资源通过 sdcard 比如在线下载资源到 sdcard,而 apk 中 内嵌资源或二进制文件时一般使用下面的两种方法: 方法一 : res/raw 目录下存放,比如 cwj.dat 一个二进制文件,我们可以读取可以直 接 : InputStream is=context.getResources().openRawResource(R.raw.cwj); 方法二 工程根目录下的 assets 文件夹中存放, 比如 assets/cwj.dat 这样我们使用下面 的代码 AssetManager am = context.getAssets(); InputStream is = am.open(cwj.dat); 这里提示大家 Google 的 Android 系统处理 Assert 有个 bug,在 AssertManager 中不 能处理单个超过 1MB 的文件,不然会报异常,具体数值大家可以测试下传个稍大的文件,我 们在两年前的文章中有提到,而第一种 raw 没这个限制可以放个 4MB 的 Mp3 文件没问题。25. Android 自定义 View 以及 layout 属性全攻略对于 Android 系统的自定义 View 可能大家都熟悉了,对于自定义 View 的属性添加, 以及 Android 的 Layout 的命名空间问题,很多网友还不是很清楚; CwjView myView=new CwjView(context); 如果用于游戏或整个窗体的界面,我们可能直接在 onCreate 中 setContentView(myView); 当然如果是控件,我们可能会需要从 Layout 的 xml 中声明, 比如: &cn.com.android123.CwjView android:layout_width=&wrap_content& android:layout_height=&wrap_content& /& 当然,我们也可以直接从父类声明,比如: &View class=&cn.com.android123.CwjView& android:layout_width=&wrap_content& android:layout_height=&wrap_content& /& 上面我们仅用了父类 View 的两个属性,均来自 android 命名空间,而名称为 layout_width 或 layout_height,我们自定义的控件可能有更多的功能, 比如: &cn.com.android123.CwjView android:layout_width=&wrap_content& android:layout_height=&wrap_content& cwj:age=&22& cwj:university=&sjtu& cwj:city=&shanghai& /& 我们可以看到上面的三个属性,是我们自定义的。作为标准 xml 规范,可能还包含 了类似 xmlns:android=&http://schemas.android.com/apk/res/android& 这样的语句; 对于定义完整的 View,我们的命名空间为 cwj,这里可以写为: xmlns:cwj=http://schemas.android.com/apk/res/cn.com.android123.cwjView 或 xmlns:cwj=http://schemas.android.com/apk/res/android 都可以。对于定义的 cwj 命名空间和 age、 university 以及 city 的三个属性我们如何定义呢? 在工程的 res/values 目录中我们新建一个 cwj_attr.xml 文件,编码方式为 utf-8 是一个好习惯,内容如下: &?xml version=&1.0& encoding=&utf-8& ?& &resources& &declare-styleable name=&CwjView&& &attr name=&age& format=&integer& /& &attr name=&city& format=&string& /& &attr name=&university& format=&string& /& &/declare-styleable& &/resources& 这里我们可能对 format 不是很熟悉,目前 Android 系统内置的格式类型有 integer,比 如 ProgressBar 的进度值,float 比如 RatingBar 的值可能是 3.5 颗星,boolean 比如 ToggleButton 的是否勾选,string 比如 TextView 的 text 属性! 当然除了我们常见的基础类型外,Android 的属性还有特殊的比如 color 是用于颜色属 性的,可以识别为#FF0000 等类型,当然还有 dimension 的尺寸类型,比如 23dip,15px, 18sp 的长度单位; 还有一种特殊的为 reference,一般用于引用@+id/cwj @drawable/xxx 这样的类型。 当然什么时候用 reference 呢? 我们就以定义一个颜色为例子, &attr name=&red& format=&color|reference& /& 这里我们用了逻辑或的运算符,定义的红色是颜色类型的,同时可以被引用 当然,对于我们自定义的类中,我们需要使用一个名为 obtainStyledAttributes 的方法来 获取我们的定义。 在我们自定义 View 的构造方法(Context context, AttributeSet attrs)的重载类型中可以 用: public CwjView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.cwj_attr); mAge = a.getInteger(R.styleable.CwjView_age, 22); mCity = a.getString(R.styleable.CwjView_city, &shanghai&); mUniversity= a.getString(R.styleable.CwjView_university, &sjtu&); a.recycle(); //Android123 提示大家不要忘了回收资源 !!! } 这样类的全局成员变量 mAge、mCity 就获取了我们需要的内容,当然根据 layout 中的 数值我们自定义的 CwjView 需要动态的处理一些数据的情况, 可以使用 AttributeSet 类的 getAttributeResourceValue 方法获取: public CwjView(Context context, AttributeSet attrs) { super(context, attrs); resId = attrs.getAttributeResourceValue(&cn.com.android123.CwjView&, &age&, 100); resId = attrs.getAttributeResourceValue(&cn.com.android123.CwjView&, &city&, &shanghai&); //resID 就可以任意使用了 } 以上两种方法中,参数的最后一个数值为默认的.26. 自定义 Android 主题风格 theme.xml 方法在 Android 中可以通过自定义主题风格方式来实现个性化以及复用,首先我们创建 theme.xml 主题文件,保存位置为工程的 res/values/theme. 这里我们可以可以为主题起一个名称,比如 CWJ,这里去除了 xml 的文件头&?xml version=&1.0& encoding=&utf-8&?&这行,我们在工程中只需在 androidmanifest.xml 文件 的 Activity 节点中加入 android:theme=&@style/Theme.CWJ& 属性, 则这个 Activity 就使 用了这种主题风格,整个 xml 的关键代码如下: &resources& &style name=&Theme.CWJ& parent=&android:Theme&& &item name=&android:windowBackground&&@drawable/android123&/item& &/style& &/resources&其中上面的代码中,我们定义设置全局 android:windowBackground 即背景值为 /res/drawable 中的 android123 图片为背景, 更多的属性定义可以参考 view 的 layout xml 属性设置,比如我们设置所有字体颜色、大体大小和样式, 可以在 style 节点中加入: &item name=&android:textColor&&#fff&/item& &item name=&android:textSize&&14sp&/item& &item name=&android:textStyle&&bold&/item& 当然我们可以将上面的 android123 的图片改进下, 使用一个 xml 文件替代, 比如使用 bitmap 对象,则/res/drawable/android123.xml 的完整代码变为 &?xml version=&1.0& encoding=&utf-8&?& &bitmap xmlns:android=&http://schemas.android.com/apk/res/android& android:src=&@drawable/cwj_image& android:tileMode=&repeat& /& 这里我们使用了一个 bitmap 对象来解析 cwj_image 图片,当然这里可以识别各种类型 的图片,其中 android:tileMode 是 bitmap 的内部属性,其中 tileMode 设置为 repeat 代 表重复,这样可以节省 bitmap 资源,比如我们的背景是一层楼,那么全屏可以显示同样的 为 5 层效果,而图片仅是一层大小,对于资源利用相对更高。 当然 bitmap 的属性 tileMode 的值为 repeat 外还有其他的值比如 clamp、mirror,这 些值并没有在 SDK 中并没有找到定义, 通过上次 Android 开发网的 Android 自定义 View 以 及 layout 属性全攻略 一文,我们可以联想到 bitmap 属于 android.graphics.Bitmap 包, 由于是 android 框架,所以下载 git 的 base 包,找到该类,类的实例化时 android123 已经 在 Android 自定义 View 以及 layout 属性全攻略 说的很清楚, 所以我们定位到 res\values 中找到 attr.xml 有关 bitmap 的定义即可, 有关 bitmap 的更多属性如 antialias、 filter 和 dither 都可以找到使用。27. android 调试工具 monkey 压力测试实战很多 Android 开发者可能因为没有充分测试自己的软件造成很容易出现 FC(Force Close)的问题,这里我们可以通过使用 Android 固件中自带的 monkey 工具来做软件的压力 测试,monkey 工具可以模拟各种按键,触屏,轨迹球、activity 等事件,这里 Android123 提示大家说白了 monkey 就是一个小猴子随机狂玩你的 android 软件, 看看会不会产生异常。 具体的使用我们通过 Android SDK 给我们的 adb 调试桥链接设备或模拟器, 进入 Linux Shell 状态,当然我们可以输入 adb shell 获取设备的 shell,也可以直接通过 adb 命令执行,比 如说 adb shell monkey 来查看 monkey 工具中的参数说明,如图: 我们要测试的 apk 文件要在 android 设备中已经安装, 当然模拟器中也可以测试的。 执行 adb shell monkey -p cn.com.android123.cwj -v 100 我们执行这句的中包含了 p 参数,这里代表已安装软件的 packageName, v 代表查看 monkey 生成的详细随机事件名, 而 最后的数字 100 为我们测试的 随机事件数量为 100.有关更多的测试方法,请查看上图中的参数,整个测试比较简单单很 有效,不妨试试。28. 自定义 View 有关 Android 的自定义 View 的框架今天我们一起讨论下; 对于常规的游戏,我们在 View 中需要处理以下几种问题: 1.控制事件 2.刷新 View 3. 绘制 View 1.对于控制事件,今天我们只处理按键事件 onKeyDown,以后的文章中将会讲到屏幕触 控的具体处 理 onTouchEvent 以及 Sensor 重力感应等方法。 2.刷新 view 的方法这里主要有 invalidate(int l, int t, int r, int b) 刷新局部, 四个参数分别为左、上、右、下。整个 view 刷新 invalidate(),刷新一个矩形区域 invalidate(Rect dirty) ,刷新一个特性 Drawable, invalidateDrawable(Drawable drawable) ,执行 invalidate 类的方法将会设置 view 为无效,最终导致 onDraw 方法被重 新调用。由于今天的 view 比较简单,提示大家如果在线程中刷新,除了使用 handler 方式 外,可以在 Thread 中直接使用 postInvalidate 方法来实现。 3.绘制 View 主要是 onDraw()中通过形参 canvas 来处理; 相关的绘制主要有 drawRect、drawLine、drawPath 等等。 view 方法内部还重写了很多接口, 其回调方法可以帮助我们判断出 view 的位置和大小, 比如 onMeasure(int, int) Called to determine the size requirements for this view and all of its children. 、onLayout(boolean, int, int, int, int) Called when this view should assign a size and position to all of its children 和 onSizeChanged(int, int, int, int) Called when the size of this view has changed. 具体的作用,大家可 以用 Logcat 获取当 view 变化时每个形参的变动。 下面 cwjView 是我们为今后游戏设计的一个简单自定义 View 框架,我们可以看到在 Android 平台自定义 view 还是很简单的,同时 Java 支持多继承可以帮助我们不断的完善复 杂的问题。 public class cwjView extends View { public cwjView(Context context) { super(context); setFocusable(true); //允许获得焦点 setFocusableInTouchMode(true); //获取焦点时允许触控 } @Override protected Parcelable onSaveInstanceState() { //处理窗口保存事件 Parcelable pSaved = super.onSaveInstanceState(); Bundle bundle = new Bundle(); //dosom } @Override protected void onRestoreInstanceState(Parcelable state) { //处理窗口还原事 件 Bundle bundle = (Bundle) //dosomething super.onRestoreInstanceState(bundle.getParcelable(&cwj&)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) //处理窗口大 小变化事件 { super.onSizeChanged(w, h, oldw, oldh); } @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //如果不让父类处理 记住调用 setMeasuredDimension } @Override protected void onLayout (boolean changed, int left, int top, int right, int bottom) { super.onLayout (changed,left,top, ight,bottom) ; } @Override protected void onDraw(Canvas canvas) { Paint bg = new Paint(); bg.setColor(Color.Red); canvas.drawRect(0, 0, getWidth()/2, getHeight()/2, bg); //将 view 的左上 角四分之一填充为红色 } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); //让父类处理屏幕触控事件 } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { //处理按键事件,响应 的轨迹球事件为 public boolean onTrackballEvent (MotionEvent event) switch (keyCode) { case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_DPAD_CENTER: //处理中键按下 default: return super.onKeyDown(keyCode, event); }
} } // 上面我们可以看到 onMeasure 使用的是父类的处理方法, 如果我们需要解决自定义 View 的大小,可以尝试下面的方法 @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { height = View.MeasureSpec.getSize(heightMeasureSpec); width = View.MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(width,height); //这里面是原始的大小, 需要重新计算可以 修改本行 //dosomething }29. Canvas 和 Paint 实例昨天我们在 Android 游戏开发之旅三 View 详解中提到了 onDraw 方法,有关详细的实 现我们今天主要说下 Android 的 Canvas 和 Paint 对象的使用实例。 Canvas 类主要实现 了屏幕的绘制过程,其中包含了很多实用的方法,比如绘制一条路径、区域、贴图、画点、 画线、渲染文本; 下面是 Canvas 类常用的方法,当然 Android 开发网提示大家很多方法有不同的重载 版本,参数更灵活。 void drawRect(RectF rect, Paint paint) //绘制区域,参数一为 RectF 一个区域 void drawPath(Path path, Paint paint) //绘制一个路径,参数一为 Path 路径对象 void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //贴图, 参数一就是我们常规的 Bitmap 对象,参数二是源区域(这里是 bitmap),参数三是目标区域 (应该在 canvas 的位置和大小), 参数四是 Paint 画刷对象, 因为用到了缩放和拉伸的可能, 当原始 Rect 不等于目标 Rect 时性能将会有大幅损失。 void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) //画线,参数一起始点的 x 轴位置,参数二起始点的 y 轴位置,参数三终点的 x 轴水平位置,参数四 y 轴垂直位置,最后一个参数为 Paint 画刷对象。 void drawPoint(float x, float y, Paint paint) //画点,参数一水平 x 轴,参数 二垂直 y 轴,第三个参数为 Paint 对象。 void drawText(String text, float x, float y, Paint paint) //渲染文本,Canvas 类除了上面的还可以描绘文字,参数一是 String 类型的文本,参数二 x 轴,参数三 y 轴, 参数四是 Paint 对象。 void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) //在路径上绘制文本,相对于上面第二个参数是 Path 路径对象; 从上面来看我们可以看出 Canvas 绘制类比较简单同时很灵活,实现一般的方法通常 没有问题, 同时可以叠加的处理设计出一些效果, 不过细心的网友可能发现最后一个参数均 为 Paint 对象。如果我们把 Canvas 当做绘画师来看,那么 Paint 就是我们绘画的工具,比 如画笔、画刷、颜料等等。 Paint 类常用方法: void setARGB(int a, int r, int g, int b) //设置 Paint 对象颜色,参数一 为 alpha 透明通道 void setAlpha(int a) //设置 alpha 不透明度,范围为 0~255 void setAntiAlias(boolean aa) //是否抗锯齿 void setColor(int color) //设置 颜色,这里 Android 内部定义的有 Color 类包含了一些常见颜色定义: void setFakeBoldText(boolean fakeBoldText) //设置伪粗体文本 void setLinearText(boolean linearText) //设置线性文本 PathEffect setPathEffect(PathEffect effect) //设置路径效果 Rasterizer setRasterizer(Rasterizer rasterizer) //设置光栅化 Shader setShader(Shader shader) //设置阴影 void setTextAlign(Paint.Align align) //设置文本对齐 void setTextScaleX(float scaleX) //设置文本缩放倍数,1.0f 为原始 void setTextSize(float textSize) //设置字体大小 Typeface setTypeface(Typeface typeface) //设置字体,Typeface 包含了字 体的类型,粗细,还有倾斜、颜色等。 void setUnderlineText(boolean underlineText) //设置下划线最终 Canvas 和 Paint 在 onDraw 中直接使用 : @Override protected void onDraw(Canvas canvas) { Paint paintRed=new Paint(); paintRed.setColor(Color.Red); canvas.drawPoint(11,3,paintRed); //在坐标 11,3 上画一个红点 } 下一次将会具体讲到强大的 Path 路径,和字体 Typeface 相关的使用。30. View 类详解在 Android 游戏开发之旅二中我们讲到了 View 和 SurfaceView 的区别,今天 Android123 从 View 类开始着重的介绍 Android 图形显示基类的相关方法和注意点。 自定义 View 的常用方法: onFinishInflate() ; 当 view 中所有的子控件均被映射成 xml 后触发: onMeasure(int, int) 确定所有子 元素的大小 onLayout(boolean, int, int, int, int) ; 当 View 分配所有的子元素的大小和位置时触发 : onSizeChanged(int, int, int, int) ; 当 view 的大小发生变化时触发 : onDraw(Canvas) view 渲染内容的细节 : onKeyDown(int, KeyEvent) //有按键按下后触发 onKeyUp(int, KeyEvent) //有按键按下后弹起时触发 onTrackballEvent(MotionEvent) //轨迹球事件 onTouchEvent(MotionEvent) // 触屏事件 onFocusChanged(boolean, int, Rect) //当 View 获取或失去焦点时触发 onWindowFocusChanged(boolean) //当窗口包含的 view 获取或失去焦点时触发 onAttachedToWindow() //当 view 被附着到一个窗口时触发 onDetachedFromWindow() //当 view 离开附着的窗口时触发;该方法和 onAttachedToWindow() 是相反的。 onWindowVisibilityChanged(int) //当窗口中包含的可见的 view 发生变化 时触发 以上是 View 实现的一些基本接口的回调方法, 一般我们需要处理画布的显示时, 重写 onDraw(Canvas)用的是最多的: @Override protected void onDraw(Canvas canvas) { //这里我们直接使用 canvas 对象处理当前的画布,比如说使用 Paint 来选择要填 充的颜色 Paint paintBackground = new Paint(); paintBackground.setColor(getResources().getColor(R.color.xxx)); //从 Res 中找到名为 xxx 的 color 颜色定义 canvas.drawRect(0, 0, getWidth(), getHeight(), paintBackground); //设置当 前画布的背景颜色为 paintBackground 中定义的颜色, 0,0 作为为起点, 以 以当前画布的宽 度和高度为重点即整块画布来填充。 具体的请查看 Android123 未来讲到的 Canvas 和 Paint, Canvas 中我们可以实现画路径, 在 图形, 区域, 而 Paint 作为绘画方式的对象可以设置颜色, 线。 大小, 甚至字体的类型等等。 } 当然还有就是处理窗口还原状态问题(一般用于横竖屏切换),除了在 Activity 中可以调用 外,开发游戏时我们尽量在 View 中使用类似: @Override protected Parcelable onSaveInstanceState() { Parcelable p = super.onSaveInstanceState(); Bundle bundle = new Bundle(); bundle.putInt(&x&, pX); bundle.putInt(&y&, pY); bundle.putParcelable(&android123_state&, p); } @Override protected void onRestoreInstanceState(Parcelable state) { Bundle bundle = (Bundle) dosomething(bundle.getInt(&x&), bundle.getInt(&y&)); //获取刚才存储的 x 和 y 信息 super.onRestoreInstanceState(bundle.getParcelable(&android123_state&)); } 在 View 中如果需要强制调用绘制方法 onDraw,可以使用 invalidate()方法,它有 很多重载版本, 同时在线程中的 postInvailidate()方法将在 Android 游戏开发之旅六中的 自定义 View 完整篇讲到。31. View 和 SurfaceView 在 Android 游戏当中充当主要的除了控制类外就是显示类,在 J2ME 中我们用 Display 和 Canvas 来实现这些,而 Google Android 中涉及到显示的为 view 类,Android 游戏开发 中比较重要和复杂的就是显示和游戏逻辑的处理。 这里我们说下 android.view.View 和 android.view.SurfaceView。 SurfaceView 是从 View 基类中派生出来的显示类, 直接子类有 GLSurfaceView 和 VideoView,可以看出 GL 和视频播放以及 Camera 摄像头一般均使用 SurfaceView! SurfaceView 到底有哪些优势呢? SurfaceView 可以控制表面的格式,比如大小,显示在屏幕中的位置,最关键是的提 供了 SurfaceHolder 类,使用 getHolder 方法获取; 相关的有: Canvas lockCanvas() Canvas lockCanvas(Rect dirty) 、 void removeCallback(SurfaceHolder.Callback callback)、 void unlockCanvasAndPost(Canvas canvas) 控制图形以及绘制,而在 SurfaceHolder.Callback 接口回调中可以通过下面三个抽象类可以自己定义具体 的实现; 比如第一个更改格式和显示画面: abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height) abstract void surfaceCreated(SurfaceHolder holder) abstract void surfaceDestroyed(SurfaceHolder holder) 对于 Surface 相关的,Android 底层还提供了 GPU 加速功能,所以一般实时性很强的 应用中主要使用 SurfaceView 而不是直接从 View 构建, OpenGL 中的 GLSurfaceView 也是从 该类实现。32. Android 程序内存管理必读Android 作为以 Java 语言为主的智能平台对于我们开发一些高性能和质量的软件来 说了解 Android 程序内存管理机制是必须的。 Android 的 Dalvik VM 在基础方面和 Sun JVM 没有什么大的区别仅仅是字节码的优 化, 我们要知道什么时候用 gc 什么时候用 recycle 以及到底用不用 finalization, 因为 Java 对内存的分配只需要 new 开发者不需要显示的释放内存, 但是这样造成的内存泄露问题的几 率反而更高。1.对于常规开发者而言需要了解 Java 的四种引用方式,比如强引用,软引用,弱引用 以及虚引用。一些复杂些的程序在长期运行很可能出现类似 OutOfMemoryError 的异常。 2.并不要过多的指望 gc,不用的对象可以显示的设置为空,比如 obj=null,这里 Android123 提示大家, java 的 gc 使用的是一个有向图, 判断一个对象是否有效看的是其他 的对象能到达这个对象的顶点,有向图的相对于链表、二叉树来说开销是可想而知。 3.Android 为每个程序分配的对内存可以通过 Runtime 类的 totalMemory() freeMemory() 两个方法获取 VM 的一些内存信息,对于系统 heap 内存获取,可以通过 Dalvik.VMRuntime 类的 getMinimumHeapSize() 方法获取最小可用堆内存, 同时显示释放软 引用可以调用该类的 gcSoftReferences() 方法,获取更多的运行内存。 4.对于多线程的处理,如果并发的线程很多,同时有频繁的创建和释放,可以通过 concurrent 类的线程池解决线程创建的效率瓶颈。 5. 不要在循环中创建过多的本地变量。 有关 Android 和 Java 的系统性能分析, Android123 将在以后的文章中详细讲述如何调试 Java 分析内存泄露以及 Android 上的 gdb 调试器分析得出内存性能改进。33. Android 中内嵌字体实现个性化在 android 中我们的应用可以灵活的内嵌自己的字体文件, 实现各个手机上可以正常的 显示个性化文字, 我们都知道 TextView 的 setTypeface 方法可以设置目标文字的显 示特性, 比如字体、 颜色、 粗体、 斜体等。 我们直接找一个 TrueTypeFont 的字体文件即. 对于 Win32 系统的用户可以直接在 Windows/fonts 文件夹中能找到很多。比如微软 雅黑就不错,可是体积太大,由于 Android 的 Assets 类有单个文件 1MB 体积的限制,我们 先找个英文字体做测试。 这里我们将字体文件 android123.ttf 放到工程的 assets 文件夹的 fonts 目录中。 Typeface tf = Typeface.createFromAsset(ge}

我要回帖

更多关于 su插件怎么用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信