[Android问答] px、dp和sp,这些单位有什么区别?

相信每个Android新手都会遇到这个问题,希望这篇帖子能让你不再纠结。

px:

即像素,1px代表屏幕上一个物理的像素点;

px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同,如下图所示(图片来自android developer guide,下同)。

file

偶尔用到px的情况,是需要画1像素表格线或阴影线的时候,用其他单位如dp会显得模糊。

dp:

这个是最常用但也最难理解的尺寸单位。它与“像素密度”密切相关,所以首先我们解释一下什么是像素密度。假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算出在这部手机的屏幕上,每英寸包含的像素点的数量为240/1.5=160dpi(横向)或320/2=160dpi(纵向),160dpi就是这部手机的像素密度,像素密度的单位dpi是Dots Per Inch的缩写,即每英寸像素数量。横向和纵向的这个值都是相同的,原因是大部分手机屏幕使用正方形的像素点。

不同的手机/平板可能具有不同的像素密度,例如同为4寸手机,有480x320分辨率的也有800x480分辨率的,前者的像素密度就比较低。Android系统定义了四种像素密度:低(120dpi)、中(160dpi)、高(240dpi)和超高(320dpi),它们对应的dp到px的系数分别为0.75、1、1.5和2,这个系数乘以dp长度就是像素数。例如界面上有一个长度为“80dp”的图片,那么它在240dpi的手机上实际显示为80x1.5=120px,在320dpi的手机上实际显示为80x2=160px。如果你拿这两部手机放在一起对比,会发现这个图片的物理尺寸“差不多”,这就是使用dp作为单位的效果,见下图。

file

更新20140701: 是不是所有android手机的屏幕宽度用dp衡量都是固定值(例如320dp)呢?答案是否定的,如果写一个程序画宽度等于320dp的横线,在不同手机上运行,会发现在有些手机上横线比手机屏幕短,有些则比屏幕长,在平板上与手机上相比差别则更加明显。

dip:

与dp完全相同,只是名字不同而已。在早期的Android版本里多使用dip,后来为了与sp统一就建议使用dp这个名字了。

sp:

与缩放无关的抽象像素(Scale-independent Pixel)。sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。

还有几个比较少用到的尺寸单位:

mm:

即毫米;

in:

即英寸,1英寸=2.54厘米(约);

pt:

1pt=1/72英寸=0.035厘米;

最佳实践:

文字的尺寸一律用sp单位,非文字的尺寸一律使用dp单位。例如textSize="16sp"、layout_width="60dp";偶尔需要使用px单位,例如需要在屏幕上画一条细的分隔线时:

<View layout_width="match_parent" layout_height="1px"/>

最后,推荐一张Android UI设计参考图:Android Design Cheat Sheet

参考资料:

Difference of px, dp, dip and sp in Android?
Supporting Multiple Screens

DisplayMetrics

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/06/2757300.html

Android开发问题汇总

注:Android 5.0的问题总结在这个帖子里。

1、用(SDK starter package)的installler安装Android SDK时提示无法找到JDK,但实际上机器上已经安装了JDK。

一个对不少人有效的解决方法是看到此提示时先点一下“回退”按钮,再点下一步,就会发现JDK被找到了。参考链接

但在我机器上这个方法不起作用。所以我选择不使用installer,而是下载zip格式的文件,解压缩后运行SDK Manager.exe即可。参考链接

2、在Eclipse里新建一个Android项目,运行/调试时提示Could not find xxx.apk!

网上提到的大部分解决方案是clean整个项目,或选fix project properties菜单项。但对我的环境不起作用。

升级到Eclipse 3.7后,新创建的android项目在Build阶段报告一个异常sun/security/x509/X500Name,怀疑与所用的jdk(IBM JDK6)有关。果然,卸载IBM JDK并安装SUN JDK后问题解决。

3、让程序适应不同的屏幕分辨率

可参考这个链接:Android屏幕分辨率问题

4、在android模拟器里用10.0.2.2可访问宿主机。

5、在setWidth()方法里指定的宽度是以pixel为单位的,如何转换为使用dip(device independent pixels)为单位?

使用下面的代码,参考链接 

// Converts 14 dip into its equivalent px
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics());

6、弹出式对话框的用法,这个链接介绍得比较详细。 

7、导出apk文件时需要签名,这个链接比较详细。 

8、android界面设计原则,参考这个链接

9、使用merge(而不是layout)可以达到在避免重复写layout的同时减少layout的数量。

10、真机USB调试比用AVD调试快得多,设置也很简单,见这个链接。不过10.0.2.2不能用了,真机可以通过wifi访问局域网内的服务器。

11、android-ui-utils,一个不错的在线Android图标生成器,地址在此

12、调试时如果出现莫名其妙的空指针错误,例如findViewByID()返回null,先试试clean一下整个project,通常都能解决。

13、让ListView里无数据时显示一行信息。

参考链接,注意ListView和TextView的id。

14、用自己的图标替换ListView里的RadioButton

在用作row的layout里添加一个图片,在java code里覆盖adapter的getView()方法,根据ListView的getCheckedItemPosition()结果设置图片的可见性。注意不要通过setOnClickListener()方法设置view里的图片可见性,因为ListView只维护可见的那些row控件,这样做会导致很奇怪的结果(点第一条记录结果第二条记录被选中,并且滚动ListView时选中状态还会随机变化,见此链接)。

另外一个办法是通过style设置checkMark为所需要的图标,可能是更简单的解决方案(还没试)。

Update: 以上结果基于对ListView实现不了解的情况,其实使用RadioButton是可以实现的,见#22。

15、ListView的selection和choice是完全不同的,所以不要指望ListView#clearChoice()能清楚你已经是checked状态的那些item。

16、屏幕方向变化时(横屏->竖屏,或反过来),ListView里发生变化的内容丢失,状态回到开始时的样子。

当屏幕方向发生变化时,android会重建整个Activity以便你构造更适合某个方向的UI。为了避免这种情况,在AndroidManifest.xml里的那个Activity声明上增加android:configChanges="orientation" 即可。详见参考链接

