Three20 是一个非常有名的iPhone开发库,采用Objective-C语言。它是从Facebook iPhone app衍生而来的,所以很多Facebook app上使用的UI控件,像:TabBar,Launch view,Photo view等,都能从这个开发库中找到,且它使用的是Apache License(对商业友好),因此很多iPhone开发者都很喜欢使用它。
关于Three20 的介绍及使用,我打算以后再详细说明,今天要说的是最近一件关于App Store开始拒绝那些使用Three20 来开发的app,Apple的说法是:Three20 中引用了一些非public的API,违反了iPhone开发者许可。 此事详细说明可见Three20 邮件列表中的讨论:Heads up: Apple store rejection for apps using three20
那些非public的API如下:
firstResponder
UITouch._locationInWindow
UITouch._phase
UITouch._previousLocationInWindow
UITouch._tapCount
UITouch._timestamp
UITouch._touchFlags
UITouch._view
UITouch._window
我想Three20 使用这些API主要是为了省事,比如firstResponder,而UITouch的这些属性也主要是在Debug的时候才用到。因此拿掉以上这些private API是比较安全的,关键就是如何找到这些API的位置。
因为我的app(购便宜 )也用到了Three20 ,所以在发布新版前,为了不被Apple拒掉,必需解决private API的问题,下面我就描述一下我是如何去除这些API的过程:
解决此问题的方法有两个:
方法一(适用SDK3.0以上的app) :
有一个热心的开发者(uprise78 ),在爆出此问题后,根据邮件列表中的讨论以及自己的研究,在github上做了一个fork(three20-P31 ),移除了以上所有的private API,所以只需更新此fork即可。
方法二(适用SDK2.x,或自行修改过Three20 的app):
因为Three20 在今年7月份以后就不再兼容SDK2.x,但是我们的app还希望支持2.x的用户,所以我所用的版本就一直很低,没有向上升级。那为了解决此问题,就必须手动移除上面那些API。
在移除之前,我基本扫过了邮件列表中10月底到现在为止所有的文章,发现有两篇文章很重要:
1.发生问题后,作者本人做了一次修改,移除部分private API
2.作者本人并没有完全移除那些private API,uprise78 在作者的基础上又做了一次完整修改,
看起来经过这次修改后,基本解决了此问题。
也就是说只要参考作者及uprise78 的两次修改,就能完整移除所有的private API使用。于是我上github上寻找Three20 的修改历史,发现他们两的修改分别如下:
2009-10-28 作则修改:
c ommit 8183ae25528bbc575ab41a41227756f06c166240
t
ree 2b2fd7b49fc71755d675eb0fd85c82098da563a5
p
arent e8ecb801ca70ba824846928b7ca75faf376aeedb
2009-10-31 uprise78 修改:
c ommit 2c03d2b98ddb287ba68ddb628c4a72588bbee79e
t ree cc3cbfe0b23468d876166881f028a576f4ddcc7b
p arent 8183ae25528bbc575ab41a41227756f06c166240
c ommit 3a1f0a9bea92aed27f12a513e1e70c5412ba0f91
t
ree 0da7dba956f71ca57a029dc3a7c88448e76f9942
p
arent 2c03d2b98ddb287ba68ddb628c4a72588bbee79e
在github上比对以上修改后,就能完整移除private API了。
以上所谓移除private API,均是使用#ifdef DEBUG将之包裹,所以要注意以下两点:
1.程序中不能有#define DEBUG
2.在build时,不能在preprocessor marco设置DEBUG
目前方法一已经被证明是可行的,见邮件列表中的讨论:***P31 FORK FIXES REJECTIONS***
而我的app还处于送审review中,必须等审核通过后才能证明方法二有用!
希望上面的信息能帮助使用Three20的iPhone开发者!
from:http://eleda.iteye.com/blog/542302
j2me开发,最后离不开移植。既是移植,就离不开机型。
一些基本概念这里分享一下。
1.大屏
2.中屏
3.小屏
做一款游戏,需要知道屏幕参数,然后根据它的长宽确定一些线路,距离等,美术同样做封面等也需要这些做参考。所以有了以上三个概念。
通常
大屏是指240X320分辨率
中屏是指176X204或者176X208或者176X220分辨率
小屏是比以上更小的。
很多时候,美术可能要对这些不同屏做不同大小的图。现在很多手机游戏开发,都只考虑大屏,中屏的游戏了。小屏用来玩游戏的用户极少极少。手机也不断走向高端,必然走向大屏,甚至更大。做游戏就需要一个很好的用户定位了。
在手机游戏论坛下载游戏时,你会发现有很多什么系列的,如n73,7370,7610,k790,k700等。这是怎么回事呢?大家知道,一个手机商,会出很多手机,如nokia有n73,n78,n96,7370,5000,5300等等,索爱用k790,w580i,k700等,moto最著名的E398,L7等等。这些是不同型号的机器。而实际上游戏在最后做移植(适配时)不必去找每一款手机去测试,只要找同一系列的机测试通过了,同系列的就基本ok的了。像n78,n95和n73是同一系列,同属n73系列,移植时,只要测试n73通过了,n73系列的,就都过了。n73是n73系列手机中,性能最差的。而我们又不能通过文字就判断它是什么系列的,如n70,它可不是n73系列的哦,它是7610系列的,n97目前的机皇,同样不是n73系列的。
这些同系列中,有共同的特性,相同版本或者相近的操作系统,相同的分辨率,相同的键值,相似的性能,内存堆栈等。所以移植也降低了烦度。
什么机器是什么系列,你的手机是什么系列,网上也大把,有些也分得细些,有些大些,但专业的手机游戏网,系列分的是绝对准确的。像155啊,joyes啊,网友们可以去这些网去确认自己的系列属性。而往往商业网更关注什么手机功能方面的东西,你可能就找不到是什么系列的哦。
以下提供个表:
Nokia N7210系列 2650,3100,3108,3120,3200,3300,6030,6100,6108,6220,6610,6610i,6820,7200,7210,7250,7250i,6021
N7610系列 6681,6670,6600,6260,3230,7610,N70,3250,N91,N72
QD系列 N-Gage QD,7650
N7260系列 N7260,N6230,N6020,N3220,N2626,2610
N6101系列 N6101,N6102,N7270,N6170,N7360,N6060,N6111,N5200,N6070,N6085,3110c,5070,3100c,3500c,2630,2760,2660,6060V,6125
N6230i系列 N8800,N6230i,N8800Sirocco
N7370系列 N7370,N6270,N6131,N5300,N6300,N7373,7500,8860,5310,6288
N73系列 N73,N93,N93i,E50,N71,5700,6110N,6120,N95,N76
E62系列 E62
N5500系列 N5500
Motorola A760系列 A768i,A768,A760i,A728,A760
V600系列 V600,V501,V500,V303,V300
V878系列 V878,V872,V690
E680系列 E680i,E680,A780,E680G
C650系列 V220,V180,C650,C381,C168
E398系列 V3,E375,V80,V635,V600I,E398,E770,V191
A668系列 A668,A732
E2系列 E2
V8系列 Z6,V8,V8 2GB
L7系列 L7,U6,V360,V3i,V3re,V3r,W510
L6系列 L6,L2,L6i,L6g
E6系列 E6,A1200,A1200e,E6e
K1系列 K1,L7i,Z3,L72
sonyericsson K700C系列 K758c,K750c,K700c,K600i,K550C,W550c,W600c,W810c,W700c,W530,W800,W610C,W710C,Z558C,Z710C,Z800c,Z550,v600i,
K506系列 K508C,K506C,K500C,Z608C,Z520C,Z530C,W200c,K310C,W300C,K510C,Z550c,K310i,K320i,K510i,W200i,W300i,Z530i
S700系列 S700C,K790C,W830c,T658,W580C,S500c,K818c,W888c
T628系列 T628,T618,T630
P908系列 P800,P910c,P908
K300C系列 K300C,J300C
J210 J210
W958C系列 M608c,W958c,P1c,M600i,P990c,P990i,W950c,W950i,W960,P5i,P3i,W760
K600系列 K600,K608,K610C,K618
K750系列 K750i,W550i,W700i,W800i,W810i,D750i,K530i,W660i,Z558i,W600i
K790系列 K790c,K800i,K810i,T650i,T921i,W580i,K790i
K800系列 W900C,K790,K800,W830C,W850C,p990i,W850i,S710a
M600系列 K800c,M600c
Samsung S208系列 S208
E108系列 E208,E638,X168,X218,X638,E108,E358,E708,X668,X678,D488,E338,E808,E818,X208,X458,X478,X488,X818,P518,X308,X468,X628,X648,X658
D508系列 E728,E628,D508,D428,P738,F118,E538,D418,E578,E618,E738,J618,D528,D618,E388,E398,E648,E758,E778,E788,E788e,L608,E768,E498
X108系列 X108,X608,C208,C218,C238,E418,E608,X138,C158,C458
D608系列 U608,D808,D888,D820,E838,D828,D908i,E848,E908,D608,D838,D848,D908,E898,G608,U308,E958,G808E,J218,P858
P318系列 P318
E258系列 E118,E488,M608,C308,E378,E218,E358,M618,B508,E250,E258,E348,E368,E428,J608,X518 ;
NEC N800系列 N800
N810系列 N810
N820系列 N830,N820
NK系列 NK,NQ,Ag,N850
N100系列 N109,N108,N100
Panasonicx MX6系列 VS7,VS3,VS2,SA7,SA6,MX7,MX6
X800系列 PanasonicX700,PanasonicX800
A500系列 Panasonic A500
TCL E767系列 TCL E767
dopod 585系列 Dopod585,Dopod575,Dopod565,Dopod535
BenQSiemens EL71系列 EL71
Lenovo V800系列 V800
LG KG90系列 LG-KE608n,LG-KG90n
KG77系列 LG-KG77
KU250系列 KU250,KU380,KE590,KU311,KE770
KG800系列 KG800,KG320
KU970系列 KU970,KE970,KG300
Menu
[功能]
Menu 对手机界面友好来说 是不可缺少的
而且 不仅可以通过java来创建/使用 menu 还可以通过xml 来创建/使用
[原理]
1. android 自身提供了对 menu 的良好支持 但是需要注意一点:menu 是绑定在 Activity 的。
2. 我们要做的就是:填充下列函数的实现部分:
public boolean onCreateOptionsMenu(Menu menu)
public boolean onOptionsItemSelected(MenuItem item)
public boolean onPrepareOptionsMenu(Menu menu)
以上3个函数的用途 从函数名应该就能知道了 就不多说了 比较特殊的是 public boolean onPrepareOptionsMenu(Menu menu) 这个函数会在你每次点击MENU键时被调用 所以我们可以在这里根据一些状态的变化来实时更新 menu 的内容 比如标题或作用
比如:有一个播放的功能 要求有一个menu 用来 播放/暂停 功能 且要二者公用一个 menu 且该menu 的标题要实时改变 比如 如果此刻正在播放 要求其标题为"暂停" 反之为"播放" 那么应该如何实现呢? (设:该menu的id为: int play = Menu.FIRST +3 ).代码如下:
private static final int play = Menu.FIRST +3; MediaPlay mp = new MediaPlay(); MenuItem item = menu.findItem(play ); if(mp.isPlaying()){ item.setTitle("stop"); } else { item.setTitle("play"); }
[代码]
1. menu @ java
import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; public class MenuAdd1Usage extends Activity { private static final int Menu1 = Menu.FIRST; private static final int Menu2 = Menu.FIRST+1; private static final int Menu3 = Menu.FIRST+2; private static final int Menu4 = Menu.FIRST+3; private static final int Menu5 = Menu.FIRST+4; private static final int Menu6 = Menu.FIRST+5; private static final int Menu7 = Menu.FIRST+7; public int FLAG_STATUS = 0; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, Menu1, 0, "Menu-1").setIcon(R.drawable.se32); menu.add(0, Menu2, 0, "Menu-2").setIcon(R.drawable.heart); menu.add(0, Menu3, 0, "Menu-3"); menu.add(0, Menu4, 0, "Menu-4"); menu.add(0, Menu5, 0, "Menu-5"); menu.add(0, Menu6, 0, "Menu-6"); menu.add(0, Menu7, 0, "Menu-7"); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case Menu1: //to fill action listened by MenuItem break; } return false; } public boolean onPrepareOptionsMenu(Menu menu) { Log.d("TAG","-------------------------"); MenuItem item = menu.findItem(Menu1); item.setEnabled(false); //to query menu //MenuItem item = menu.findItem(MENU_QUIT); //item.setChecked(false); //item.setTitle("update"); /* to update menu if(FLAG_STATUS == 0){ item.setTitle("QUIT-1"); FLAG_STATUS = 1; } else { item.setTitle("QUIT-0"); FLAG_STATUS = 0; } */ /* to add menu if(FLAG_STATUS == 0){ menu.add(0, MENU_NEW_NEW, 0, "New NEW"); } */ return true; } }
2. menu @ xml
public class MenuAdd2Usage extends Activity { public int FLAG_STATUS = 0; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.layout.menu_item, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.Item_1: Log.d("TAG","[Item_1]"); break; case R.id.Item_2: Log.d("TAG","[Item_2]"); break; case R.id.Item_3: Log.d("TAG","[Item_3]"); break; } return false; } public boolean onPrepareOptionsMenu(Menu menu) { MenuItem item = menu.findItem(R.id.Item_2); item.setEnabled(false); /* if(FLAG_STATUS == 0){ item.setTitle("QUIT-1"); FLAG_STATUS = 1; } else { item.setTitle("QUIT-0"); FLAG_STATUS = 0; } */ return true; } }
menu_item.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/Item_1" android:title="Item_1" /> <item android:id="@+id/Item_2" android:title="Item_2" /> <item android:id="@+id/Item_3" android:title="Item_3" android:enabled="false" /> </menu>
over!!!
似乎不可以 不过你可以使用PopupWindow + GridView 来达到该效果 更多细节 见:Menu 大变脸