17、各手机平台仿真器/模拟器的下载链接

18、AlertDialog#show()方法是不会阻塞当前线程的。

19、让不同Activity之间进行通信,例如一个TabActivity里有多个Activity,它们之间需要传递一些消息。

可使用BroadcastReceiver机制。 参考链接。要注意的是,tab还未启动时是无法接收到消息的,所以要在tabhost所在的activity里也接收消息,当tab启动时传给它。

20、Android SDK里的style和theme文档

见此链接

21、Tab的样式。

Android的tab样式问题比较多,不同版本的样式也不一样。这里有一个自定义样式的参考链接

22、在ListView里使用单选/复选按钮。

问题很多,这个链接看起来解决了问题,但在我的环境里没试验成功。

Update: 以复选按钮为例,本质的问题在于ListView里的复选按钮不知道对应的model是哪个,需要事先用CheckBox#setTag(myModelObject)关联,onclick事件里用getTag()改变其选中状态。建议看这篇文档。 

23、strings.xml里定义的字符串里增加参数。

在字符串里用“%1$s”、“%2$d”表示参数的序号和类型,然后用String.format()方法赋值。参考这个链接

24、嵌入条码/二维码扫描功能

使用zxing。方法是在手机上先安装BarcodeScanner.apk,然后在程序里调用其提供的Activity,该Activity会返回扫描结果。 参考链接

25、改变ListView里每个Row的背景颜色

直接在getView()里写view.setBackgroundColor()是不行的。正确的方法是先在drawable目录里建一个xml文件,自己起名如my_row.xml,内容如下(关键是第一个和第四个<item>):

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:state_selected="true" android:drawable="@android:color/transparent" />
    <item android:state_selected="true" android:drawable="@android:color/transparent" />
    <item android:state_pressed="true" android:state_selected="false" android:drawable="@android:color/transparent" />
    <item android:state_selected="false" android:drawable="@color/solid_red" />
</selector>

然后在getView()里这样写就可以了:

if (item.getStopId().equals(User.stopId)) {
    view.setBackgroundResource(R.drawable.my_row);
} else {
    view.setBackgroundResource(android.R.drawable.list_selector_background);
}

参考链接1 参考链接2 参考链接3

26、在一个TableLayout里让Button宽度相同并占满表格宽度(想象一个由按钮组成的九宫格)

如果Button上的文字不长,按一般的方法就可以实现,例如这个链接。当Button上的文字很长,还是会使布局变乱,每个列的长度将不一样。解决方案是将按钮的layout_width设为0,layout_height设置为所需要的值,需要换行时将singleLine设置为false,最后将按钮的gravity设置为center。

27、对切换屏幕方向的处理

参考stackoverflow上的一个典型讨论

当MyActivity位于一个TabActivity里时,我做了一些实验表明, TabActivity是否声明android:configChanges="keyboardHidden|orientation"与MyActivity无关,只有MyActivity做了上述声明后才会在改变方向时触发onConfigurationChanged()方法。

28、strings.xml里的字符串包含html格式标签时

需要用<Data><![CDATA[...]]></Data>把html代码包起来。参考链接

29、在AlertDialog里用ListAdapter(如ArrayAdapter)时,文字不显示。

view的resourceId要用select_dialog_singlechoice而不能用simple_list_item_single_choice,否则文字颜色会与背景颜色相同而无法显示。参考链接

30、在android程序里使用第三方包的配置方法

参考这个链接成功。

31、定制tabhost的外观

这方面的需求和问题网上都很多,主要的解决方案有基于tabhost的和基于radiobutton的两大类,这里有几个可以参考的链接。链接1链接2链接3

32、取得当前屏幕方向

有好几个方法可以得到orientation值,但一些得到的值不对,例如getWindowManager().getDefaultDisplay().getOrientation()getReqestedOrientation()。我实验下来比较靠谱的是getResources().getConfiguration().orientation。

33、Activity里第一个View是EditText的时候,软键盘自动弹出。

似乎只是一些机型会这样做。要阻止软键盘弹出,可在onCreate()里加一行代码。参考链接

this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

34、Dialog的theme问题

使用Theme.Light主题时,Dialog显示不正常。相关讨论:链接1链接2

35、注意区分CheckBox和RadioButton的OnClickListener和OnCheckedChangeListner

使用前者时,当在代码里执行myCheckBox.setChecked(true),不会触发事件,而后者会触发事件。

36、监视GPS开启/关闭事件

用GpsStatus.Listener不可靠(事件不上来),这个链接的方法是监视Settings里的事件:参考链接

37、仿真器横屏

快捷键ctrl+f11。有人提到ctrl+f12和numpad 7也可以,但我这里不起作用。

38、用JAXB生成KML对应的Java Code时会遇到一些问题,解决方法如下:

1)写一个binding文件如bindings.xjb放在与ogckml22.xsd同一目录,内容可参考这个链接(根据错误提示要删除几行)

2)在命令行里加-extension

3)在命令行里加-target 2.1(否则生成的java类的annotation带有jdk6不识别的关键字"required")

完整的命令行如下:

xjc -xmlschema -verbose -extension -b bindings.xjb -d src -target 2.1 ogckml22.xsd

39、ListPreference的entryValues只能使用string-array

如果使用了integer-array,在点击这个preference项时会产生一个空指针异常,相关讨论见链接1链接2

Update: 又发现一个ListPreference的新问题,即使用string-arraydefaultValue值也不能取太大的(超过Integer.MAX_VALUE)整数,否则defaultValue不起作用(选项没有缺省被选中)。 真是问题多多。android版本2.2。

40、Android提供了方便的Share机制,但一般都是把文字share到微博或SMS,如何能"share"文字到SD卡文件呢?

这个链接描述了同样的问题,等待有人回答。 目前的想法是,在程序里自己实现一个接受ACTION_SEND的activity,做法可参考此链接、或此文章

41、一个在线查看kml文件的网站,供参考。

GPS Visualizer 

42、关于onSaveInstanceState()的使用

通常与onCreate()配合,而非onRestoreInstanceState(),参考这个链接

2015/5/18补充:一个误区是onSaveInstanceState()方法是当activity被系统销毁时才调用,其实是当activity“变得容易被系统销毁”时就会被调用,例如当按下home键时,此时activity不再在前台显示,也就变得容易被销毁了。参考链接参考链接2

43、ListView里点击一个item背景色不变为橘黄(缺省的反馈颜色),各种OnClick事件不被触发。

一种可能是在item的布局文件(如foo_list_row.xml)里使用了下面的这些属性(自动滚动显示文字时常会用到),去掉后即可恢复正常:

android:ellipsize="marquee" android:scrollHorizontally="true"
android:marqueeRepeatLimit="marquee_forever" android:focusable="true"
android:focusableInTouchMode="true"

44、实现不停向上滚动的ListView

假设你有一个长长的list需要自动展示,方式是每隔几秒向上滚动一行,有点类似TextView的marquee功能(跑马灯?)。这个需求可以通过Handler实现,具体参考这个链接;当列表滚动到最后一行时,直接滚回第一行显得很生硬,可以用这个链接里提供的方法解决。

45、在Button的文字旁加图片

<Button>里使用android:drawableLeft="@drawable/my_icon" android:gravity="left|center_vertical"即可,类似的可以加在右侧或上下方。但如果Button有其他状态时,需要用selector指定不同状态下的图片。此外,图片的大小是不会自动根据Button调整的。

46、旧版本兼容

参考sdk文档里的这个文章

47、轻松实现圆角背景

不需要做圆角图片,看看这个链接,很方便,注意把angle改为45的倍数否则运行时报错。

48、Android内存泄漏检测

在DDMS可以查看heap使用情况,大概了解是否有内存泄漏。DDMS还可以dump出.hprof文件,后者可以用Eclipse MAT打开,进一步分析错误原因。注意,startActivity()后要根据情况决定是否调用finish()方法(如果需要back则不finish(),在适当的时机用FLAG_ACTIVITY_CLEAR_TOP一并回收内存空间),未finish()的activity是会一直占用内存的。

49、Android的第三方library

这个链接总结了不少。 

50、Android UI设计模式

参考这个链接

51、Google Map扩展的使用。

参考这个链接

52、当Spinner是invisible状态时,貌似调用mySpinner.setSelection(i) 不会触发其onItemSelected()事件。

53、实现gzip压缩服务器返回的json对象时,注意要response.setContentType("application/json"),并且在server.xml里把application/json设置到compressableMimeType里才能实现。我因为前一个原因浪费了三四个小时。

54、对AlertDialog.Builder应用定制的theme

使用ContextThemeWrapper,用法参考这个链接。但后来发现这个方法不起作用,有一种说法是这个方法只对部分android版本有效,也有说其根本无效的(都是在so上)。用AlertDialog.Builder的setVIew()方法也是有问题的,因为你会发现在这个view的旁边一圈(假设你的view是浅色背景)还是黑色的,效果很不好。其实如果只是要将对话框设置为白色背景,可以使用AlertDialog.Builder的setInverseBackgroundForced(true)方法,很简单,很直接。

55、从gallery选择图片的一些可用参数

参考这个链接,和这个相关issue (文件得不到原始图片,只能得到缩小后的图片)。

56、从gallery取图片时要注意内存是有限的,而图片可能很大。

利用inSampleSize可以帮助解决,参考这个链接的代码。

57、当程序进程被系统kill掉(常常在内存紧张时发生):

HttpClient的session可以用persist方式保留;

其余数据建议保留在SharedPreferenes里;

onActivityResult()里是能够获得另一个activity传回来的intent里的参数的;

58、连接到真机时提示Unable to open sync connection!

原因未知,解决方法是在真机的设置里去掉usb调试,然后重新勾选。

59、得到GPS状态(寻星或已获得位置)

这个问题看似简单,但Android里的LocationListener#onStatusChanged()方法工作不正常,表现为在大多数版本下都不会被调用。这个链接里提供的方法试了一下好像是可以用的。

60、在Android里画统计图(柱图饼图等等)

这个链接里给出了不少解决方案,我暂时选择的是aChartEngine,用法和JFreechart比较类似,参数超多,目前的活跃度也不错。

61、进程经常被kill

可以考虑启动一个service(即使什么都不做),这样进程的“重要性”就变得很高,因此就不容易被kill了。

62、“Receiver not registered”错误

执行unregisterReceiver(myReceiver)方法时,如果之前没有注册过myReceiver,会抛出这个异常。参考链接

63、应用在后台运行,需要弹出对话框(例如触发自service产生的事件)时报错:“BadTokenException: Unable to add window”

利用isFinishing()测试activity是否在后台,参考链接

64、Emulator太慢了

试试Android x86,据说比官方的快很多(我还没试过,正在下载)。 参考链接

65、禁止自动切换到横屏模式

<activity android:name=".SomeActivity" android:label="@string/app_name"
          android:screenOrientation="portrait">
</acivity>

参考链接

66、几个众包模式的beta测试平台

iOS最著名的是testflight,也有其他一些类似的测试平台支持android,详见这个链接

67、实现锁机后黑屏但不出现锁机画面

getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

还有一个功能类似的flag是FLAG_DISMISS_KEYGUARD,区别在于前者只对当前activity有效,后者全局有效。另外前者对安全锁屏也有效,后者则只对普通锁屏有效。附一个参考链接

但是前者有一个问题,就是在两个都设置了FLAG_SHOW_WHEN_LOCKED的activity间切换时,可能锁屏界面会闪一下。见此问题报告

68、一些可用于android应用性能测试和内存泄漏检测的工具

这个链接

69、进度条的一个bug

重设setMax()以后显示的进度百分比不正确,至少在Android 2.1 (API Level 7)里有这个问题。 见这个链接这个链接

70、当ListView的item里包含控件(如按钮、复选框)时 ,这个item无法被选中。

Android不允许选中ListView里带有focusable元素的item,解决方法是将该控件的focusable属性设置为false。参考链接

71、帮助做简单web测试的工具(构造并发送各种http请求)

这个链接里总结了不少。

72、Tab放在底部(仿iphone风格)

中文的教程看了好几个都不靠谱,so上的一个链接搞定,或者这个带有源码的教程

73、让ListView没有数据时也显示HeaderView/FooterView

技巧是让empty view包含headerview/footerview,见这个链接

74、在Google map上添加popup的方法

最简单的方法见这个链接

75、一个网站,可以搜索android相关项目的代码和资源。

链接在此

76、Android里的Search Activity不支持返回结果到调用其的Activity(因为onSearchRequested()方法没有调用startActivityForResult()启动search activity)

SO上有若干个提出此问题的帖子,例如链接1链接2等等,没有特别方便的办法解决,一个我没试过但看回复应该可行的方法见这个链接

77、查看apk文件内容的工具

推荐apktool,一个命令行工具,用法如下:

> apktool.bat d my.apk

可以还原所有的资源文件,但.java文件一般无法还原。

78、Android中使用的各类图标的标准尺寸

请参考sdk自带的guideline文档。 

79、Android的Searchable接口,无法让调用者获得查询结果。

参考这篇文档(抱歉链接已失效)可以实现,思路是自己用startActivityResult()启动搜索界面,然后在onActivityResult()里取出结果。我测试可行,但该文档有两处错误需要注意:

1) 是handleIntent()而非handleActivity()

2) 在startActivityResult()前最好intent.setAction(Intent.ACTION_SEARCH)一下。

另外,注意按该文章中提到的官方文档实现相应的newIntent()和onCreate()方法,以及在AndroidManifest.xml里设置调用者的android:launchMode="singleTop"。

80、很诡异的问题,有时EditText无法输入文字(软键盘正常弹出但字符进不去文本框),必须切换到另一个输入法才可以输入。

经测试,有些机型存在这个问题,具体原因还不详。以下链接可能与此有关:链接1

81、定时重复执行一段程序

要执行类似闹钟这样的功能,用AlarmManager配合BroadCastReceiver即可,网上有很多例子不再赘述。值得一提的是,在这个BroadCastReceiver里不要执行异步操作(例如异步访问一个远程服务、获取当前位置等等),因为onReceive()方法一旦执行结束,用于容纳BroadCastReceiver的进程随时可能被系统kill掉,导致异步操作结束后出现异常。解决的办法是在onReceive()方法里启动一个Service(我用的是startService,用bindService的方式可能也行),在Service里执行任何操作就可以了。参考BroadCastReceiver Life Cycle

2015/12/10注:AlarmManager也可以直接配合Service使用

2015/12/10注2:要查看手机上所有alarm,在命令行里执行adb shell dumpsys alarm > alarms.txt命令即可。

82、Monkey测试

> adb shell monkey -v -p com.my.app 100

MonkeyRunner可以进行更高级的测试。

83、用getIntent().getExtras().clear()无法清除掉extras里的数据

原因是getExtras()返回的是一个copy实例,用getIntent().removeExtra()可一个个清除。参考链接

84、用AlertDialog实现输入对话框时,若直接builder.setView(myEditText)文本框显得太长。

用dialog.setView()可以指定padding(注意是dialog.setView()而不是builder.setView()),具体见这个链接

85、在TextView里显示图片

通过Html.fromHtml()方法可以在TextView里显示HTML格式的文本,但只支持部分tag:

myTextVIew.setText(Html.fromHtml(myHtmlStr))

要在TextView里显示远程图片,必须向fromHtml()函数里提供一个ImageGetter对象,在它的getDrawable()方法里获取远程图像并转换为Drawable类型。示例代码请参考此链接

如果TextView设置了android:lineSpacingMultiplier属性,将导致图片显示的位置不正确(顶部多出一些空白),用android:lineSpacingExtra属性则没有这个问题。

如果图片加载比较慢将导致整个TextView空白很长时间,为解决这个问题需要异步加载图片,这样文字可以先出来,待图片下载完毕后再补充道文字中间。实现方法是扩展Drawable,具体方法参考这两个链接:链接1链接2 ,其中链接1的方案存在图片尺寸不正确的问题,原因是TextView#invalidate()没能起作用(原因不详),用链接2提供的方法可以解决,这个链接提到用textView.setText(textView.getText())也可以工作但我没试。

86、在TextView里显示列表(

  • 标签)

    Android的TextView只支持一小部分的html标签(见这个链接),缺省是不支持

      • 这样的列表标签的。通过TagHandler可以实现一个简单的列表效果(见此链接),但这个方法有个严重问题:当列表文字超过一行时,第二行的文字是顶头的没有缩进效果(见这个提问),而该问题暂时还没有好的解决方法。

        87、百度地图android sdk问题

        百度地图最大的问题:文档太烂!百度地图sdk版本比较多,网上充斥着各种版本的例子代码,官网上的例子代码也不清晰,在线文档含混不清。

        遇到过一个奇怪的问题,与这个帖子描述的情况相同,即第二次打开地图只能显示上次缓存过区域的地图,最后删除了所有onPause()onResume()onDestroy()里的百度地图相关方法才解决。

        88、启用Andoird设备的otg功能

        /system/etc/permissions目录里检查是否有名为android.hardware.usb.host.xml的文件,如果没有,新建一个内容如下:

        <permissions>
            <feature name="android.hardware.usb.host" />
        </permissions>

        89、命令行对一个未签名apk进行签名

        jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore my_application.apk alias_name

        参考连接

        90、Pull-to-refresh控件不显示内容问题

        将ListView替换为PullToRefreshListView,setAdapter()后发现列表中没有数据显示,调试发现adapter里是有数据的,而listView的getView()方法没有被执行到。最后发现原因是在布局文件的<com.handmark.pulltorefresh.library.PullToRefreshListView>元素中指定了android:visibility="gone"属性(在.java文件里在setAdapter()之前先调用了listView.setVisibility(View.VISIBLE)),在布局文件里去掉此属性,改为在activity的onCreate()里执行listView.setVisibility(View.GONE)后恢复正常,没有深入调查这两种方式对PullToRefreshListView有什么区别,对标准ListView是没有区别的。

        91、切换fragment时fragment显示不全

        fragmentTransaction.replace(R.id.layoutRoot, newTransaction)时,发现当layoutRoot是LinearLayout时,(有时)newTransaction的高度无法充满layoutRoot(虽然已经指定了高度为match_parent,指定高度为固定值也无效),后来将layoutRoot改为FrameLayout问题解决。

        提供了一些线索的参考链接

        92、利用Activity的android:process属性

        在开发一个视频播放器过程中,遇到一个问题,当视频还在缓冲时按返回键退出当前activity,应用失去反应长达数秒。后来在AndroidManifest.xml里对这个activity增加了android:process=":player"后问题解决,因为这个属性使得此activity在一个独立的进程运行,当activity被关闭时,此进程也被杀掉。这样,即使播放器有未完成的工作,也不会影响到主程序的UI线程了。

        需要注意,用PreferenceManager.getDefaultSharedPreferences()得到的SharedPreferences实例,是不支持跨进程访问的,因此在独立的进程里将无法向这样获得的SharedPreferences实例存取数据。解决的办法是改为使用context.getSharedPreferences(prefName, Context.MODE_MULTI_PROCESS)获取SharedPrefences实例。

        不过还是要特别注意,在多进程环境下,如果你用一个静态变量缓存了SharedPreference里的内容,由于静态变量在多个进程间不是共享的(Application对象也不是跨进程共享的),所以取出的结果可能不是你想要的。解决办法 1)去掉这个作为缓存的静态变量,每次都直接从SharedPreferences里取 2) 有人建议使用MemoryFile作为跨进程的机制,但我没有实际测试过,而且用起来应该会比Application对象繁琐。

        参考链接 参考链接2 参考链接3

        93、Service的START_STICKY与START_NOT_STICKY的区别

        参考这个链接:当系统由于资源不足可能会杀掉一个service,之后如果系统又有了足够资源,若被杀掉的service是START_STICKY的,则系统会调用其onStartCommand()方法恢复这个service;若被杀掉的service是START_NOT_STICKY的,则系统不会尝试恢复这个service。

        94、如何用纯java的方式获取apk文件版本信息?

        apk文件就是zip文件,解析并读取AndroidManifest.xml里的信息即可。开源项目android-apk-parser已经实现了这个功能,可以直接在java项目里使用。

        95、Android手机使用https代替http会带来明显的性能问题吗?

        对CPU消耗来讲,不会。见参考链接

        对网络消耗来讲,有一些影响。见参考链接

        关于在手机端“接受所有证书”做法的安全性,见参考链接

        注意:ssl处于tcp和http之间,使用https后,url也是加密过的,因此不需要担心api地址暴露(浏览器历史除外)。参考链接

        96、代码混淆

        project.properties里的#proguard.config=xxx这句前面的#去掉即可。会按照android sdk目录里的proguard-android.txt设置文件进行混淆,如果项目有特殊要求,在项目目录下的proguard-project.txt里进行设置。有两点注意:

        1) 只有在正式export apk时才会进行混淆,直接运行产生的bin/xxx.apk不会进行混淆;

        2) library工程里的混淆设置不起作用,以宿主工程的设置为准。

        97、Fragment里getActivity()有时返回为null

        一般当activity所在进程在后台被系统回收,然后用户重新回到这个进程时,activity重建后发生。解决方法是在activity的onSaveInstanceState()方法里保存fragment的状态:

        public void onSaveInstanceState(Bundle outState) {
            activity.getSupportFragmentManager().putFragment(outState, "current_fragment", currentFragment);
        }

        然后在activity的onCreate()方法里恢复:

        if (savedInstanceState != null) {
            currentFragment = (Fragment) activityBase.getSupportFragmentManager().getFragment(savedInstanceState, "current_fragment");
        }

        这样currentFragment所代表的fragment实例的getActivity()就不为null了。

        98、分级Preference界面布局的实现方式

        引用这篇文章的总结:Android 3.0之前:采用PreferenceScreen嵌套的方法;Android 3.0及之后:采用Preference Headers的方法。

        采用Preference Headers方法时,要求继承PreferenceActivity,但这样就无法同时继承ActionbarActivity了(问题链接)。这个第三方library可以基本解决这个问题。

        99、自定义进度条progressbar的风格

        这篇文章总结得很好,特记录在此:How-to: Customize Android progress bars

        100、Color State List不能用于android:background属性

        Android似乎不支持在android:background里指定Color State List资源(例如android:background="@color/bg_button_selector"会导致崩溃,但android:background="#ff0000"是可以的),得用Drawable State List才行,例如android:background="@drawable/bg_button_selector"参考链接

        搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2011/06/28/2092514.html

  • Palm OS开发常见问题和技巧

    1. 判断当前focus是否为field
    index=FrmGetFocus(form); if(index= =noFocus) return(false);
    field=FrmGetObjectPtr(form,index);
    1. FrmDoDialog()使用方法:
    FrmInitForm
    FrmDrawForm set form controls
    FrmDoDialog
    read form controls
    FrmDeleteForm 

    注意:FrmDoDialog()无法获得frmOpenEvent。

    1. 测试控件类型:
    switch (FrmGetObjectType(pForm, index)) { 
       case frmControlObj: 
       case frmFieldObj: 
       case frmScrollBarObj: 
       default:
    }
    1. 在程序里使用标准的edit menu:

    If your form has a menubar that consists of just the "Edit" menu, you can specify menu ID 10000 at form creation time. If your form has a menubar with several menus, you should specify your Edit menu like this, using PilRC notation:

    PULLDOWN "Edit"
    BEGIN
        MENUITEM "Undo" ID 10000 "U"
        MENUITEM "Cut" ID 10001 "X"
        MENUITEM "Copy" ID 10002 "C"
        MENUITEM "Paste" ID 10003 "U"
        MENUITEM "Select All" ID 10004 "S"
        MENUITEM "-" ID 10005
        MENUITEM "Keyboard" ID 10006 "K"
        MENUITEM "Grafitti Help" ID 10007 "G"
    END

    If you're using Constructor, just create an Edit menu with ID 10000, and the IDs for the items will be provided for you. http://www.palmoswerks.com/2001/11/16

    1. Push button的使用

    GroupID若为0则与普通button一样,若GroupID不为0则同组内保证只有一个被选中。 FrmSetControlGroupSelection给push button赋值。

    1. 关于PrefGetAppPreferences

    PrefGetAppPreferences要判断返回结果是否为noPreferenceFound

    1. 给文本框(Field)赋值
    static void SetFieldText(FormType *form, FieldType *field, Char* value){
        MemHandle newTextH;
        MemHandle oldTextH;
        Char *text;
        newTextH = MemHandleNew(20);
        text = MemHandleLock(newTextH);
        StrCopy(text, value);
        MemHandleUnlock(newTextH);
        oldTextH = FldGetTextHandle(field);
        FldSetTextHandle(field, newTextH);
        if (oldTextH)  
            MemHandleFree(oldTextH);
        if(FrmVisible(form))
            FldDrawField(field);
    }
    1. 关于CtlGetLabel()

    如果需要CtlGetLabel(),则在CtlSetLabel()时不应立即释放Char*参数,否则CtlGetLabel()得到的将是乱内容。

    This function stores the newLabel pointer in the control's data structure. It doesn't make a copy of the string that is passed in. Therefore, if you use CtlSetLabel, you must manage the string yourself. You must ensure that it persists for as long as it is being displayed (that is, for as long as the control is displayed or until you call CtlSetLabel with a new string), and you must free the string after it is no longer in use (typically after the form containing the control is freed). If you never use CtlSetLabel, you do not need to worry about freeing a control's label.

    1. 关于HideState()

    HideState()返回代码之一是statXXX而非sysXXX,Palm SDK参考有误。

    1. 最好不要使用全局变量,用Feature代替之。

    2. Simulator没有截屏的快捷键,用Alt+PrintScr代替之。

    3. 让modal dialog全屏的方法

    FormType* pOriForm = FrmGetActiveForm();
    pForm = FrmInitForm(KeyboardForm);
    FrmSetActiveForm(pForm);//Must
    FrmSetEventHandler(pForm, KeyboardFormHandleEvent);
    
    formWinH = FrmGetWindowHandle(pForm);
    WinSetConstraintsSize(formWinH, 160, 160, 160, 240, 240, 240);
    FrmSetDIAPolicyAttr(pForm, frmDIAPolicyCustom);
    PINSetInputTriggerState(pinInputTriggerDisabled);
    PINSetInputAreaState(pinInputAreaClosed);
    SysSetOrientation(sysOrientationLandscape);
    StatHide();
    1. 关于RepeatingButton

    RepeatingButton响应CtlRepeatEvent而非CtlSelectEvent

    1. Palm simulator与电脑同步

    可参考这个网址:http://duchaoqian.blogbus.com/logs/538520.html,注意电话号码用"00"

    1. 多行文本框

    Multi-line的text改变内容后要FldRecalculateField(textField, false);否则换行可能不正确。

    1. 关于下拉列表

    要产生popSelectEvent,在ctlSelectEvent里一定让handled=false

    1. 关于debug

    遇到不知原因的死机等错误,最有效的解决办法是排除法,用if(false){...}不断缩小范围直到找到导致错误的代码。 按下按钮后,若模拟器不是崩溃而是没有反应,很可能是程序陷入了死循环。

    1. JPilotDB的使用方法

    JPilotDB提供的lib文件太大,有4M多(因为包含了很多UI和相关lib),如果只是用于在Java里处理.pdb文件完全不需要它的全部内容,精简后的大小为96K,点击下载

    代码范例:创建一个.pdb文件

    try {
      //Construct the database
        PilotDBSchema schema = new PilotDBSchema();
        PilotDBDatabase database = new PilotDBDatabase("DB Name", "TypeID", "Creator", schema);
        for (int i = 0; i < 10; i++) {
            PilotDBRecord record = database.createRecord();
          record.setRecordData(new byte[]{});//set contents of the record
        }
        //Write to file
        FileOutputStream fos = new FileOutputStream("c:/test.pdb");
        database.write(fos);
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (PalmDbException e) {
        e.printStackTrace();
    }

    代码范例:读取一个.pdb文件

    try {
        //Read database from file
        FileInputStream fis = new FileInputStream("c:/test.pdb");
        PilotDBDatabase database = new PilotDBDatabase(fis);
        fis.close();
        //Read records of the database
        int recCount = database.getRecordCount();
        for (int i = 0; i < recCount; i++) {
            Record record = database.getRecord(i);
            byte[] bytes = record.getRecordData();
            //deal with the record
        }
    } catch (IOException e) {
        e.printStackTrace();
    } catch (PalmDbException e) {
        e.printStackTrace();
    }

     19. 用程序控制退出当前运行的程序

    EvtEnqueueKey (vchrLaunch, 0, commandKeyMask);
    1. Simulator里使用五维方向键(5-Way Navigator):
    • [Alt] + [Enter] = Select
    • [Alt] + [Left Arrow] = Left
    • [Alt] + [Right Arrow] = Right
    • [Alt] + [Up Arrow] = Up
    • [Alt] + [Down Arrow] = Down
    1. 关于Gadget。帮助文档里的例子可能比较旧了,回调(Callback)函数里的第一个参数FormGadgetType类型应改为FormGadgetTypeInCallback类型。此外,第三个void*类型的参数不能直接paramP->eType,要先转换为确定类型才能使用,例如在formGadgetHandleEventCmd里要先EventType* pToEvent = (EventType*) paramP;,然后才可以用 pToEvent->eType来判断事件类型。

    2. 在PODS里使用Palm OS Glue Library,除了在.c文件头部加上#include <PalmOSGlue.h>;外,还要设置这个project的linker配置,否则会提示"Undefined reference"。配置的方法祥见这里。摘抄如下:

      For managed make 68K projects, go to the project properties, and in the C/C++ Build panel, choose PRC-Tools Palm OS 68K Linker/General. Click the "New..." button in the Additional Libraries area, and enter this text into the dialog: -lPalmOSGlue; For a standard make 68K project based on the PalmSource template, in the file "makefile", modify the line for ADDITIONAL_LINK_LIBRARIES to read: ADDITIONAL_LINK_LIBRARIES = -lPalmOSGlue

    3. 判断Form里的对象是否可见:用FrmGlueGetObjectUsable()方法,注意要先加载Glue库。

    4. 根据新闻组里的言论以及自己的试验,FrmReturnToFrom(0)在Debug ROM里很可能有bug,会导致Simulator因内存问题崩溃。

    5. 若两个数据库的TypeID和CreatorID都相同,Palm将视其为同一数据库的两个版本,因此若要枚举出它们,DmGetNextDatabaseByTypeCreator()的第五个参数必须为false(有些应用可能恰恰不需要枚举出每个版本,而只需要最新版本,则应使用true)。

    6. 虽然数据库都是在内存里,但打开一个数据库的开销还是不能忽视,DmOpenDatabase()执行50次的时间大约有0.1秒。

    7. Palm SDK没有提供画圆的函数,可以用画圆角矩形的方法代替,让圆角的半径等于矩形边长一半即可。

    8. 关于使用表格控件的方法,这篇文章介绍的很详细,建议参考:http://www.mit.jyu.fi/~mweber/teaching/docs/palmos/book/ch08.htm

    搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2008/04/14/1152995.html

    四种方法修改Palm Simulator使用的ROM

    第一次运行Palm OS Garnet Simulator时,模拟器会提示你选择一个ROM文件,但这之后想换其他ROM文件时该怎么做呢?以下四种方法都可以实现:

    1. 在模拟器运行文件(palmsim.exe)所在目录找到palmsim.ini文件,修改里面的ROM项;
    2. 运行模拟器时加-rom参数;
    3. 启动模拟器时按住shift键,模拟器会像第一次运行一样提示你选择一个ROM文件;
    4. 在windows文件管理器里直接把ROM文件拖到palmsim.exe图标上启动。

    来自Palm OS Tools Documentation。

    Palm OS Developer Suite的安装和使用

    终于买了一个掌上电脑,我又开始关注Palm开发了。Palm上虽然可以安装Java虚拟机,但据我所知绝大多数用户受里的Palm并没有安装,让他们为了用你的一个小软件而装虚拟机不太现实,因此只能用C/C++了。大学里学的C语言一直没有实际应用过,所以经过这么多年也和没学差不多,最近通过看wj给我的书,对C语言又有了重新的认识。不过C语言是面向过程的,我在Java里积累的经验似乎帮不上什么忙,边走边看吧。我在“Palm开发”这个分类里记录学习过程,这是第一篇,先介绍一下PODS的使用方法。

    如果不熟悉Palm,可以先通过这篇文章了解一下Palm公司的历史和产品。没有掌上电脑也可以开发Palm应用,你可以在模拟器上运行和调试,但我相信把自己编写的应用程序拿在手里的感觉一定不错。

    Palm OS Developer Suite(PODS)是PalmSource提供的基于Eclipse的Palm应用程序开发工具,它包含了编译工具、调试工具、模拟器、资源编辑工具和Palm SDK等等,可以在这个地址下载。虽然很多人都使用CodeWarrier开发Palm应用程序,但PODS毕竟是PalmSource官方推出的开发工具,从最初的1.0到现在的1.2版本看来,支持得也不错,加上它是基于我们熟悉的Eclipse平台,更重要的一点它是免费的,所以我还是选择了PODS。当然,用什么工具只能在一定程度上影响开发速度,熟练以后工具间的差别就不那么明显了。

    我目前收集到的Palm开发资料主要有这几部分:首先是PalmSource网站上提供的不少pdf文档,对入门者比较有用的是“Palm OS Companion”和“Palm OS Reference”这两份,还有一个“UI Guideline”在设计窗体之前可以看看;另一个是PalmOS的开发者新闻组(news.palmos.com),比较活跃,到现在已经有超过10万个post了;最后就是纸版的图书,我找到的比较新的中文书籍是《PalmOS编程宝典(第二版)》,网上可以找到该书第一版的英文电子版,决定购买以前可以先看一下。

    PODS的安装很简单:运行你下载的安装文件就可以了(PODS没有Linux的版本,它带的模拟器也是运行在Windows里的,所以我暂时还是要用回Windows了),可能需要你的机器里事先装有JRE。安装以后在“开始”菜单里会出现PODS组,其中有两个模拟器(Simulator,注意和Emulator的区别),开发OS5及以下应用程序用Garnet Simulator,开发OS6应用程序用Cobalt Simulator,所以我们一般用前者,它的样子见图1。

    file
    图1 Garnet模拟器

    如果你用过Eclipse,对PODS的界面也不会感到陌生,基本上PODS就是在Eclipse里加了个透视图(Perspective)和一些向导,PODS 1.2版本带的Eclipse是3.0.1版本,显得有点过时了(也许可以把PODS那部分拿出来放在新版本Eclipse和CDT里,不知道能不能兼容),图2是PODS的运行界面。

    file
    图2 PODS的运行界面

    环境看得差不多了,现在试着创建第一个Palm应用程序吧。在新建向导里创建“Managed Make 68K C/C++ Project”,如图3。这种应用程序可以运行在各个版本的Palm上,具有最好的兼容性。

    file
    图3 创建Palm应用程序

    按下一步按钮,给项目起个名字“hello”,见图4。

    file
    图4 给项目起名

    按下一步按钮,这里要指定你的应用程序的一些属性,对普通应用程序来说,大部分可以不动,只有“Creator ID”这一项必须改为其他值。按正规的流程,我们首先要在palmos网站上注册自己唯一的ID,然后把这个ID填在这里,目的是避免应用程序间的冲突。因为现在只是试验,随便改成“HELL”就可以了,见图5。

    file
    图5 修改Creator ID

    按下一步按钮,在这一步里PODS提供了一些代码模板,这样可以不用从零开始写每个程序。我们选择“Sample Application”这一项,见图6,这样在向导结束后我们会得到一个很简单的应用程序。

    file
    图6 选择一个模板

    现在直接按Finish按钮结束向导,这时要稍等一会儿,PODS在生成必要的代码和编译它们。简单来看一下生成的代码,主要的程序文件是src目录下的AppMain.c文件,注意它的入口方法不是main()而是PilotMain(),这个文件里的内容以后的帖子里会说明;在rsc目录下生成了名为AppResources.xrd的文件,这是一个资源文件,如果你在PODS里双击它,会打开Palm OS资源编辑器,见图7,在资源编辑器里你可以编辑窗体、定义菜单、定义图标、定义字符串等等。

    file
    图7 Palm OS资源编辑器

    怎样在模拟器里运行这个应用程序呢?首先确认你已经启动了Garnet模拟器;然后在PODS里选择菜单“Run->Run...”,这将弹出一个对话框。在对话框左边选中“Palm OS Application”,然后按下面的New按钮,在“Palm OS Application”下面会出现一个新的节点(运行项);选中这个节点,在对话框右半部分把它的名称改为和项目名一样的“hello”,并确认“Files to install”框里只勾选了hello项目;点击Target属性页,在Device下拉列表里选择“Palm OS Garnet Simulator 5.4”,这个设置很重要,不要忽略。

    现在运行项已经配置好了,见图8,按下对话框右下方的Run按钮即可运行程序。注意,以上这个过程对一个项目只需要配置一次就够了,再需要运行可以在PODS的工具栏里直接按Run下拉按钮。

    file
    图8 为hello项目配置运行项

    我们第一个应用程序在模拟器上运行的界面如图9所示。

    file
    图9 第一个应用程序

    你可能已经注意到了,在项目的Debug目录下已经生成了hello.prc文件,这个文件可以直接在Palm设备上运行(方法和安装其他软件一样,通过同步,或者复制到扩展卡上,等等),图10是我们的hello项目在真正的Palm上运行的样子。

    file
    图10 运行在Palm T|X上的hello项目

    搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/06/09/421821.html

    让我试试”eRCP”

    我对掌上电脑有兴趣已经好几年了,不过到现在都没能拥有一台实物,毕竟价格挺高的,而且也不是缺它不可的情况。今天在Eclipse网站上下载了一个eRCP,这好象是去年底开始的新项目,是Eclipse RCP向嵌入式应用的发展,相当于对RCP中各个组成部分的精简实现,因为那些设备对应用程序的大小十分敏感。

    由于以前没有实际做过这方面的开发,对掌上电脑的使用也不了解,所以让eRCP自带的例子在模拟器上运行起来还花了些功夫,主要是环境麻烦点,这里记下来以免下次忘记,有一些链接我感觉还是比较有用的。

    首先从eclipse.org/ercp下载eRCP程序包,它是一个只有4M多的.zip文件,打开一看里面包含了x86(即我们大部分人用的PC机)、wm2003(MS Windows Mobile 2003)和series80(Symbian OS series80)这三种环境的支持,其中前二者的每一个文件夹的结构都和标准RCP应用程序差不多,在eRCP目录下是startup.jar和一个链接文件,然后有plugins和configuration目录;后者这种操作系统我不熟悉,目录里包含的主要是两个.sis文件。

    在每个版本里都包含了一个很简单的例子,我先在电脑上试了一下x86版本,因为机器上已经装有jre,所以只要系统path变量里有java.exe所在路径,直接运行j2se.bat即可。(这里还有点问题,在公司的电脑上一切正常,但在我笔记本上提示“An unexpected exception has been detected in native code outside the VM.”,原因未知。)

    因为没有掌上电脑,所以要下载一个模拟器,其实搞开发用模拟器应该比较方便。想起前几天在Hi!PDA上看到微软新出了一个wm2003模拟器(查看原贴,下载地址在这里),这回正好用来当环境!还要下载一个ActiveSync软件用来同步和控制PocketPC,同步这个模拟器的方法在原贴里已经有人提到,这里就不重复了,后面一些步骤须要把电脑里的文件复制到PocketPC上就可以利用ActiveSync的Explorer功能。

    然后要下载一个PocketPC上的java环境,比较常用的是IBM的j9虚拟机,下载地址在这里(需要登录),下载页面会有很多个文件,不过只下载名称以“weme-wm2003-arm-ppro10”开头(Personal Profile)的那个.exe或.bin文件就够了。在电脑上运行这个程序,会得到一个jre以及一些.cab文件,把名为weme-ppro10-wm2003-arm_22.cab的文件(在我的电脑上该文件位于“C:Program FilesIBMWEME572WM2003armPPro10cab”目录)复制到PocketPC上运行,就会在PocketPC上安装java运行环境了。

    现在要把eRCP程序包里wm2003那部分复制到PocketPC的根目录,注意要复制的目录从eRCP开始,即复制后在PocketPC上形成eRCP目录,如下图所示。

    file
    图1 从电脑里把wm2003下的eRCP目录复制到下

    不过现在还不能直接点j9foun链接,因为我们在PocketPC上安装的是j9ppro,链接里的路径和参数都是无效的。解决方法是新建(或修改它为)一个链接把原来内容里的两处foun都改为ppro,修改后如下:

    37#"\Program Files\J9\PPRO10\bin\j9.exe" -jcl:ppro10 -cp \eRCP\startup.jar org.eclipse.core.launcher.Main -application org.eclipse.ercp.example.ercpHello.ercpHello -console

    假设新的链接名为j9ppro,这时就可以点它运行eRCP例子程序了。执行画面如下图所示。我感觉在模拟器上运行速度慢了许多,不知道在真机上效果会是怎样的,应该会有所提高,毕竟执行速度对PocketPC上的应用来说是至关重要的。

    file
    图2 eRCP例子在PocketPC上运行效果

    搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2005/05/28/163928.html