??xml version="1.0" encoding="utf-8" standalone="yes"?> add_months(d,n) 日期d加n个月 Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q- Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?br>SQL> select to_char(sysdate,'yyyy-mm-dd hh:mi:ss am') from dual; 在oracle中有很多关于日期的函敎ͼ? 1、add_months()用于从一个日期值增加或减少一些月?/p>
date_value:=add_months(date_value,number_of_months) ? SQL> select add_months(sysdate,12) "Next Year" from dual; Next Year ---------- 13-11?04 SQL> select add_months(sysdate,112) "Last Year" from dual; Last Year ---------- 13-3?-13 SQL> 2、current_date()q回当前会放时区中的当前日期 date_value:=current_date SQL> column sessiontimezone for a15 SQL> select sessiontimezone,current_date from dual; SESSIONTIMEZONE CURRENT_DA --------------- ---------- +08:00 13-11?03 SQL> alter session set time_zone='-11:00' 2 / 会话已更攏V?/p>
SQL> select sessiontimezone,current_timestamp from dual; SESSIONTIMEZONE CURRENT_TIMESTAMP --------------- ------------------------------------ -11:00 12-11?03 04.59.13.668000 下午 -11: 00 SQL> 3、current_timestamp()以timestamp with time zone数据cdq回当前会放时区中的当前日期 timestamp_with_time_zone_value:=current_timestamp([timestamp_precision]) SQL> column sessiontimezone for a15 SQL> column current_timestamp format a36 SQL> select sessiontimezone,current_timestamp from dual; SESSIONTIMEZONE CURRENT_TIMESTAMP --------------- ------------------------------------ +08:00 13-11?03 11.56.28.160000 上午 +08: 00 SQL> alter session set time_zone='-11:00' 2 / 会话已更攏V?/p>
SQL> select sessiontimezone,current_timestamp from dual; SESSIONTIMEZONE CURRENT_TIMESTAMP --------------- ------------------------------------ -11:00 12-11?03 04.58.00.243000 下午 -11: 00 SQL> 4、dbtimezone()q回时区 varchar_value:=dbtimezone SQL> select dbtimezone from dual; DBTIME ------ -07:00 SQL> 5、extract()扑և日期或间隔值的字段?/p>
date_value:=extract(date_field from [datetime_value|interval_value]) SQL> select extract(month from sysdate) "This Month" from dual; This Month ---------- 11 SQL> select extract(year from add_months(sysdate,36)) "3 Years Out" from dual; 3 Years Out ----------- 2006 SQL> 6、last_day()q回包含了日期参数的月䆾的最后一天的日期 date_value:=last_day(date_value) SQL> select last_day(date'2000-02-01') "Leap Yr?" from dual; Leap Yr? ---------- 29-2?-00 SQL> select last_day(sysdate) "Last day of this month" from dual; Last day o ---------- 30-11?03 SQL> 7、localtimestamp()q回会话中的日期和时?/p>
timestamp_value:=localtimestamp SQL> column localtimestamp format a28 SQL> select localtimestamp from dual; LOCALTIMESTAMP ---------------------------- 13-11?03 12.09.15.433000 下午 SQL> select localtimestamp,current_timestamp from dual; LOCALTIMESTAMP CURRENT_TIMESTAMP ---------------------------- ------------------------------------ 13-11?03 12.09.31.006000 13-11?03 12.09.31.006000 下午 +08: 下午 00 SQL> alter session set time_zone='-11:00'; 会话已更攏V?/p>
SQL> select localtimestamp,to_char(sysdate,'DD-MM-YYYY HH:MI:SS AM') "SYSDATE" from dual; LOCALTIMESTAMP SYSDATE ---------------------------- ------------------------ 12-11?03 05.11.31.259000 13-11-2003 12:11:31 下午 下午 SQL> 8、months_between()判断两个日期之间的月份数?/p>
number_value:=months_between(date_value,date_value) SQL> select months_between(sysdate,date'1971-05-18') from dual; MONTHS_BETWEEN(SYSDATE,DATE'1971-05-18') ---------------------------------------- 389.855143 SQL> select months_between(sysdate,date'2001-01-01') from dual; MONTHS_BETWEEN(SYSDATE,DATE'2001-01-01') ---------------------------------------- 34.4035409 SQL> 9、next_day()l定一个日期|q回q二个参数指出的日子第一ơ出现在的日期?应返回相应日子的名称字符? 说明: 单行日期函数 单行日期函数操作data数据cdQ绝大多数都有data数据cd的参敎ͼl大多数q回的也是data数据cd的倹{?/p>
add_months(,) q回日期d加上i个月后的l果。i可以使Q意整数。如果i是一个小敎ͼ那么数据库将隐式的他转换成整敎ͼ会截去数点后面的部分?/p>
last_day() 函数q回包含日期d的月份的最后一?/p>
months_between(,) q回d1和d2之间月的数目,如果d1和d2的日的日期都相同Q或者都使该月的最后一天,那么返回一个整敎ͼ否则会返回的l果包含一个分数?/p>
new_time(,,) d1是一个日期数据类型,当时区tz1中的日期和时间是dӞq回时区tz2中的日期和时间。tz1和tz2时字W串?/p>
next_day(,) q回日期d后由dowl出的条件的W一天,dow使用当前会话中给出的语言指定了一周中的某一天,q回的时间分量与d的时间分量相同?/p>
select next_day(''01-jan-2000'',''monday'') "1st monday",next_day(''01-nov-2004'',''tuesday'')+7 "2nd tuesday") from dual;1st monday 2nd tuesday03-jan-2000 09-nov-2004 round([,]) 日期d按照fmt指定的格式舍入,fmt为字W串?/p>
syadate 函数没有参数Q返回当前日期和旉?/p>
trunc([,]) q回由fmt指定的单位的日期d. 单行转换函数 单行转换函数用于操作多数据类型,在数据类型之间进行{换?/p>
chartorwid() c 使一个字W串Q函数将c转换为rwid数据cd?/p>
select test_id from test_case where rowid=chartorwid(''aaaa0saacaaaaliaaa'') convert(,[,]) c֭W串Qdset、sset是两?a class=bluekey target=_blank>字符?/font>Q函数将字符串c由sset字符集{换ؓdset字符集,sset的缺省设|ؓ数据库的字符集?/p>
hextoraw() x?6q制的字W串Q函数将16q制的x转换为raw数据cd?/p>
rawtohex() x是raw数据cd字符Ԍ函数raw数据c{换ؓ16q制的数据类型?/p>
rowidtochar() 函数rowid数据cd转换为char数据cd?/p>
to_char([[,) x是一个data或number数据cdQ函数将x转换成fmt指定格式的char数据cdQ如果x为日期nlsparm=nls_date_language 控制q回的月份和日䆾所使用的语a。如果x为数字nlsparm=nls_numeric_characters 用来指定数位和千分位的分隔W,以及货币W号?/p>
nls_numeric_characters ="dg", nls_currency="string" to_date([,[,) c表示字符Ԍfmt表示一U特D格式的字符丌Ӏ返回按照fmt格式昄的c,nlsparm表示使用的语a。函数将字符串c转换成date数据cd?/p>
to_multi_byte() c表示一个字W串Q函数将c的担子截字符转换成多字节字符?/p>
to_number([,[,) c表示字符Ԍfmt表示一个特D格式的字符Ԍ函数q回值按照fmt指定的格式显C。nlsparm表示语言Q函数将q回c代表的数字?/p>
to_single_byte() 字W串c中得多字节字W{化成{h的单字节字符。该函数仅当数据库字W集同时包含单字节和多字节字W时才用。Oracle关于旉/日期的操?/p>
1.日期旉间隔操作 当前旉减去7分钟的时?/p>
select sysdate,sysdate - interval '7' MINUTE from dual 当前旉减去7时的时?/p>
select sysdate - interval '7' hour from dual 当前旉减去7天的旉 select sysdate - interval '7' day from dual 当前旉减去7月的旉 select sysdate,sysdate - interval '7' month from dual 当前旉减去7q的旉 select sysdate,sysdate - interval '7' year from dual 旉间隔乘以一个数?/p>
select sysdate,sysdate - 8 *interval '2' hour from dual 2.日期到字W操?/p>
select sysdate,to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual select sysdate,to_char(sysdate,'yyyy-mm-dd hh:mi:ss') from dual select sysdate,to_char(sysdate,'yyyy-ddd hh:mi:ss') from dual select sysdate,to_char(sysdate,'yyyy-mm iw-d hh:mi:ss') from dual 参考oracle的相兛_文档(ORACLE901DOC/SERVER.901/A90125/SQL_ELEMENTS4.HTM#48515) 3. 字符到日期操?/p>
select to_date('2003-10-17 21:15:37','yyyy-mm-dd hh24:mi:ss') from dual 具体用法和上面的to_char差不多?/p>
4. trunk/ ROUND函数的?/p>
select trunc(sysdate ,'YEAR') from dual select trunc(sysdate ) from dual select to_char(trunc(sysdate ,'YYYY'),'YYYY') from dual 5.oracle有毫U的数据类?/p>
--q回当前旉 q月日小时分U毫U?/p>
select to_char(current_timestamp(5),'DD-MON-YYYY HH24:MI:SSxFF') from dual; --q回当前 旉的秒毫秒Q可以指定秒后面的精?最?9) select to_char(current_timestamp(9),'MI:SSxFF') from dual; 6.计算E序q行的时?ms) declare type rc is ref cursor; l_rc rc; l_dummy all_objects.object_name%type; l_start number default dbms_utility.get_time; begin for I in 1 .. 1000 loop open l_rc for 'select object_name from all_objects '|| 'where object_id = ' || i; fetch l_rc into l_dummy; close l_rc; end loop; dbms_output.put_line ( round( (dbms_utility.get_time-l_start)/100, 2 ) || ' seconds...' ); end;
V_RETURN DATE;
V_ISSUE VARCHAR2(20) :=ISSUE;
V_DATE_TYPE VARCHAR2(20) := DATE_TYPE;
V_ISSUE_TYPE VARCHAR2(20) :=SUBSTR(ISSUE,1,1);
begin
IF V_DATE_TYPE = '0' THEN
CASE
WHEN V_ISSUE_TYPE = '1' Q-q?br> THEN V_RETURN := TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD');
WHEN V_ISSUE_TYPE = '2' Q-?br> THEN SELECT DECODE( SUBSTR(V_ISSUE,6,2),
'01',TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD'),
'02',TO_DATE(SUBSTR(V_ISSUE,2,4)||'0401','YYYY-MM-DD'),
'03',TO_DATE(SUBSTR(V_ISSUE,2,4)||'0701','YYYY-MM-DD'),
'04',TO_DATE(SUBSTR(V_ISSUE,2,4)||'1001','YYYY-MM-DD'),TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD'))
INTO V_RETURN
FROM DUAL;
WHEN V_ISSUE_TYPE = '3' Q-?br> THEN V_RETURN := TO_DATE(SUBSTR(V_ISSUE,2,6)||'01','YYYY-MM-DD');
WHEN V_ISSUE_TYPE = '4' Q-?br> THEN SELECT AA INTO V_RETURN FROM (
SELECT aa ,ROWNUM BB
FROM (
SELECT (TO_DATE (SUBSTR(V_ISSUE,2,6)||'01', 'yyyy-mm-dd') + ROWNUM - 1) aa ,rownum cc
FROM all_objects
WHERE ROWNUM < TO_DATE(TO_CHAR(last_day(TO_DATE (SUBSTR(V_ISSUE,2,6)||'01', 'yyyy-mm-dd')),'YYYYMMDD'),'YYYY-MM-DD') - TO_DATE (SUBSTR(V_ISSUE,2,6)||'01', 'yyyy-mm-dd') + 1) bb
WHERE MOD (TO_CHAR (aa, 'dd'), 10) = 1 and to_char(aa,'dd') <> 31 )
WHERE BB = TO_NUMBER(SUBSTR(V_ISSUE,8,2));
WHEN V_ISSUE_TYPE = '5' Q-?br> THEN SELECT MONDAY INTO V_RETURN FROM (
select
MONDAY.the_week,decode(sign(MONDAY.the_day-SUNDAY.the_day),-1,MONDAY.the_day,MONDAY.the_day-7) MONDAY,SUNDAY.the_day SUNDAY
from
(select to_char(wwm,'WW') the_week,to_char(wwm,'D') the_daynum,wwm the_day
from (
select trunc(TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD'), 'MM')+rownum-1 as wwm
from user_objects
where rownum < 366
)
where to_char(wwm,'D')=2 ) MONDAY,
(select to_char(wwm,'WW') the_week,to_char(wwm,'D') the_daynum,wwm the_day
from (
select trunc(TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD'), 'MM')+rownum-1 as wwm
from user_objects
where rownum < 366
)
where to_char(wwm,'D')=1 ) SUNDAY
where MONDAY.the_week=SUNDAY.the_week)
WHERE THE_WEEK = SUBSTR(V_ISSUE,6,2);
WHEN V_ISSUE_TYPE = '6' Q-?br> THEN V_RETURN := TO_DATE(SUBSTR(V_ISSUE,2,8),'YYYY-MM-DD');
ELSE
V_RETURN := TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD');
END CASE;
ELSE
CASE
WHEN V_ISSUE_TYPE = '1'
THEN V_RETURN := TO_DATE(SUBSTR(V_ISSUE,2,4)||'1231','YYYY-MM-DD');
WHEN V_ISSUE_TYPE = '2'
THEN SELECT DECODE( SUBSTR(V_ISSUE,6,2),
'01',TO_DATE(SUBSTR(V_ISSUE,2,4)||'0331','YYYY-MM-DD'),
'02',TO_DATE(SUBSTR(V_ISSUE,2,4)||'0630','YYYY-MM-DD'),
'03',TO_DATE(SUBSTR(V_ISSUE,2,4)||'0930','YYYY-MM-DD'),
'04',TO_DATE(SUBSTR(V_ISSUE,2,4)||'1231','YYYY-MM-DD'),
TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD'))
INTO V_RETURN
FROM DUAL;
WHEN V_ISSUE_TYPE = '3'
THEN V_RETURN := TO_DATE(TO_CHAR(last_day(TO_DATE(SUBSTR(V_ISSUE,2,6)||'01','YYYY-MM-DD')),'YYYYMMDD'),'YYYY-MM-DD');
WHEN V_ISSUE_TYPE = '4'
THEN SELECT DECODE( SUBSTR(V_ISSUE,8,2),
'01',TO_DATE(SUBSTR(V_ISSUE,2,6)||'10','YYYY-MM-DD'),
'02',TO_DATE(SUBSTR(V_ISSUE,2,6)||'20','YYYY-MM-DD'),
'03',TO_DATE(TO_CHAR(last_day(TO_DATE(SUBSTR(V_ISSUE,2,6)||'01','YYYY-MM-DD')),'YYYYMMDD'),'YYYY-MM-DD'),
TO_DATE(TO_CHAR(last_day(TO_DATE(SUBSTR(V_ISSUE,2,6)||'01','YYYY-MM-DD')),'YYYYMMDD'),'YYYY-MM-DD'))
INTO V_RETURN
FROM DUAL;
WHEN V_ISSUE_TYPE = '5'
THEN SELECT SUNDAY INTO V_RETURN FROM (
select
MONDAY.the_week,decode(sign(MONDAY.the_day-SUNDAY.the_day),-1,MONDAY.the_day,MONDAY.the_day-7) MONDAY,SUNDAY.the_day SUNDAY
from
(select to_char(wwm,'WW') the_week,to_char(wwm,'D') the_daynum,wwm the_day
from (
select trunc(TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD'), 'MM')+rownum-1 as wwm
from user_objects
where rownum < 366
)
where to_char(wwm,'D')=2 ) MONDAY,
(select to_char(wwm,'WW') the_week,to_char(wwm,'D') the_daynum,wwm the_day
from (
select trunc(TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD'), 'MM')+rownum-1 as wwm
from user_objects
where rownum < 366
)
where to_char(wwm,'D')=1 ) SUNDAY
where MONDAY.the_week=SUNDAY.the_week)
WHERE THE_WEEK = SUBSTR(V_ISSUE,6,2);
WHEN V_ISSUE_TYPE = '6'
THEN V_RETURN := TO_DATE(SUBSTR(V_ISSUE,2,8),'YYYY-MM-DD');
ELSE
V_RETURN := TO_DATE(SUBSTR(V_ISSUE,2,4)||'0101','YYYY-MM-DD');
END CASE;
END IF;
return(V_RETURN);
end TF_ISSUE_DATE;
dd number 12
dy abbreviated fri
day spelled out friday
ddspth spelled out, ordinal twelfth
Month:
mm number 03
mon abbreviated mar
month spelled out march
Year:
yy two digits 98
yyyy four digits 1998
24时格式下时间范围ؓQ?0:00:00 - 23:59:59....
12时格式下时间范围ؓQ?1:00:00 - 12:59:59 ....
1.
日期和字W{换函数用法(to_date,to_charQ?
2.
select to_char( to_date(222,'J'),'Jsp') from dual
昄Two Hundred Twenty-Two
3.
求某天是星期?
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day') from dual;
星期一
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
monday
讄日期语言
ALTER SESSION SET NLS_DATE_LANGUAGE='AMERICAN';
也可以这?
TO_DATE ('2002-08-26', 'YYYY-mm-dd', 'NLS_DATE_LANGUAGE = American')
4.
两个日期间的天数
select floor(sysdate - to_date('20020405','yyyymmdd')) from dual;
5. 旉为null的用?
select id, active_date from table1
UNION
select 1, TO_DATE(null) from dual;
注意要用TO_DATE(null)
6.
a_date between to_date('20011201','yyyymmdd') and to_date('20011231','yyyymmdd')
那么12?1号中?2点之后和12?L12点之前是不包含在q个范围之内的?
所以,当时间需要精的时候,觉得to_charq是必要?
7. 日期格式冲突问题
输入的格式要看你安装的ORACLE字符集的cd, 比如: US7ASCII, date格式的类型就? '01-Jan-01'
alter system set NLS_DATE_LANGUAGE = American
alter session set NLS_DATE_LANGUAGE = American
或者在to_date中写
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
注意我这只是举了NLS_DATE_LANGUAGEQ当然还有很多,
可查?
select * from nls_session_parameters
select * from V$NLS_PARAMETERS
8.
select count(*)
from ( select rownum-1 rnum
from all_objects
where rownum <= to_date('2002-02-28','yyyy-mm-dd') - to_date('2002-
02-01','yyyy-mm-dd')+1
)
where to_char( to_date('2002-02-01','yyyy-mm-dd')+rnum-1, 'D' )
not
in ( '1', '7' )
查找2002-02-28?002-02-01间除星期一和七的天?
在前后分别调用DBMS_UTILITY.GET_TIME, 让后结果相?得到的是1/100U? 而不是毫U?.
9.
select months_between(to_date('01-31-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1
select months_between(to_date('02-01-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1.03225806451613
10. Next_day的用?
Next_day(date, day)
Monday-Sunday, for format code DAY
Mon-Sun, for format code DY
1-7, for format code D
11
select to_char(sysdate,'hh:mi:ss') TIME from all_objects
注意Q第一条记录的TIME 与最后一行是一L
可以建立一个函数来处理q个问题
create or replace function sys_date return date is
begin
return sysdate;
end;
select to_char(sys_date,'hh:mi:ss') from all_objects;
12.
获得时?
SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 2:38:40') from offer
SQL> select sysdate ,to_char(sysdate,'hh') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH')
-------------------- ---------------------
2003-10-13 19:35:21 07
SQL> select sysdate ,to_char(sysdate,'hh24') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH24')
-------------------- -----------------------
2003-10-13 19:35:21 19
获取q月日与此类?
13.
q月日的处理
select older_date,
newer_date,
years,
months,
abs(
trunc(
newer_date-
add_months( older_date,years*12+months )
)
) days
from ( select
trunc(months_between( newer_date, older_date )/12) YEARS,
mod(trunc(months_between( newer_date, older_date )),
12 ) MONTHS,
newer_date,
older_date
from ( select hiredate older_date,
add_months(hiredate,rownum)+rownum newer_date
from emp )
)
14.
处理月䆾天数不定的办?
select to_char(add_months(last_day(sysdate) +1, -2), 'yyyymmdd'),last_day(sysdate) from dual
16.
扑և今年的天?
select add_months(trunc(sysdate,'year'), 12) - trunc(sysdate,'year') from dual
闰年的处理方?
to_char( last_day( to_date('02' || :year,'mmyyyy') ), 'dd' )
如果?8׃是闰q?
17.
yyyy与rrrr的区?
'YYYY99 TO_C
------- ----
yyyy 99 0099
rrrr 99 1999
yyyy 01 0001
rrrr 01 2001
18.不同时区的处?
select to_char( NEW_TIME( sysdate, 'GMT','EST'), 'dd/mm/yyyy hh:mi:ss') ,sysdate
from dual;
19.
5U钟一个间?
Select TO_DATE(FLOOR(TO_CHAR(sysdate,'SSSSS')/300) * 300,'SSSSS') ,TO_CHAR(sysdate,'SSSSS')
from dual
2002-11-1 9:55:00 35786
SSSSS表示5位秒?
20.
一q的W几?
select TO_CHAR(SYSDATE,'DDD'),sysdate from dual
310 2002-11-6 10:03:51
21.计算时,?U?毫秒
select
Days,
A,
TRUNC(A*24) Hours,
TRUNC(A*24*60 - 60*TRUNC(A*24)) Minutes,
TRUNC(A*24*60*60 - 60*TRUNC(A*24*60)) Seconds,
TRUNC(A*24*60*60*100 - 100*TRUNC(A*24*60*60)) mSeconds
from
(
select
trunc(sysdate) Days,
sysdate - trunc(sysdate) A
from dual
)
select * from tabname
order by decode(mode,'FIFO',1,-1)*to_char(rq,'yyyymmddhh24miss');
//
floor((date2-date1) /365) 作ؓq?
floor((date2-date1, 365) /30) 作ؓ?
mod(mod(date2-date1, 365), 30)作ؓ?
23.next_day函数
next_day(sysdate,6)是从当前开始下一个星期五。后面的数字是从星期日开始算赗?
1 2 3 4 5 6 7
?一 ?????br>
last_day(d) 包含d的月份的最后一天的日期
month_between(d,e) 日期d与e之间的月份数Qe先于d
new_time(d,a,b) a时区的日期和旉d在b时区的日期和旉
next_day(d,day) 比日期d晚,由day指定的周几的日期
sysdate 当前的系l日期和旉
greatest(d1,d2,...dn) l出的日期列表中最后的日期
least(d1,k2,...dn) l出的日期列表中最早的日期
to_char(d [,fmt]) 日期d按fmt指定的格式{变成字符?br>to_date(st [,fmt]) 字符串st按fmt指定的格式{成日期|若fmt忽略Qst要用~省格式
round(d [,fmt]) 日期d按fmt指定格式舍入到最q的日期
trunc(d [,fmt]) 日期d按fmt指定格式截断到最q的日期
格式代码 说明 举例或可取值的范围
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-
DD 该月某一?nbsp; 1Q?
DY 三个大写字母表示的周?nbsp;SUNQ?..SAT
DAY 完整的周几,大写英文 SUNDAYQ?..SATURDAY
MM 月䆾 1Q?2
MON 三个大写字母表示的月?nbsp;JANQ?..DEC
MONTH 完整 JANUARY,...DECEMBER
RM 月䆾的罗马数?nbsp; I,...XII
YY或YYYY 两位Q四位数字年
HH:MI:SS Ӟ分:U?br>HH12或HH24 ?2时?4时昄
MI ?br>SS U?br>AM或PM 上下午指C符
SP 后缀SP要求拼写ZQ何数值字D?br>TH 后缀TH表示d的数字是序数 4th,1st
FM 前缀Ҏ或日或年|止填充
昨天在网上闲逛,发现一讲解用delphi实现华容道游戏的文章Q颇受启发.于是Q生了华定w游戏UL到手Zȝ冲动Q现在手机游戏琳琅满目,不一而Q华定w的实现版本也很多Q正巧不久前W者对J2ME下了一番功夫,正想借这个机会小试牛刀。选用J2ME的原因还有一个就是目前Java开发大行其刎ͼ无线增殖业务q猛发展QJ2ME的应用日渐活跃v来,也希望我的这文章能够ؓJ2ME知识的普及和开发团队的壮大推L助澜。由于长期受ISO规范的媄响,q次试牛刀我也打算늅软g工程的要求,q取瀑布式的开发模式来规划目Q也希望借此Z向各位没有机会参与正式项目开发的读者介l一下Y件开发的程?/p>
q里我们先定义项目组的h员体?其实只有我一个h)Q技术调研、需求分析、概要设计、详l设计、编码、测试均有笔者一人担任;工q里我找了个捷径Q盗用网上现成的囄Q然后用ACDSee把它由BMP转换成PNG格式(我出于讲座的目的Q未做商业应用,应该不算侉|?Q至于发布工作,׃~少OTA服务器,此项工作不做(但是我会介绍q步如何??/p>
接下来,我们规划一下项目实现的旉表,以我个hl验Q设惛_下:技术调研用2?q部分解决项目的可行性和重大技术问题,旉会长一?Q需求分析用半天(毕竟有现成的东东可以参照Q只要理清思\p了,况且q有很多以前用过的设计模式和写好的代?Q概要设计再用半?有了需求,概要只不够是照方抓药)Q详l设计要??q一步要把所有的问题x楚,q要可能的准确描述出来)Q编码用2?其实1天就够了Q技术已l不是问题,多计划出一天来应付H发事g)Q测试用2?试应该臛_占全部项目的四分之一Q不q这个项目只是一个DemoQ也太简单了)Q发布也要用上半?管我们不去实际发布它,但是q要q旉搞清楚应该如何做)Q最后就是项目ȝ和开庆功?旉待定)?/p>
二、利其器
“公Ʋ善其事Q必先利其器”,做项目之前第一步是前期调研Q我们要做的华容道这个东东随处可见,我们要调研的是两个方面:
1. 游戏的内容:游戏本n很简单,是有几个格子,Ҏ占据其中一个较大的格子Q然后被几个格子包围Q这些格子Ş状不一定相同,但是挡住了曹操移动的方向Q游戏者需要挪动这些格子最l把ҎUdC个指定的位置才算是过养I更具体的分析我们攑֜后面需求分析和概要设计中讨论?/p>
2. 技术储备:谈到技术,q里单介l一下J2ME.Java有三个版本,分别是J2MEQ微型版Q?J2SEQ标准版Q?J2EEQ企业版Q.J2ME是一个标准,采用Q层l构设计Q最低层是配|层QConfigurationQ也是讑֤层,其上是简表层QProfileQ?再上是应用层QApplicationQ?MIDP是Ud信息讑֤表,目前L手机支持MIDP1.0Q最新的是MIDP2.0,它比前一个版本增加了Ҏ戏的支持Q在javax.microedition.lcdui.game包中提供了一些类来处理游戏中的技术,比如我们后面会用到的Spritec,它是用来{囄?权衡再三Q笔者决定用MIDP2.0来做开发.首先需要安装一个J2ME的模拟器Q我们就用Sun公司的WTK2.0Q我觉得Sun的东西最权威Q当然你也可以用Nokia.Siemens或是Motolora{其他模拟器Q但是他们的JDK不尽相同Q写出来的程序移植是比较ȝ的.
Sun公司的WTK2.0可以到搜索引擎寻找下载,当然要想成功下蝲的前提是你要先注册成为Sun的会员(其实q样对你是有好处的)Q当下来之后是按照提示一步一步的安装Q安装好了之后,我们用一?Hello World"E序开始你的J2ME之旅Q我们启动WTK2.0工具集中的KToolBarQ然后点击New Project按钮Q在弹出的输入框中输入Project Name为HelloWorld,MIDlet Class Name为Hello,然后点击Create ProjectQ开始生成项目,工具会弹出MIDP配置表,q里接受生成的默认|以后q可以修改)点击OKQ工hC我们把写好的Java源程序放到[WTK_HOME]\apps\HelloWorld\src目录之下Q我们编辑如下代码,q保存在上述目录之下Q文件名为Hello.java?/p>
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Hello extends MIDlet
{
private Display display;
public Hello(){
display =Display.getDisplay(this);
}
public void startApp(){
TextBox t = new TextBox("Hello","Hello",256,0);
display.setCurrent(t);
}
public void pauseApp(){
}
public void destroyApp(boolean unconditional){
}
}
保存好了之后Q点击Build按钮Q工具会Z~译E序Q如无意外再点击Run按钮Q会弹出一个手机界面,剩下的就不用我教了吧Q用鼠标Ҏ机按键一狂点)。呵呵,你的W一个J2MEE序已经OK?什么?你还一炚w没懂呢(真是厉害Q不懂都能写出J2MEE序来,果然是高手)Q我q里主要是介lWTK2.0工具的用,E序q不是目的,不懂的话后面q会有详l的解说Q这里只是带你上路.什么?你不懂JavaQ那也没有关p,后面我再讲得l一炏V?
跌J2MEQ我们先来讲Ҏ戏的理论Q具体到华容道这个游戏,主要有三个方面,贴图Q游戏操作.逻辑判断Q这里讲讲脓图,其他两方面放在概要设计和详细设计里讲Q所谓的贴图Q其实就是画图,是在要昄囑Ş的位|上输出一副图片,Q要是牵扯到动画pȝ一些,可以使用TimerTask.Thread或Rannable之类的技?Q这副图片可以是事先准备好的也可以是临时处理的.在J2ME中有一个Imagec?专门用于理囄Q它有createImage()ҎQ可以直接读取图片文ӞJ2ME只支持PNG格式的图片)Q也可以截取已有的图片的一部分Q这h们可以把很多囄攑֜一P然后一张一张的截下来,好处是节省存储空间和文gd旉Q对于手两者都是性能的瓶颈)Q?
J2MEq有一个Graphicsc,专门用于l图Q它有drawImage()ҎQ可以把一副图片在指定的位|上昄出来Q它q有drawRect()Ҏ和setColor()ҎQ这两个Ҏ在后面我们进行游戏操作时׃用到Q这里先交代一下.有了囄和绘囄ҎQ还需要知道把囄到谁w上QJ2ME提供了一个Canvasc,字面意思就是画布,它有一个paint()Ҏ用于h面Q还有一个repaint()Ҏ用于调用paint()ҎQ听着有些p涂是吧Q不要紧Q我来结合具体程序讲解一下.Z今后~程的方便,我们创徏两个cImages和Draw,Images用于保存一些常量值和囄QDraw主要是用于画图,q两个类的源代码如下?
Imagescȝ源代码如下:
package huarongroad;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
public class Images {//保存帔R
//l图位置帔R
public static final int UNIT = 32;//方块的单位长?
public static final int LEFT = 10;//d的左边界点
public static final int TOP = 9;//d的上边界点
//地图位置帔R
public static final int WIDTH = 4;//地图的宽?
public static final int HEIGHT = 5;//地图的高?
//地图标记帔R
public static final byte CAOCAO = (byte) ′a? QA href="file://?Qfile://曹</AQ操的地图标?
public static final byte MACHAO = (byte) ′b?//马超的地图标?
public static final byte HUANGZHONG = (byte) ′c?//黄忠的地图标?
public static final byte GUANYU = (byte) ′d?//关羽的地图标?
public static final byte ZHANGFEI = (byte) ′e?//张飞的地图标?
public static final byte ZHAOYUN = (byte) ′f?//赵云的地图标?
public static final byte ZU = (byte) ′g?//卒的地图标记
public static final byte BLANK = (byte) ′h?//I白的地图标?
public static final byte CURSOR = (byte) ′i?//光标的地图标?
//地图l合标记帔R
public static final byte DLEFT = (byte) ?? QA href="file://l?Qfile://l</AQ合囑Ş左边标记
public static final byte DUP = (byte) ?? QA href="file://l?Qfile://l</AQ合囑Ş上边标记
public static final byte DLEFTUP = (byte) ?? QA href="file://l?Qfile://l</AQ合囑Ş左上标记
//囄帔R
public static Image image_base;//基本囄
public static Image image_Zhaoyun;//赵云的图?
public static Image image_Caocao;//Ҏ的图?
public static Image image_Huangzhong;//黄忠的图?
public static Image image_Machao;//马超的图?
public static Image image_Guanyu;//关羽的图?
public static Image image_Zhangfei;//张飞的图?
public static Image image_Zu;//卒的囄
public static Image image_Blank;//I白的图?
public static Image image_Frame;//游戏框架的图?
public Images() {//构造函?
}
public static boolean init() {//初始化游戏中用到的图?
try {
image_base = Image.createImage("/huarongroad/BITBACK.png");
image_Frame = Image.createImage(image_base, 126, 0, 145, 177,
Sprite.TRANS_NONE);
//SpritecL用来{囄的,是MIDP2.0新新增加的支持游戏的Ҏ?
image_Zhaoyun = Image.createImage(image_base, 0, 0, UNIT, 2 * UNIT,
Sprite.TRANS_NONE);
image_Caocao = Image.createImage(image_base, UNIT, 0, 2 * UNIT,
2 * UNIT, Sprite.TRANS_NONE);
image_Huangzhong = Image.createImage(image_base, 3 * UNIT, 0, UNIT,
2 * UNIT,
Sprite.TRANS_NONE);
image_Machao = Image.createImage(image_base, 0, 2 * UNIT, UNIT,
2 * UNIT,
Sprite.TRANS_NONE);
image_Guanyu = Image.createImage(image_base, UNIT, 2 * UNIT,
2 * UNIT, UNIT,
Sprite.TRANS_NONE);
image_Zhangfei = Image.createImage(image_base, 3 * UNIT, 2 * UNIT,
UNIT, 2 * UNIT,
Sprite.TRANS_NONE);
image_Zu = Image.createImage(image_base, 0, 4 * UNIT, UNIT, UNIT,
Sprite.TRANS_NONE);
image_Blank = Image.createImage(image_base, 1 * UNIT, 4 * UNIT,UNIT,
UNIT,
Sprite.TRANS_NONE);
return true;
}catch (Exception ex) {
return false;
}
}
}
Drawcȝ源代码如下:
package huarongroad;
import javax.microedition.lcdui.*;
public class Draw {
//l制游戏中的囄
public Draw(Canvas canvas) {//构造函?
}
public static boolean paint(Graphics g, byte img, int x, int y) {
//在地囄x,y点绘制img指定的图?
try {
paint(g, img, x, y, Images.UNIT);//把地图x,y点{化成d的绝对坐标,l图
return true;
}
catch (Exception ex) {
return false;
}
}
public static boolean paint(Graphics g, byte img, int x, int y, int unit) {
try {
switch (img) {
case Images.CAOCAO://L?
//变成l对坐标Qƈ做调?
g.drawImage(Images.image_Caocao, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.GUANYU://d?
g.drawImage(Images.image_Guanyu, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.HUANGZHONG://画黄?
g.drawImage(Images.image_Huangzhong, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.MACHAO://画马?
g.drawImage(Images.image_Machao, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.ZHANGFEI://d?
g.drawImage(Images.image_Zhangfei, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.ZHAOYUN://画n?
g.drawImage(Images.image_Zhaoyun, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.ZU://d
g.drawImage(Images.image_Zu, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.BLANK://ȝ?
g.drawImage(Images.image_Blank, Images.LEFT + x * unit,
Images.TOP + y * unit,
Graphics.TOP | Graphics.LEFT);
break;
case Images.CURSOR://d?
g.drawRect(Images.LEFT + x * unit,
Images.TOP + y * unit,Images.UNIT,Images.UNIT);
break;
}
return true;
}catch (Exception ex) {
return false;
}
}
}
其中Imagescd的是l图位置帔RQ也是在画图时每个格子的长度和相对坐标原点位置要进行的调整Q、地图位|常量(地图的长、宽Q,地图标记帔RQh物对应的记号Q,地图l合标记帔RQ后面会l说Q,囄帔RQ存放h物的囄Q;DrawcM要负责在制定的位|画Zh物图片。下面我来说说ImagescM的地图标记常量和地图l合标记帔R。ؓ了能够灵zȝ安排各个关面的布局Q我们决定把游戏布局的信息存储在外部文g中,然后E序启动后把它读q来?
q样我们制定了一套存储图片的代码Q这是地图标记帔RQ如上面ImagescM定义的Caocao(Ҏ)用a字符来表C,当程序读到a字符时就能将它{化成Ҏ对应的图片,q在da字符的位|上q行昄。但是从实际观察中我们发现所有的囄q不是统一大小的,有的?个格子,有的?个格子,q有的占1个格子,而且即便同是占两个格子的囄q有横、竖之分。有鉴于此,我们引入了地囄合标记常量,是说在遇到占有多个格子的时候,?(也就是Images.LEFT)表示它的左边是一个真正的地图标记Q?(也就是Images.UP)表示它的上边是一个真正的地图标记Q?(也就是Images.LEFTUP)表示它的左上Ҏ一个真正的地图标记。地囄合标记常量其实就是用来占位置的,与实际显C无养I当后面我们将到移动时q会再来分析l合标记的用?
DrawcM要是用来在画布上d囑ŞQ它有两个paintҎQ这是很常见的函数重载。但是程序中实际上只用到?个参数的paintҎQ它直接获得要画囄的相对坐标位|信息,然后调用5个参数的paintҎ?个参数的paintҎ相对坐标位|信息{换成l对位置Qƈ实际调用Graphics.drawImage()ҎQ将Images中的囄M出来。这U实现方法的好处是灵zd便于扩展Q但你需要画囄位置q不能够对应到格子中的相对坐标位|时Q你可以直接调?个参数的paintҎQ而不必再M改这各类Q但你添加新的图片时Q只要在Images中增加对应的帔RQ然后向Draw?个参数的paintҎd一条处理就可以了。写到这里,两天的时间刚好用完?
三、需求分?
q部分叫做需求分析,听v来挺吓h的,其实是搞清楚我们要做什么,做成什么样Q那些不做。下面我引领着大家共同来完成这一步骤。首先,我们要做一个华定w的游戏,华容道的故事q里不再赘述了,但其中的人物在这里限定一下,如上面Imagesc里的定义,我们q个版本只提供曹?Caocao)、关?Guanyu)、张?Zhangfei)、n?Zhaoyun)、黄?Huangzhong)、马?Machao)和卒(Zu)。我们这里也限定一下游戏的操作ҎQ首先要通过方向键选择一个要Ud的区?是一张图?Q被选择的区域用黑色Ҏ框住Q选好后按Fire?是定?这块区域选中Q被选中的区域用l色Ҏ框住Q然后选择要移动到的区域,此时用红色方框框住被选择的区域;选好要移动到的区域之后按Fire键将要移动的区域(囄)Ud要移动到的区域,q去掉绿色和U色的方框。这里需要强调的概念有选择的区域、选中的区域、要Ud的区域和要移动到的区域,q四个概念请读者注意区分,当然也应当把q一部分记入数据字典之中?
Z使文章的重点H出(介绍如何制作一个J2ME的收集游?Q我们这里限定一些与本主题无关的内容暂不d玎ͼq关之后的动?实现时要用到TimerTask或Threadc,后箋的系列文章中我会详细介绍动画斚w的知?、关面之间的切换(其实很简单,当完成Q务之后重新再做一?、暂停和保存{操?q部分的内容介绍的资料很多,我也写不Z么新的东东来Q难免抄袭,故此免掉)?
需求分析基本完成,M午还有一D|_马上动手用ACDSee把从|上找来的BMP文gQ调整其大小?71*177(我的q个囄是两个部分合在一P所以比手机实际屏幕大了)Q另存ؓPNG格式。半天时间刚刚好Q不但搞清楚了要做的东东Q还把要用的囄准备好了?
四、概要设?
概要设计是从需求分析过渡到详细设计的桥梁和U带Q这一部分中我们确定项目的实现Ҏ和模块的划分。我们决定将整个目分成五个部分Q分别是前面介绍的Images、DrawQ还有Map和Displayable1和MIDlet1。Images和Drawcd能简单、结构固定,因此很多目我们都用这两各c,q里直接拿来Ҏp用了Q前面已l介l过q里不再赘述。MapcL用来从外部文件读入地图,然后保存在一个数l之中,q部分的内容是我们在本阶D讨论的重点。Displayable1是一个承了CanvascȝdQ它用来处理E序的主要控刉辑和一部分控制逻辑所需的辅助函敎ͼ主要函数应该包括用来l图的paint()函数、用来控制操作的keyPressed()函数、用来控刉择区域的setRange()函数、用来控刉择要移动到区域的setMoveRange()函数、用来移动选中区域的Move()函数和判断是否完成Q务的win()函数Q更具体的分析,我们攑ֈ详细设计中去l化。MIDlet1实际上就是一个控制整个J2ME应用的控制程序,其实也没有什么可特别的,它和我们前面介绍?Hello World"E序大同异Q这里就不展开来说了,后面会脓出它的全部代码?
MapcM要应该有一个Grid[][]的二l数l,用来存放华容道的地图Q还应该有一个read_map()函数用来从外部文件读取地囑ֆ容填充Grid数据l构Q再是要有一个draw_map()函数用来把Grid数据l构中的地图内容转换成图片显C出?当然要调用DrawcȝpaintҎ)。说到读取外部文ӞW者知道有两种ҎQ一U是传统的定义一个InputStream对象Q然后用getClass().getResourceAsStream()Ҏ取得输入,然后再从输入中取得外部文g的内容,例如
InputStream is = getClass().getResourceAsStream("/filename");
if (is != null) {
byte a = (byte) is.read();
}
q里h意文件名中的根\径是相对于便以后的class文g攄的位|,而不是源文g(java)。第二种Ҏ是用onnector.openInputStreamҎQ然后打开的协议是ResourceQ但是这U方法笔者反复尝试都没能调通,报告的错误是~少Resource协议Q估计第二种Ҏ用到J2ME的某些扩展类包,此处不再q。由于以前已l做q一些类似华定wq样的地图,q里直接l出Mapcȝ代码Q后面就不再详细解释MapcMQ以便于我们可以集中_֊处理Displayable1中的逻辑?
Mapcȝ代码如下Q?
package huarongroad;
import java.io.InputStream;
import javax.microedition.lcdui.*;
public class Map {
//处理游戏的地图,负责从外部文件加载地图数据,存放地图数据Qƈ按照地图数据l制地图
public byte Grid[][];//存放地图数据
public Map() {//构造函敎ͼ负责初始化地图数据的存储l构
this.Grid = new byte[Images.HEIGHT][Images.WIDTH];
//用二l数l存攑֜图数据,注意W一l是竖直坐标Q第二维是水q_?
}
public int[] read_map(int i) {
QA href="file://?Qfile://从</AQ外部文件加载地图数据,q存攑֜存储l构中,q回值是光标点的位置
//参数是加载地图文件的{
int[] a = new int[2];//光标点的位置Q?是水q位|,1是竖直位|?
try {
InputStream is = getClass().getResourceAsStream(
"/huarongroad/level".concat(String.valueOf(i)));
if (is != null) {
for (int k = 0; k Q?Images.HEIGHT; k++) {
for (int j = 0; j Q?Images.WIDTH; j++) {
this.Grid[k][j] = (byte) is.read();
if ( this.Grid[k][j] == Images.CURSOR ) {
//判断出光标所在位|?
a[0] = j;//光标水^位置
a[1] = k;//光标竖直位置
this.Grid[k][j] = Images.BLANK;//光标位|设成空白背?
}
}
is.read();//d回RQ?3Q?忽略?
is.read();//d换行Q?0Q?忽略?
}
is.close();
}else {
//d文gp|
a[0] = -1;
a[1] = -1;
}
}catch (Exception ex) {
//打开文gp|
a[0] = -1;
a[1] = -1;
}
return a;
}
public boolean draw_map(Graphics g) {
//调用Drawcȝ静态方法,l制地图
try {
for (int i = 0; i Q?Images.HEIGHT; i++) {
for (int j = 0; j Q?Images.WIDTH; j++) {
Draw.paint(g, this.Grid[i][j], j, i);//l制地图
}
}
return true;
}catch (Exception ex) {
return false;
}
}
}
对于像华定wq样的小型地囑֏以直接用手工来绘制地囄内容Q比如:
fa1c
2232
bd1e
2gg2
gihg
但是Q如果遇到像坦克大战或超U玛莉那L地图Q就必须另外开发一个地囄辑器?我会在后l的文章中介l用vb来开发一个地囄辑器)?
五、详l设?
详细设计是程序开发过E中臛_重要的一个环节,好在我们在前面的各个阶段中已l搭建好了项目所需的一些工P现在q个阶段中我们只需集中_֊设计好Displayable1中的逻辑?两天的时间当然不只干q点z,q要把其他几个类的设计修改一?
Displayable1q个c负责处理程序的控制逻辑。首先,它需要有表示当前关面的变量level、表C当前光标位|的变量loc、表CUd区域的变量SelectArea、表CUd到的区域的变量MoveArea、表C是否已有区域被选中而准备移动的变量Selected和Mapcȝ实例MyMap。然后,我们Ҏ用户按不同的键来处理不同的消息,我们要实现keyPressed()函数Q在函数中我们处理按键的上下左右和选中(Fire)Q这里的处理需要我展开来讲一Ԍ后面我很快会把这一部分详细展开?
接下来,是实现paint()函数Q我们打在q一部分中反复的重画背景、地囑֒选择区域Q这个函数必d理好区域被选中之后的画W颜色的切换Q具体讲是在没有选中M区域时要用黑色画W,当选重要移动的区域时用绿色画W,当选择要移动到的区域时改用U色ȝ(当然附加一张流E图是必不可的)?
再下面要实现的setRange()函数和setMoveRange()函数Q这两个函数用来讄要移动的区域和要Ud到的区域Q我的思\是利用前面在ImagescM介绍q的地图l合标记帔RQ当Ud到地囄合标记常量时Q根据该点地图中的值做逆向变换扑ֈ相应的地图标记常量,然后讄相应的loc、SelectArea和MoveArea,其中setMoveRange()函数q用C一个辅助函数isInRange(),isInRange()函数是用来判断给定的Ҏ否在已选中的要Ud的区域之?如果isInRange()的返回值是假ƈ且该点处的g是空白就表明要移动到的区域R犯了其他以被占用的区域。有了setRange()和setMoveRange()函数QMove()函数水到渠成了,Move()函数要Ud的区域移动到要移动到的区?在移动过E中分ؓ三步q行:
W一.复制要移动的区域Q?
W二.复制出的要Ud区域复制到要Ud到的区域(q两步分开q行的目的是防止在复制过E中覆盖掉要Ud的区?Q?
W三.用isInRange2()判断l定的点是否在要Ud到的区域?不在要Ud到的区域内的点设|成I白?下面我们详细的分析一下keyPressed()函数的实现方?首先,keyPressed()函数要处理按键的上下左右和选中(Fire),在处理时需要用CanvascȝgetGameAction函数来将按键的键D{换成游戏的方?q样可以提高游戏的兼Ҏ?因ؓ不同的J2ME实现,其方向键的键g一定是相同??
接下?分别处理四个方向和选中.当按下向上时,先判断是否已l选定了要Ud的区?即this.selected是否为真),如果没有选中要移动区域则让光标向上移动一?然后调用setRange()函数讄选择要移动的区域,再调用repaint()函数h屏幕,否则如果已经选中了要Ud的区?p光标向上Ud一?然后调用setMoveRange()函数判断是否能够向上Ud已选中的区?如果能移动就调用repaint()函数h屏幕,如果不能Udp光标向下退回到原来的位|?
当按下向下时,先判断是否已l选定了要Ud的区?如果没有选中要移动的区域则判断当前所处的区域是否Z个格?如果是两个格高则向下Ud两格,如果是一个格高则向下Ud一?接着再调用setRange()函数讄选择要移动的区域,而后调用repaint()函数h屏幕,否则如果已经选中了要Ud的区?p光标向下Ud一?然后调用setMoveRange()函数判断是否能够向下Ud已选中的区?如果能移动就调用repaint()函数h屏幕,如果不能Udp光标向上退回到原来的位|?按下向左时情况完全类似向上的情况,按下向右时情况完全类似向下的情况,因此q里不再赘述,详细情况请参见程序的源代码?
当按下选中键时,先判断是否已l选中了要Ud的区?如果已经选中了要Ud的区域就调用Move()函数完成pUd的区域到要移动到的区域的Udq程,接着调用repaint()函数h屏幕,然后已选择标记|成false,l箋调用win()函数判断是否完成了Q?否则如果q没有选定要移动的区域则再判断当前选中区域是否为空?如果不是I白将选中标记|成true,然后h屏幕.q里介绍一个技?在开发程序遇到复杂的逻辑的时?可以构造一格打印函数来所兛_的数据结构打印出来以利调?q里我们构造一个PrintGrid()函数,q个函数Ua是ؓ了调试之?效果q得不错.x我们完成了编码前的全部工作?
六、编?
整个目共有五个c?有四个类的代码前面已l介l过?而且是在其他目中用过的相Ҏ熟的代码.现在只需全力d现Displayable1c?Displayable1cȝ代码如下:
package huarongroad;
import javax.microedition.lcdui.*;
public class Displayable1 extends Canvas implements CommandListener {
private int[] loc = new int[2]; QA href="file://?Qfile://光</AQ标的当前位|,0是水q位|,1是竖直位|?
private int[] SelectArea = new int[4];//被选定的区域,卌Ud的区?
private int[] MoveArea = new int[4];//要移动到的区?
private Map MyMap = new Map();//地图c?
private boolean selected;//是否已经选中要移动区域的标志
private int level;//但前的关?
public Displayable1() {//构造函?
try {
jbInit();//JBuilder定义的初始化函数
}catch (Exception e) {
e.printStackTrace();
}
}
private void Init_game(){
//初始化游戏,d地图Q设|选择区域Q清IUd到的区域
this.loc = MyMap.read_map(this.level);//d地图文gQƈq回光标的初始位|?
//0为水q位|,1为竖直位|?
this.SelectArea[0] = this.loc[0];//初始化选中的区?
this.SelectArea[1] = this.loc[1];
this.SelectArea[2] = 1;
this.SelectArea[3] = 1;
this.MoveArea[0] = -1;//初始化要Ud到的区域
this.MoveArea[1] = -1;
this.MoveArea[2] = 0;
this.MoveArea[3] = 0;
}
private void jbInit() throws Exception {//JBuilder定义的初始化函数
QA href="file://?Qfile://初</AQ始化实例变?
this.selected = false;//讄没有被选中的要Ud区域
this.level = 1;
Images.init();//初始化图片常?
Init_game();//初始化游戏,d地图Q设|选择区域Q清IUd到的区域
setCommandListener(this);//d命o监听Q这是Displayable的实例方?
addCommand(new Command("Exit", Command.EXIT, 1));//d“退出”按?
}
public void commandAction(Command command, Displayable displayable) {
//命o处理函数
if (command.getCommandType() == Command.EXIT) {//处理“退出?
MIDlet1.quitApp();
}
}
protected void paint(Graphics g) {
//d函数Q用于绘制用L面,xC图片,勄选中区域和要Ud到的区域
try {
g.drawImage(Images.image_Frame, 0, 0,
Graphics.TOP | Graphics.LEFT);//画背?
MyMap.draw_map(g);//按照地图内容d
if ( this.selected )
g.setColor(0,255,0);//如果被选中Q改用绿色画选中的区?
g.drawRect(this.SelectArea[0] * Images.UNIT + Images.LEFT,
this.SelectArea[1] * Images.UNIT + Images.TOP,
this.SelectArea[2] * Images.UNIT,
this.SelectArea[3] * Images.UNIT);//d选择区域Q?
QA href="file://?Qfile://如</AQ果被选中Q就用绿?
QA href="file://?Qfile://否</AQ则Q用黑?
g.setColor(255,255,255);//恢复ȝ颜色
if (this.selected) {//已经选中了要Ud的区?
g.setColor(255, 0, 255);//改用U色
g.drawRect(this.MoveArea[0] * Images.UNIT + Images.LEFT,
this.MoveArea[1] * Images.UNIT + Images.TOP,
this.MoveArea[2] * Images.UNIT,
this.MoveArea[3] * Images.UNIT);//d要移动到的区?
g.setColor(255, 255, 255);//恢复ȝ颜色
}
}catch (Exception ex) {
}
System.out.println(Runtime.getRuntime().freeMemory());
System.out.println(Runtime.getRuntime().totalMemory());
}
private void setRange() {
//讄Ud后能够选中的区?
//调整当前光标位置到地囄M|,卌录h物信息的位置
if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DLEFT) {
this.loc[0] -= 1;//向左?
}else if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DUP) {
this.loc[1] -= 1;//向上?
}else if (this.MyMap.Grid[this.loc[1]][this.loc[0]] == Images.DLEFTUP) {
this.loc[0] -= 1;//向左?
this.loc[1] -= 1;//向上?
}
this.SelectArea[0] = this.loc[0];//讄光标的水q位|?
this.SelectArea[1] = this.loc[1];//讄光标的竖直位|?
//讄光标的宽?
if (this.loc[0] + 1 Q?Images.WIDTH) {
this.SelectArea[2] = this.MyMap.Grid[this.loc[1]][this.loc[0] + 1] != (byte) ???
1 : 2;
}else {
this.SelectArea[2] = 1;
}
//讄光标的高?
if (this.loc[1] + 1 Q?Images.HEIGHT) {
this.SelectArea[3] = this.MyMap.Grid[this.loc[1] + 1][this.loc[0]] != (byte) ???
1 : 2;
}else {
this.SelectArea[3] = 1;
}
}
private boolean setMoveRange() {
//讄要移动到的区域,能够Udq回true,否则q回false
for (int i = 0; i Q?this.SelectArea[2]; i++) {
for (int j = 0; j Q?this.SelectArea[3]; j++) {
if (this.loc[1] + j Q? Images.HEIGHT ||
this.loc[0] + i Q? Images.WIDTH ||
(!isInRange(this.loc[0] + i, this.loc[1] + j) &&
this.MyMap.Grid[this.loc[1] + j][this.loc[0] + i] !=
Images.BLANK)) {
return false;
}
}
}
this.MoveArea[0] = this.loc[0];
this.MoveArea[1] = this.loc[1];
this.MoveArea[2] = this.SelectArea[2];
this.MoveArea[3] = this.SelectArea[3];
return true;
}
private boolean isInRange(int x, int y) {
//判断l定的(xQyQ点是否在选定区域之内Qx是水q_标,y是竖直坐?
if (x Q? this.SelectArea[0] &&
x Q?this.SelectArea[0] + this.SelectArea[2] &&
y Q? this.SelectArea[1] &&
y Q?this.SelectArea[1] + this.SelectArea[3]) {
return true;
}else {
return false;
}
}
private boolean isInRange2(int x, int y) {
//判断l定的(xQyQ点是否在要Ud到的区域之内Qx是水q_标,y是竖直坐?
if (x Q? this.MoveArea[0] &&
x Q?this.MoveArea[0] + this.MoveArea[2] &&
y Q? this.MoveArea[1] &&
y Q?this.MoveArea[1] + this.MoveArea[3]) {
return true;
}else {
return false;
}
}
protected void keyPressed(int keyCode) {
//处理按下键盘的事Ӟq是Canvas的实例方?
switch (getGameAction(keyCode)) {//按键的D{化成方向帔R
case Canvas.UP://向上
if (!this.selected) {//q没有选定要移动的区域
if (this.loc[1] - 1 Q? 0) {//向上q有UdI间
this.loc[1]--;//向上Ud一?
setRange();//讄光标Ud的区域,该函数能光标移动到地图M|?
repaint();//重新l图
}
}else {//已经选定了要Ud的区?
if (this.loc[1] - 1 Q? 0) {//向上q有UdI间
this.loc[1]--;//向上Ud一?
if (setMoveRange()) {//能够UdQ该函数能够讄要移动到的区?
repaint();//重新l图
}else {//不能Ud
this.loc[1]++;//退回来
}
}
}
break;
case Canvas.DOWN://向下
if (!this.selected) {//q没有选定要移动的区域
if (this.loc[1] + 1 Q?Images.HEIGHT) {//向下q有UdI间
if (this.MyMap.Grid[this.loc[1] + 1][this.loc[0]] ==
Images.DUP){//该图片有两个格高
this.loc[1]++;//向下Ud一?
if (this.loc[1] + 1 Q?Images.HEIGHT) {//向下q有
QA href="file://U?Qfile://U</AQ动I间
this.loc[1]++;//向下Ud一?
setRange();//讄光标Ud的区域,
QA href="file://?Qfile://该</AQ函数能光标移动到地图M|?
repaint();//重新l图
}else {//向下没有UdI间
this.loc[1]--;//退回来
}
}else {//该图片只有一个格?
this.loc[1]++;//向下Ud一?
setRange();//讄光标Ud的区域,
QA href="file://?Qfile://该</AQ函数能光标移动到地图M|?
repaint();//重新l图
}
}else {
}
}else {//已经选定了要Ud的区?
if (this.loc[1] + 1 Q?Images.HEIGHT) {//向下q有UdI间
this.loc[1]++;//向下Ud一?
if (setMoveRange()) {//能够UdQ该函数能够讄要移动到的区?
repaint();//重新l图
}else {//不能Ud
this.loc[1]--;//退回来
}
}
}
break;
case Canvas.LEFT://向左
if (!this.selected) {//q没有选定要移动的区域
if (this.loc[0] - 1 Q? 0) {//向左q有UdI间
this.loc[0]--;//向左Ud一?
setRange();//讄光标Ud的区域,该函数能光标移动到地图M|?
repaint();//重新l图
}
}else {//已经选定了要Ud的区?
if (this.loc[0] - 1 Q? 0) {//向左q有UdI间
this.loc[0]--;//向左Ud一?
if (setMoveRange()) {//能够UdQ该函数能够讄要移动到的区?
repaint();//重新l图
}else {//不能Ud
this.loc[0]++;//退回来
}
}
}
break;
case Canvas.RIGHT://向右
if (!this.selected) {//q没有选定要移动的区域
if (this.loc[0] + 1 Q?Images.WIDTH) {//向右q有UdI间
if (this.MyMap.Grid[this.loc[1]][this.loc[0] + 1] ==
Images.DLEFT) {//该图片有两个格宽
this.loc[0]++;//向右Ud一?
if (this.loc[0] + 1 Q?Images.WIDTH) {//向右q有
QA href="file://U?Qfile://U</AQ动I间
this.loc[0]++;//向右Ud一?
setRange();//讄光标Ud的区域,
QA href="file://?Qfile://该</AQ函数能光标移动到地图M|?
repaint();//重新l图
}else {//向右没有UdI间
this.loc[0]--;//退回来
}
}else {//该图片只有一个格?
this.loc[0]++;//向右Ud一?
setRange();//讄光标Ud的区域,
QA href="file://?Qfile://该</AQ函数能光标移动到地图M|?
repaint();//重新l图
}
}else {
}
}else {//已经选定了要Ud的区?
if (this.loc[0] + 1 Q?Images.WIDTH) {//向右q有UdI间
this.loc[0]++;//向右Ud一?
if (setMoveRange()) {//能够UdQ该函数能够讄要移动到的区?
repaint();//重新l图
}else {//不能Ud
this.loc[0]--;//退回来
}
}
}
break;
case Canvas.FIRE:
if (this.selected) {//已经选定了要Ud的区?
Move();//要Ud的区域移动到刚选中的区?
repaint();//重新l图
this.selected = false;//清除已选定要移动区域的标志
if ( win()) {
System.out.println("win");
}
}else {//q没有选定要移动的区域
if (this.MyMap.Grid[this.loc[1]][this.loc[0]] ==
Images.BLANK) {//要移到的位置是一个空?
}else {//要移到的位置不是I白
this.selected = true;//讄已选定要移动区域的标志
}
repaint();//重新l图
}
break;
}
}
private boolean win(){
QA href="file://?Qfile://判</AQ断是否已经救出了曹?
if ( this.MyMap.Grid[Images.HEIGHT - 2 ][Images.WIDTH - 3 ] == Images.CAOCAO )
return true;
else
return false;
}
private void PrintGrid(String a) {
QA href="file://?Qfile://打</AQ印当前地图的内容,用于调试
System.out.println(a);
for (int i = 0; i Q?Images.HEIGHT; i++) {
for (int j = 0; j Q?Images.WIDTH; j++) {
System.out.print( (char)this.MyMap.Grid[i][j]);
}
System.out.println("");
}
}
private void Move() {
QA href="file://?Qfile://</AQ要Ud的区域移动到刚选中的区?
if (this.MoveArea[0] == -1 || this.MoveArea[1] == -1 ||
this.SelectArea[0] == -1 || this.SelectArea[1] == -1) {//没有选中区域
}else {//已经选中了要Ud的区域和要移动到的区?
byte[][] temp = new byte[this.SelectArea[3]][this.SelectArea[2]];
QA href="file://?Qfile://复</AQ制要移动的区域Q因块区域可能会被覆盖掉
for (int i = 0; i Q?this.SelectArea[2]; i++) {
for (int j = 0; j Q?this.SelectArea[3]; j++) {
temp[j][i] =
this.MyMap.Grid[this.SelectArea[1] +j]
[this.SelectArea[0] + i];
}
}
QA href="file://PrintGrid"Q?a href="file://PrintGrid/">file://PrintGridQ?AQ?"1"); // 调试信息
QA href="file://?Qfile://</AQ要Ud的区域移动到刚选中的区域(卌Ud到的区域Q?
for (int i = 0; i Q?this.SelectArea[2]; i++) {
for (int j = 0; j Q?this.SelectArea[3]; j++) {
this.MyMap.Grid[this.MoveArea[1] + j]
[this.MoveArea[0] + i] = temp[j][i];
}
}
QA href="file://PrintGrid"Q?a href="file://PrintGrid/">file://PrintGridQ?AQ?"2");// 调试信息
QA href="file://?Qfile://</AQ要Ud的区域中无用内容|成I白
for (int i = 0; i Q?this.SelectArea[3]; i++) {
for (int j = 0; j Q?this.SelectArea[2]; j++) {
if (!isInRange2(this.SelectArea[0] + j,
this.SelectArea[1] + i)) {//该点是不在要Ud?
QA href="file://?Qfile://的</AQ区域之内,需|空
this.MyMap.Grid[this.SelectArea[1] + i]
[this.SelectArea[0] + j] = Images.BLANK;
}else {
}
}
}
QA href="file://PrintGrid"Q?a href="file://PrintGrid/">file://PrintGridQ?AQ?"3");// 调试信息
this.SelectArea[0] = this.MoveArea[0];//重置选中位置的水q_?
this.SelectArea[1] = this.MoveArea[1];//重置选中位置的竖直坐?
this.MoveArea[0] = -1;//清空要移动到的位|?
this.MoveArea[1] = -1;//清空要移动到的位|?
this.MoveArea[2] = 0;//清空要移动到的位|?
this.MoveArea[3] = 0;//清空要移动到的位|?
}
}
}
代码的相兛_?在详l设计阶D已l讲q?代码中有比较相近的注?误者自行研d?全部的代码写好,用wtk2.0自带的Ktoolbar工具建立一个工E?接下来把M源文件放到正位|下,然后点击build,再点run,完成了E序的编?当然如果有错误还要修改和调试?
七、测?
作ؓ一个真正的产品要经q单体测试、结合测试和pȝ试。由于项目本w简?而且大部分代码已l是相对成熟?我们跌单体试Q又׃W者的实际环境所?无法搞到Java手机,无法架设OTA服务?因此我们也只能放弃系l测试。那么就让我们开始结合测试吧。测试之前要先出一个测试式样书,也就是测试的计划。我们将它简化一?只测试如下几U情?W一、对各种形状的区域的选择和移?W二、͘q边界区域的选择和移?W三、同一区域的反复选择和反复移?W四、非法选择和非法移动。有了测试的目标,接下来的工作是用wtk2.0自带的Run MIDP Application工具q行试。打开q个工具,加蝲huarongRoad的jad文g,E序׃自动q行,选择launch上MIDlet1q个E序,华容道游戏就会跃然屏q之?接下来的工作是左三?右三?拇指扭扭,来做试。测试过E中发现M的问?立刻发一个bug给自己,然后又是痛苦的调试和修正bug,如此如此?
八、发?
谈到发布,其实是个关键,再好的品不能很好的发布出去也只是个产品而已,变不成商品也得不到回报.׃W者的条g所?q里只能是纸上谈?不过q是希望能够使读者对q一q程有所了解(|上的资料也很多)?
J2ME的程序发布一般都是通过OTA(Over The Air),你只需要一台有公网IP的主机和一个普通的web Server可以了(管要求很低,但笔者还是没?Q这里我们以apacheZ介绍一下OTA服务的配|,首先是安装好了apache服务器,然后在conf目录下找到mime.types文gQ在该文件中加入如下两行
application/java-archive jar
text/vnd.sun.j2me.app-descriptor jad
然后重vapache服务器就可以了。接下来的工作就是修改jad文g中MIDlet-Jar-URL:后面的参敎ͼ它改ؓURL的绝对\径,卻IA href="http://***/"Q?a href="http://***/">http://***/Q?AQhuarongroad.jar(其中***是你的域名或IP地址)。在下面是用java手机下蝲jad文gQ它会自动部|相应的jar文gq加载它。剩下的工作和在模拟器上操作是一L了?
九、项目ȝ
xQ我们已l完成了一个J2ME游戏的全部开发过E,E序中涉及到了调研、分析、设计、编码、测试和发布{方面的问题Q其实在实际的工作中q有很多更ؓ具体的问题,毕竟技术只在Y件开发过E中占据很有限的一部分Q这里限于篇q的限制无法一一具体展开。今后,W者计划再写一用J2ME开发手机屏保的文章Q借此Z向读者展CJ2ME动画技术;然后再写一J2ME|络应用的文章,做一个类似开心辞兔RL知识问答游戏Q以便向读者展CJ2ME的网l技术;待这两方面的技术交待清楚之后,我将引领读者制作一个稍大一些的游戏?/p>
|
set path=c:jdk1.3in;%path% set classpath=.; |
?/td> | 颜色?/td> |
1 | 2 |
2 | 4 |
4 | 16 |
8 | 256 |
![]() |
![]() |
![]() |
![]() |
simpleMIDlet.java import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class simpleMIDlet extends MIDlet implements CommandListener { private Display display; // 引用MIDlet的Display 对象 private TextBox tbxMain; // Textbox 昄一条消?br />private Command cmdExit; // 讑֮按钮用于退出MIDlet // MIDlet构造程?br />public simpleMIDlet() { display = Display.getDisplay(this); cmdExit = new Command("Exit", Command.SCREEN, 1); tbxMain = new TextBox("Simple MIDlet", "Welcome ", 50, 0); tbxMain.addCommand(cmdExit); tbxMain.setCommandListener(this); } // 被应用程序管理器调用来启动MIDlet?br />public void startApp() { display.setCurrent(tbxMain); } // 一个必要的Ҏ public void pauseApp() { } file://一个必要的Ҏ public void destroyApp(boolean unconditional) { } file://查一下是否选择了退出命?br />public void commandAction(Command c, Displayable s) { if (c == cmdExit) { destroyApp(false); notifyDestroyed(); } } } |
avac -bootclasspath c:j2memidp-fcsclasses simpleMIDlet.java |
preverify -classpath c:j2memidp-fcsclasses;. -d . simpleMIDlet |
midp firstMIDlet |
?1 | |
属?/div> | 用?/div> |
MIDlet-Name | MIDletE序包的名称。例如“Game Pack?/td> |
MIDlet-Version | MIDlet的版本号 |
MIDlet-Vendor | MIDlet的创或提供?/td> |
MIDlet-Icon | 应用E序理器把q个图标?MIDlet-Name相关联,q是一个图形文Ӟ?PNG图象格式储存?/td> |
MIDlet-Description | 描述 MIDlet的文?/td> |
MIDlet-Info-URL | 可能提供更多MIDlet?或供应商信息?URL |
MIDlet- | q个属性包括三D信息: ??MIDlet名称 ??用于q个 MIDlet的图?可? ??应用E序理器将调用来加载这?MIDlet的类?在我们的?Game Pack”例子中Q有两个条目Q?MIDlet-1: KOF, /images/kof.png, kof.kofMIDlet MIDlet-2: Golf, /images/golf.png, golfMIDlet |
MIDlet-Jar-URL | JAR文g?URL |
MIDlet-Jar-Size | JAR文g的大?/td> |
MIDlet-Data-Size | 持久数据存储必需的最字节数 |
MicroEdition-Profile | MIDlet需要哪一U?J2ME?/td> |
MicroEdition-Configuration | MIDlet需要哪一U?J2ME配置 |
MIDlet-Name: Show Properties MIDlet MIDlet-Version: 1.0.1 MIDlet-Vendor: ABC WorkGroup. MIDlet-1: ShowProps, , showProperties MicroEdition-Profile: MIDP-1.0 MicroEdition-Configuration: CLDC-1.0 MIDlet-Description: A simple property list example MIDlet-Data-Size: 1500 |
MIDlet-Name: Show Properties MIDlet MIDlet-Version: 1.0.1 MIDlet-Vendor: ABC WorkGroup. MIDlet-Jar-URL: file://showProperties.jar MIDlet-Jar-Size: 1132 MIDlet-1: ShowProps, , showProperties JadFile-Version: 1.5 MIDlet-Data-Size: 500 |
import javax.microedition.midlet.*; public class showProperties extends MIDlet { public void startApp() throws MIDletStateChangeException { System.out.println("Vendor: " + getAppProperty("MIDlet-Vendor")); System.out.println("Description: " + getAppProperty("MIDlet-Description")); System.out.println("JadFile Version: " + getAppProperty("JadFile-Version")); System.out.println("MIDlet-Data-Size: " + getAppProperty("MIDlet-Data-Size")); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } } |
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class MIDlet1 extends MIDlet implements CommandListener { private Display display; // 引用Display对象 private TextBox tbxMain; // 昄消息的文本框 private Command cmdExit; // 退出MIDlet的命? // 构造程? public MIDlet1() { display = Display.getDisplay(this); cmdExit = new Command("Exit", Command.SCREEN, 1); tbxMain = new TextBox("MIDlet 1", "Welcome", 50, 0); tbxMain.addCommand(cmdExit); tbxMain.setCommandListener(this); } // 启动MIDlet时由应用E序理器调? public void startApp() { display.setCurrent(tbxMain); } // 一个必要的Ҏ public void pauseApp() { } // 一个必要的Ҏ public void destroyApp(boolean unconditional) { } file://查一下是否选择Exit命o public void commandAction(Command c, Displayable s) { if (c == cmdExit) { destroyApp(false); notifyDestroyed(); } } } |
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class MIDlet2 extends MIDlet implements CommandListener { private Display display; file://引用Display对象 private List lstMain; private Command cmdExit; // 退?MIDlet的命? // 构造程? public MIDlet2() { display = Display.getDisplay(this); cmdExit = new Command("Exit", Command.SCREEN, 1); lstMain = new List("MIDlet 2", Choice.IMPLICIT); lstMain.append("Welcome Back", null); lstMain.addCommand(cmdExit); lstMain.setCommandListener(this); } file://启动MIDlet时由应用E序理器调? public void startApp() { display.setCurrent(lstMain); } file://一个必要的Ҏ public void pauseApp() { } file://一个必要的Ҏ public void destroyApp(boolean unconditional) { } file://查一下是否选择Exit命o public void commandAction(Command c, Displayable s) { if (c == cmdExit) { destroyApp(false); notifyDestroyed(); } } } |
javac -bootclasspath c:mefcs*.java preverify -classpath c:mefcs;. -d . MIDlet1 MIDlet2 jar cvfm MIDlets.jar manifest.txt MIDlet1.class MIDlet2.class spin.png |
MIDlet-Name: MIDlet Examples MIDlet-Version: 1.0 MIDlet-Vendor: My Corporation Inc. MIDlet-1: MIDlet1, /spin.png, MIDlet1 MIDlet-2: MIDlet2, /spin.png, MIDlet2 MicroEdition-Profile: MIDP-1.0 MicroEdition-Configuration: CLDC-1.0 |
jar cvfm MIDlets.jar manifest.txt MIDlet1.class MIDlet2.class spin.png |
MIDlet-Name: MIDlet Examples MIDlet-Version: 1.0 MIDlet-Vendor: My Corporation Inc. MIDlet-Description: Two simple examples to show how to compile and run a MIDlet MIDlet-Jar-URL: http://localhost/MIDlets.jar MIDlet-Jar-Size: 2604 MIDlet-1: MIDlet1, /spin.png, MIDlet1 MIDlet-2: MIDlet2, /spin.png, MIDlet2 |
MIDlet-1: MIDlet1, /spin.png, MIDlet1 MIDlet-2: MIDlet2, /spin.png, MIDlet2 |
midp -transient file://MIDlets.jad |
file://J2ME/MIDlets/welcome.jad |
![]() |
midp -transient http://localhost/MIDlets.jad |
midp -transient http://www.yourwebserver.com/path/MIDlets.jad |
c:j2me | MIDletPackage => Java 源代码和manifest.txt文g | jclasses => 从Java~译器编译的c? pclasses =>l过预验证的c? resources =>资源文g (囑փ文g{? |
MIDlet1.java: package simpleMIDlets; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class MIDlet1 extends MIDlet implements CommandListener { ... } MIDlet2.java: package simpleMIDlets; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class MIDlet2 extends MIDlet implements CommandListener { ... } |
manifest.txt: MIDlet-Name: MIDlet Examples MIDlet-Version: 1.0 MIDlet-Vendor: My Corporation Inc. MIDlet-1: MIDlet1, /resources/spin.png, simpleMIDlets.MIDlet1 MIDlet-2: MIDlet2, /resources/spin.png, simpleMIDlets.MIDlet2 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-1.0 MIDlets.jad: MIDlet-Name: MIDlet Examples MIDlet-Version: 1.0 MIDlet-Vendor: My Corporation Inc. MIDlet-Description: Packaging multiple MIDlets MIDlet-Jar-URL: http://localhost/MIDlets.jar MIDlet-Jar-Size: 2884 MIDlet-1: MIDlet1, /resources/spin.png, simpleMIDlets.MIDlet1 MIDlet-2: MIDlet2, /resources/spin.png, simpleMIDlets.MIDlet2 |
javac -bootclasspath c:mefcs-d jclasses *.java |
preverify -classpath c:mefcs; -d pclasses jclasses |
jar cvfm MIDlets.jar manifest.txt -C pclasses . resources |
midp -transient file://MIDlets.jad |
midp -transient http://localhost/MIDlets.jad |
|
W二?J2ME的体pȝ?/b> J2ME领域的新的开发者常常被q些事实困惑的, 事实上, Sun的第一个配|?现在只是一U配|的引用实现 )带有UCؓ KVM的虚拟机引用实现Q?KVM满配置的虚拟机的必要条件。然而, Sun?KVM也可以被另外一个虚拟机所代替Q现在, 正是因ؓ配置和虚拟机l合得有点紧密,因此D了这么多的乱? W三?详细谈谈J2ME配置
J2ME现在定义两个配置Q?Connected Device configuration(q接讑֤配置 CDC )和限制性更强的 Connected Limited Device Configuration (有限q接讑֤配置)?/font> 表的实现?Java应用E序接口的一个集合,用于适应被定义配|的应用E序接口提供的服务,表是一个完整的q行环境Q一个在表上执行的应用程序不需要额外的支持cR?
J2ME没有定义满q两U配|的标准化用h口,Sun也承认现在的消费讑֤多种多样Q用L面也各不一P所以定义一个可用于所有用L界面是一场失败的战争?J2ME中的用户界面定义在简表中?/p>
现在个h计算机系l的数量和种cdl发展到无法控制的地步,请你想一惻I你编写的E序q行在“信息家电”舞台的情景吧,q些信息家电包括呼叫器,行动电话Q像Palmq样的个人数字助?PDA)Q电视机盒QPOSl端以及其他的消费电子设备。现在全世界上光是手提电话生产商有许多Q更不用说别的家电设备了Q而且每一U家电设备又有不同的Ҏ和界面。所以,你可以想刎ͼ Java应用E序的轻便性以及能够解军_发这么许多不同的讑֤E序的能力,使大家对J2ME有很大的期许。当ӞZ更好的开发这些信息家电,p求把Java的精髓压~进一个非常小的程序包中,q就是J2ME?
J2ME是一U通过许多部g和规范的技术, q众多的部g和规范帮?J2ME来满众多的消费品的不同的需要。和所有的爪哇E式语言技术一P在它的核心属于一U虚拟机?像使用所?Java技术一PJ2ME的核心也在一U虚拟机中?最初,用于 J2ME应用E序虚拟机的被称?Kilobyte virtual machine或简U?KVM。就像它名称的含义, KVM比较,通常只有 128K或更。这比v我们通常了解和用的 Java 2标准?Java虚拟?( JVM )?32 MB来说小得多了?
用于q接虚拟机的是一pd配置和简表,它们提供了用于特?J2ME环境的类应用E序接口(见图二)?每个配置和简表处理一般或具体的消费品,配置和简表规范是由多U多L讑֤生商和用户共同开发ƈ建立的。配|是用于一l通用讑֤的最的 Javaq_Q?常常归ؓ一U横向的讑֤分组Q相Ҏ_横向分组讑֤是那些共享相同的内存安排Q通信带宽Q能量需求以及用戯力的讑֤Q一般认为配|能够提供这众多的设备的所有需求?
图二解释Q?J2ME层次 Java虚拟机是 J2ME技术的核心Q但是配|和表提供特D环境的cd用程序接口。配|是用于一l通用讑֤的最的 Javaq_Q而简表则为具体的讑֤家族或特别的应用E序提供更具体的能力?/font>
另一斚wQ简表完善了 配置Qؓ某个具体的设备家族或某个具体的工业片D应用程序提供更高的性能?换言之, 表ؓ具体的纵向市场的讑֤比如说行动电话提供更多的性能。这里的关键是 表必d?配置Q?没有 配置和虚拟机提供核心cd用程序接口和q行期环境的话,表也不会工作?br />
通常Q简表ؓ一U给定的垂直分组讑֤提供用户界面、输入法、持久性机制。这c?表被认ؓ是发展这些设备应用程序的完整的工具包。我们见到最多的应用E序表的例子q动电话简表和个h数字助手(PDA)?其他表ؓ范围宽广的设备提供非常特D的功能或应用程序可UL性,q方面的例子是提供q程Ҏ调用 ( RMI )功能的简表和提供l一银行事务的简表?br />
虚拟机?配置?表…你是不是已l被搞迷p了Q?如果q样的话Q我们就来简化一?J2ME体系l构吧?如果你想为小型信息家늼?Java应用E序的话Q你需要两个前提:一?配置和至一?表?现在Q?一般是配置捆绑了虚拟机和一套针对你的^台所能够用的横向分组讑֤的Javacd。其ơ,你至还需要一?表来Z的^台提供附加的 Javac,q个 表通常会ؓ你的讑֤提供用户界面、输入和数据库类。有了这两个前提Q你׃使用 JavaZ的设备编写应用程序的基本的J2ME环境?
J2ME可以在好几个不同的配|中q行配置?像先前提到的,每个配置Zl通用讑֤提供最的 Javaq_Q到目前为止Q只有两U配|规范。通过 Java规范定义的这两种配置?Connected Limited Device Configuration (有限q接讑֤配置Q?CLDC )?Connected Device Configuration (q接讑֤配置 CDC )?br />
CLDC是ؓ使用较小的存储容量的讑֤设计?(参见? )?CLDC用于内存?28?512K之间的消费电子设备, q一cd中典型代表的讑֤包含呼叫器、行动电话、PDA和POSl端Q而另一斚wQ?CDC用于?PC机小但是h?512K内存多的讑֤Q这一c设备包括互联网l电视系l、机盒、POSpȝ、汽车导航以及娱乐系l。一般来_ CDC使小型设备只要具有少量的资源Q至比台式的资源pq行Java~程Q而CLDC使小型设备所拥有的资源只要比一张智能卡多一点就可以q行Java~程了?
图二解释 讑֤覆盖的范?J2ME有两个配|CLDC?CDCQCLDC是ؓ使用较小的存储容量的讑֤设计的,而CDC用于?PC机小但是h?512K内存多的讑֤?/font>
除了在容量大和能力上对虚拟定了必要条gQ配|还规定了类应用E序接口要包含常见的 java.io?java.net?java.util?java.lang包,配置可能q要包括其他需要的E序包?
CLDC
CLDCh可以q溯?999qJavaOne大会上介l的Sun的第一个袖珍版 Java和第一?KVM以及相关的类库,虽然 CLDC和所有的配置都满x拟机的条Ӟ可它本nq不是虚拟机QCLDC的引用实现只是包含在当前的分布中?KVM?br />
Ҏ规范中所_q行 CLDC的设备应该有 512K或更的内存I间、一个有限的甉|供给 (通常是用电池)、有限的或断断箋l的|络q接?( 9600 bps或更?)以及多样化的用户界面甚至没有用户界面?通常说来Q这个配|是Z人化的、移动的、有限连接信息设备而设计,比如呼叫器、移动电话和 PDA{?br />
?J2SE相比Q?CLDC~少下列所说的q些特征Q?br />
AWTQ抽象窗口开发包Q, Swing或其他图形库
用户定义c装载器
cd例的最l化
q引用
RMI
ReflectionQ映)
CLDC有四个包Q?java.lang?java.util?java.io?javax.microedition?除了 microedition包以外,其他的这几个包都是J2SE包的核心子集QCLDC采用q些J2SEcdQ但是把其中一些在微型讑֤中用不到cR属性、方法去掉了。因?CLDCcd有许多细微的差别?如果您想研究J2SE?CLDCcd之间的差别,请参阅相x档,在此׃详细说明了?br />
惌理解Z么CLDC去除q么多J2SE中重要的cd特征Q请回想一下与 CLDC相关的两条基本原理。首先,它只?512K的内存空_ 而像RMI和映需要的内存太大了?其次Q配|必Lؓ一l通用讑֤提供最的 Javaq_?在个人移动信息设备领域中Q许多系l都不能支持 J2SE中的众多的高U特征?例如Q许多消费电子品不能支持QҎQ?因此 FloatQQ点类Q和 DoubleQ双_ֺc)p删除了?再看另外一个例子,许多pȝ没有或不提供讉K一个文件系l的功能或权限?因此与文件有关的cM被丢弃了。又如,错误处理是一个代价非帔R的过E处理,在许多消费电子设备中Q故障恢复是很难的甚x不可能的?所以在 CLDC中,许多错误处理cM被删除了?
java.microeditionE序包提供了一个一般的l构来替代许?J2SE|络输入/输出cR?CLDC一般连接器l构q定义了一?Connectorc,允许许多不同cd的连接能够用静态方法,下表列出使用同一个Connectorcd建和打开五种不同cd的连接的ҎQ?br />
HTTP Connector.open("http://www.xyz.com");
套接?Connector.open("socket://111.222.111.222:9000");
通讯端口 Connector.open("comm:1;baudrate=9600");
数据?Connector.open("datagram://111.222.111.222");
文g Connector.open("file:/xyz.dat");
一般连接器l构提供l应用程序开发者一个到通用低水q硬件的单的映射表。成功执?open语句返回一个实C般连接界面的对象?
CDC
CDC늛了个人电脑与有至?512K内存的小型设备之间的中间地带。现在,q一c设备通常是共享的、固定的 (不用Ud)|络q接信息讑֤Q像电视机机盒Q网l电视系l、互联网电话与汽车导?׃pȝ{等?br />
首先QCDCZ J2SE 1.3应用E序接口Q包含所有定义在CLDC规范(包括 javax.microeditionE序?中的Java语言应用E序接口。与CLDC相比Q?CLDC所有缺的Ҏ和cd CDC中都被补齐,包含映射、最l化、所有的错误处理cRQҎ、属性、输?输出 ( File?FileInputStream{等 )和弱的引用?一般说来, CDC中预期的cd括一个J2SE子集和一个完整的 CLDC集Q如?中所C:
? QJ2SE、CLDC和CDC的关p?
像使用所有的配置一PCDC有基层虚拟机的具体的必要条g?Ҏ CDC规范Q基层虚拟机必须提供实现完整?Java虚拟机的支持 ?如果虚拟机实现有一个用于激z设备的本地Ҏ的界面,它必d?JNI 1.1版本?如果虚拟机实现有一个调试界面,它必d?Java虚拟试界?( JVMDI )规范?如果虚拟机有一个简表界面,它必d?Java虚拟机简表界?( JVMPI )规范?可见Qؓ了实现这些功能,CDC肯定会变得很大,׃能称其ؓK虚拟ZQ因此,我们通常U用于CDC的虚拟机?CVMQ这里的 C代表 compact、connected、consumer?br />
W四?谈谈J2ME?/b>
虽然配置Zl通用讑֤提供了最的 Javaq_Q但是应用程序开发者感兴趣的是Z个个别的讑֤生应用E序Q当他们只是使用配置的话Q他们编写的应用E序׃有一些欠~?配置必须满所有的讑֤的最的要求Q?用户界面、输入机制和数据持久性有高度地设备具体性,每一U设备都有自q用户界面、输入机制和数据存储ҎQ这些往往不在配置所满的最要求的范围之内?
表ؓ相同消费电子讑֤的不同的生商提供了标准化的 JavacdQ?事实上,虽然配置规范的开发由 Sun领导Q但是许多简表规范仍l由Ҏ讑֤的供应商领导?比如_ Motorola领导了行动电话和呼叫器简表规范的开发,又如 Palm 领导 PDA表的开发?
现在Q五个已知简表已l有了规? CQ每个简表的责Q都是Z完善配置的不I下表列出了这五个表:
?/td> 完善配置 Mobile information devices profile (MIDP) Ud电话和呼叫器 CLDC Personal digital assistant profile Palm和Handspring的PDA 讑֤ CLDC Foundation profile 用于所有不需要GUI的CDC讑֤的标准简?CDC Personal profile 替代PersonalJava的Foundation完善的简?CDC RMI profile 提供RMI的Foundation完善的简?CDC
现在我想谈一谈另一个Javacd集,它现在差不多可以被认为是另一个简表了。当Sun为Palm开发第一个KVMӞ他们需要一l类?开发Palm的演C程序。这套类库被装q?com.sun.kjavaE序包, ?CLDC早期的开发中Q这些类被广泛的使用来测试和演示 J2ME。因?kjava是唯一的允许应用程序开发者?J2ME?KVM开发应用程序的c,所以它pq泛使用了。甚臛_了今天,一个用?PDA或更Ҏ一点的 Palm的简表多已经在开发中Q许多开发者仍然希望?kjavacL开?PDA应用E序。尽?kjavacM被支持,q且仅仅用于设计试E序或演C程序,q且它们被一个即到来的表所替代Q但是开发者们仍然热衷于用它来开发?br />
MIDP
Mobile Information Device Profile(Ud信息讑֤?,U?MIDP )Q第一个实现的表,补充?CLDCq且提供应用E序语义和控件、用L面、持久存储器、网l和用于Ud电话的计时器、双通道呼叫器和其他无线电设备?因ؓ MIDP?CLDC两者都有引用实玎ͼ我们可以使用一个例E来研究一下这个简表?br />
下面的例子是一个允许用戯入代表想知道的基金报L代号的例子。应用程序然后通过 HTTP接到一个金融网站,获得基金报hQ把h储存在一个数据库Q然后把hq回l用戗?
// 到如需要的J2MEc?br />import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;
// 扩展MIDletcL构徏我们的自定义MIDlet
public class FundTracker extends MIDlet implements
CommandListener {
file://昄理者变?br />private Display display = null;
file://MIDlet的表单变?br />private RequestForm reqForm = null;
file://MIDlet构徏?br />public FundTracker () {
display = Display.getDisplay(this);
reqForm = new RequestForm("Fund Tracker");
reqForm.initForm();
reqForm.setCommandListener(this);
}
file://开?MIDlet 应用E序
protected void startApp() {
display.setCurrent(reqForm);
}
file://暂停 Midlet
protected void pauseApp() {
}
file://销毁Midlet
protected void destroyApp(boolean unconditional) {
}
file://通过监听者响应命?br />public void commandAction(Command c, Displayable s) {
if (c == reqForm.getExitCommand()) {
destroyApp(false);
notifyDestroyed();
return;
}
if ((c == reqForm.getGetCommand()) &&
(reqForm.getSymField().getString().length() > 0)) {
getAndDisplayQuote();
} else
{
reqForm.getMsgString().setText("Symbol required");
}
}
file://储存?分开的成对的基金字符串和报h字符?br />private void storeQuote (String fund, String newQuote) {
file://数据库变?
RecordStore quoteDB = null;
try {
quoteDB = RecordStore.openRecordStore(
"FundQuotes", true);
byte[] data = (fund + "#" + newQuote).getBytes();
int size = data.length;
quoteDB.addRecord(data, 0, size);
quoteDB.closeRecordStore();
}
catch (Exception recordException) {
System.out.println("Unable to store quote and/or
use Fund Quote database.");
}
}
file://通过QuoteServicecd回提交的代号表示的基金报?br />private void getAndDisplayQuote(){
String fundSymbol = reqForm.getSymField().getString();
if (fundSymbol.length() > 0) {
String theQuote = QuoteService.getQuote(fundSymbol);
if (theQuote != null) {
storeQuote(fundSymbol, theQuote);
reqForm.getMsgString().setText(theQuote);
}
else
reqForm.getMsgString().setText("No quote" +
'' '' + "Check Symbol");
}
}
}
MIDP应用E序UCؓ MIDletQ?Z创徏一?MIDletQ你必须写一个扩展基?MIDletcȝc?(像我们在上面代码段中列出的那样Q?q有点类似常见的 applet?servlet?MIDlets独有的东西是把多?MIDletl成一?MIDlet套g的能力?q就允许 MIDlet在一个单独的 JVM环境中共享资源,比如一个数据库{等?事实上,我们上面l出的例子还包括一?MIDlet ( RetrieveQuoteQ见上段E序Q,用于取回所报h根{?当MIDlet被请求时Q?MIDlet通过构造程序实例化Q然后调用实例的 startApp()Ҏ?
?FundTracker例子中, MIDlet的用L面或昄是由 Displaycȝ一个实例管理的?对于每个 MIDletQ只有一个显C管理器实例?所有可以显C的目Q像屏幕或画布(canvasQ,通过q个理器都能够成ؓ可见的。因动电话和呼叫器能力的多样?又因为用于这些设备的应用E序cd的差异, MIDP规范提供了两U类型的用户界面。一个可UL性稍差、明设备、低水^的应用程序接口,允许囑Ş元素_的控制和攄?q个接口cd是用于应用程序特性比较典型的讑֤特别设计的,比如电子游戏?一个可UL性稍好的、抽象的、高U的 GUI应用E序接口Q提供来用于商业应用E序?
我们的例E用的是高U的应用E序接口和典型的用户界面lg (文本框,列表{等 )Q是q类界面通用的。比如说Q实际的表单和所有的组件在一个单独的文g中都已定义?像在代码段一中列出的那样Q当 MIDlet创徏Ӟ一个表单的实例?MIDlet兌?在调?MIDlet startApp()Ҏ的时候,通过 Display对象昄表单?使用一个用于表单的c,允许我们在我们简单的报h索应用程序中重新使用q个表单 ( RetrieveQuote )。ؓ了清晰性和风格Q我们通过一个单独的cL定义报h服务?Z演示一般连接器l构的能力,我们的报h务类通过一?Connector实例取回报h?
MIDP要求q_讑֤提供一个机制用来储存简单的数据记录Q通过正常的^CӞ比如重新启动和电池更新维护系l的完整性?MIDPUC个持久数据库?RecordStore?在我们的CZ中, MIDlet打开q添加一条记录到 " MutualFundQuotes " RecordStore?正如我们的演C程序,能添加到 RecordStore中的唯一一U类型的记录是字节数l?相同?RecordStore是一个资源,它可以通过套g׃n?Ҏ MIDP规范Q??MIDlet从^C删除后,RecordStore也会被从q_中删除?
PDA?br />
Palm公司是开发PDA表规范的领头人, q个表也是完善了 CLDCQ在相当长的一D|间内Q它都将?kjavacȝ序包的替代品?Java规范q个 profile臛_应当提供两个核心功能片段Q?一个用L面显C工具包Q适合?"有限的尺寸和深度昄 "和一个持久数据存储器机制?昄工具包应该是抽象H口工具包的一个子集, 而持久机制将为应用程序、数据、配|?环境信息提供单的数据存储?br />
Foundation?br />
下面三种表不是非常常见, q三U简表的职责都是Z完善 CDC?Personal?RMI表实际上?Foundation表的扩展?Foundation表的d是担M个基表,便于以后开发出来的提供囑Ş用户接口?|络{功能的表附着在它之上?除了用于基础表, Foundation表还提供完整|络的支持,不管有没有用图形用h口?br />
Personal?br />
在当前的规范需求下Q?Personal表提供下一?PersonalJava环境。这个简表允诺,提供互联|连接性和 Web保真度以及一个能够运?Java applets?GUI?br />
RMI?br />
回想一?CDC配置为共享的、固定网l连接信息设备提供最的 Java环境?RMI表将通过提供 Java?Java的RMI来协助提供更好的|络q接性?通过使用 J2SE ( 1.2.x或更高版本的 ) RMIQ这个简表将允许q些|络讑֤与其他系l应用程序交互操?(q个pȝ不必也运?J2ME )?br />
kjavac?br />
正如前面提到的那P kjavacL最初提供的一个供试用的c,?Palm讑֤上运行早期的 KVM和配|版本?它们被 PDA表代ѝ?kjavacL展了 CLDCq且提供一个图形用h口?Palm数据库访问,单集合类和一个三角法计算器?br />
在代码段2中,我?com.sun.kjava重写?MIDP FundTrackerE序Q让它在 Palm上工作?和前面的E序一Pq个单的E序允许用户输入一个公基金代号q从WWW上的金融报h服务商那里取回报仗?br />
kjava应用E序被称?spotlet?事实上,一个应用程序可以由很多 spotletl成Q但是在M旉只有一?spotlet可以昄?Palm屏幕上?在我们的例子中,我们创徏一个基?spotlet-- RequestFormSpotlet.javaQؓ我们的两?spotlets子类提供用户界面。代码段 2扩展了基本的 RequestFormSpotlet以便得到q储存一个报仗?RetrieveSpotlet也扩展了基本 RequestFormSpotletq允许储存的报h被取回(见图Q?
代码D?
import com.sun.kjava.*;
public final class FundSpotlet extends RequestFormSpotlet {
public static void main (String args[]) {
new FundSpotlet().draw();
}
private void draw() {
initForm();
setTitle("Fund Quote Requested");
}
public void penDown(int x, int y){
if (getExitButton().pressed(x,y)){
getGraphic().playSound(Graphics.SOUND_CONFIRMATION);
System.exit(0);
}
if (getSymField().pressed(x,y))
getSymField().setFocus();
if (getGetButton().pressed(x,y)) {
quoteRequested();
}
}
private void storeQuote (String fund, String newQuote) {
int dbType = 0x46554e44;
int dbCreator = 0x43415454;
com.sun.kjava.Database quoteDB;
try {
quoteDB = new com.sun.kjava.Database(dbType,
dbCreator, com.sun.kjava.Database.READWRITE);
if (!quoteDB .isOpen()) {
com.sun.kjava.Database.create(0, "MutualFundQuotes",
dbCreator, dbType, false);
quoteDB = new com.sun.kjava.Database(dbType,
dbCreator, com.sun.kjava.Database.READWRITE);
}
byte[] data = (fund + "#" + newQuote).getBytes();
quoteDB.addRecord(data);
quoteDB.close();
}
catch (Exception recordException) {
System.out.println("Unable to store quote and/or use
Mutual Fund Quote database.");
}
}
private void getAndDisplayQuote() {
String fundSymbol = getSymField().getText();
if (fundSymbol.length() > 0) {
String theQuote = QuoteService.getQuote(fundSymbol);
if (theQuote != null) {
storeQuote(fundSymbol, theQuote);
message(theQuote);
}
else
message("No quote. Check Symbol");
}
}
private void quoteRequested() {
message("");
getGraphic().playSound(Graphics.SOUND_STARTUP);
if ((getSymField().getText().length() > 0)) {
getAndDisplayQuote();
} else
{
message("Symbol required!");
}
}
}
?RequestFormSpotletE序中,cM?MIDP中的 Display对象Q单独的 Graphics理许多 spotlet用户界面昄。它考虑C屏幕会被清除Q显C界会被徏立?不象 MIDletQ没有屏q或d对象来让我们d用户界面组Ӟ 取而代之的是按钮、文本字D늭{,直接描画?spotlet上?paint()Ҏ利用囑Ş环境从独一无二?Graphics在屏q上昄组件?br />
我们的MIDPE序?QuoteServicecȝ大部分可以重C用?因ؓ kjava没有?MIDP中HttpConnectionq样特定的连接器界面Q所以我们必d用更多标准的一般的q接器结构表单获?HTTP链接?Z做到q一点,使用代码D?3中的代码替换 getQuotePage()Ҏ。注意注意?ConnectorQ就像在 MIDP中我们?HttpConnection一栗?br />
代码D?
private static String getQuotePage(String symbolString) {
StringBuffer quotePage = new StringBuffer();
int ch;
try {
InputStream in = Connector.openInputStream (
"testhttp://someurl/some_application?page=++&mode=fund&symbol="+
symbolString);
while ((ch = in.read()) > 0) {
quotePage.append((char)ch);
}
in.close();
return quotePage.toString();
} catch (IOException ex) {
System.out.println("Exception reading quote from
HTTP Connection");
return null;
}
}
Palm讑֤q泛利用数据库, 你的 Palm中的通讯ѝ备忘录和记事本应用E序都与数据库有兟?kjavaE序包提供了一个非常小?Databasec,不仅可以创徏q保持应用程序数据,而且可以讉K现有的数据库?如果你熟?Palm数据库,你可能会?kjava DatabasecL供的功能和信息感到失望?然而,请再ơ记住, kjava只是一个演C的版本?br />
在我们的例子中,我们?spotlet讉K一?Palm数据?(如果不存在的话,则创Z个新的数据库Q来储存公基金报仗每?Palm数据库都必须有名字、创?ID (一?Palm登记的唯一的标识号 ) 和一个指定到某个单独应用E序的类型号?试图打开数据库要通过试创徏一个带?ID信息的数据库实例来实现?p MIDP RecordStoreQ记录被dq?kjava数据库,通过把一个字节数l当成记录添加到数据库中的Ş式?br />
W五?再谈谈一些J2ME规范
?J2ME内还有很多子规范Q?J2ME的重要的部分如下QPersonalJava、K虚拟?(KVM)、Java嵌入服务器以?PersonalJava的两个扩展规范: JavaPhone?JavaTV应用E序接口?你可以想象, JavaPhone是一个定位于无线甉|能电话和互联|络可视电话的应用程序接口,?JavaTV则满x盒市场的需求?br />
下面我想详细的谈一谈以上的规范Q?br />
1、PersonalJava
PersonalJava应用E序环境目标?Webq接消费讑֤----常常执行来自|络的小应用E序。问题是 PersonalJava如何适合 J2ME的配|和表方案?{案?PersonalJava被包容q?Connected Device Configuration中,最l将被定义ؓ Personal表,卛_面所谈到的Personal表?br />
另一斚wQ有一D|间将有两?Java应用E序接口为嵌入开发世界服务: PersonalJava?EmbeddedJava?PersonalJava偎依?J2ME大伞之下Q?可ؓ什?EmbeddedJava不呢Q?EmbeddedJava不和 PersonalJava同在 J2ME内,是因为在 PersonalJava?EmbeddedJava应用E序之间有一个基本的差别?PersonalJava应用E序期望q接到某cȝl中下蝲q执行小应用E序?按照q种观点Q?PersonalJava讑֤是一般用途的消费讑֤Q?它们的能力可以被扩展?br />
相比之下Q?EmbeddedJava讑֤则惨了点?它们执行的功能都非常具体的,基本没有必要提供下蝲新的代码?EmbeddedJava讑֤的能力?HenceQ?PersonalJava讑֤使用可扩?Java应用E序接口Q?而EmbeddedJava讑֤则没有,因ؓ没有必要使用?br />
PersonalJava可以以两UŞ式得刎ͼ 由原码Ş式的Q提供给那些ҎPersonalJavaUL到其他设备感兴趣的开发者,那些已经?PersonalJavaUL到某个具体的操作pȝ和处理机的组l提供二q制形式?PersonalJava环境。有兴趣探烦 PersonalJava的开发者如果没有二q制q_也可以?PersonalJava模拟环境 ( PJEE )?q个模拟器运行于 Solaris/SPARC?WindowsQƈ且在许多配置中可用?q些多种多样的配|基于?look and feel”和cd支持 (环境是否提供 PersonalJava规范中规定的最低限度的或最大的cdQ。PJEE包括cLӞ一个应用程?launcher和一?appletviewer (两者都是ؓ了调试功能ƈ使其最优化Q和其它的附带的文g (例如字体叙述文g)?br />
J2ME家族的另一位成?JavaCheck实用E序Q提供了 PersonalJava的补充支持?你把应用E序传过 JavaCheckQ它告诉你你的应用E序在一?PersonalJava环境中能否顺利地执行?JavaCheck查类之间的依赖关p,如果应用E序调用了一个在 PersonalJava不可用的应用E序接口Q它׃l出一个警报信受?(据我所知,目前有两UJavaCheck的版本可用,一个是用于?PersonalJava 1.0版应用程序,另一个用于检?1.1.x版程序?当前?PersonalJava应用E序接口规范?1.2Q用于这一版本?JavaCheckq没有?读者请去Sun相关|站ȝ看( http : file://java.sun.com/products/personaljavaQ?br />
2、KVM
前面我也说过QKVM是用?J2MEq_最的虚拟机,q且是用于CLDC配置的虚拟机。可是J2ME应用E序q不一定非要?KVMQJ2ME技术可以用Q何虚拟机Q不q至应当有 KVMq样的功能?br />
Z满ZKVM的设备一般只有狭的内存I间和有限的处理能力的事实, KVM使用 C~写 (它不是现有的VM改进了的以后的品)?此外Q?KVM是模块化的, 也就是说Q它是由模块构徏的,当某个模块实C预先讑֮的目标后Q就可以很容易地把这一模块卸蝲?可选的某块包括Q?大的数据cd ( long?float?double )Q多l数l、类文g验证{?br />
KVM的本地界面以M性ؓ原则构徏Q所以在KVM中Q务切换不依赖g产生的记时器中断Q因此在q种意思上来说不是抢先式。Q务切换发生在虚拟机执行了一个预讄L字节码之后?q且Q?KVM的无用单元收集利用一个标记清扫(mark and sweep)法来实现无用单元释放?因此Q对象引用是直接的,像标准 Java一栗?
当然Q除了虚拟机以外q有许多可用的执行环境,在小型设备中Q虚拟机必须要么被扩展,要么在附加工具协助下提供一个更加完整的q行期环境,正是q个原因Q?KVM需要附带的工具Q比如说Q?JavaCodeCompact工具提供了预链接和预加蝲c, 允许Javac被直接地链接进虚拟Z?Q设备上所有的应用E序使用的类 can直接地嵌入虚拟机。)
KVM一个可选的附g是 Java Application Manager ( Java应用E序理器,U?JAM )。JAM的工作就是处理下载、安装、执行和卸蝲 CLDC讑֤上的应用E序的细节问题,因ؓ资源有限Q在CLDC讑֤上有可能不存在这些功能。JAM也处理更新安装应用程序的操作?如果更新q程p|Q它甚至可以重新使用旧的应用E序?)
3、Java Embedded ServerQJava嵌入服务器)
Java Embedded ServerQ?Java嵌入服务器,U?JESQ,?PersonalJava基础上徏立,是一个用于嵌入式|络讑֤的运行期环境。ؓ了理?JESQ你必须理解两个核心概念Q服务和服务I间l构。后者是前者的容器。服务程序是q行于一?JES服务器上的组件化E序Q服务空间结构是为服务程序提供生命周?支持的环境?br />
技术上_服务E序是界面的实现Q事实上Q它是一个实现特定活动的Javac集合。比如说Q假如把 JES配置Z个家庭的气候控制系l的服务器,可以把从模数转换器读到的温度数据放进一个数据组件程序中。我可以称q个lg为ReadThermostats服务E序?
?JES的领域,服务的封装媒介称?bundle。简单地_bundle是一个带有特D内容的JAR文g。服务程序和bundle之间有一对一关系Q一个bundle带有一个服务程序。服务程序和 bundle之间有一对一关系Q一?bundle带有一个服务程序。可q也不一定,一?bundle可以讄多个服务E序索引 (注意Q?JES提供的所有的核心服务Q每?bundle中只有一?)?
正如前面提到的那P服务I间的一工作就是管理服务程序的生命周期Q这个工作的很大的部分包括解x务隶属关pRbundle内容的一个重要的部分是bundle服务的依赖信息。所以,当服务空间打开一个bundle安装它的服务Ӟ服务I间可以确定外部需要什么服务。而且Q一个服务的依赖关系q不是静止不变的Q它们可以随某些事g改变。比如说当服务程序更新时的变化就是一个很好的例子。一个服务的新的版本可以d或去除依赖关pR服务空间跟tƈ解决q样的动态依赖关pR如果服务空间处理所有服务程序的生命周期Q这暗CZ服务I间被赋予知晓一切的能力Q那是_它能够推论结构、依赖、安装的l微差别{所有它负责的服务。服务空间通过?bundle内伴随服务的 Java代码模块处理一些Q务,q些模块被称?wizardQ向|。JES向导是根据它们完成的d命名的:
Dependencies -向导告诉调用者一个bundle依赖关系是什么?
Installer-向导处理bundle中服务的安装和删除操作?
Activator -向导知道如何启动和终止服务?
Updater -向导控g更新bundle中的服务?更新向导不仅知道更新一个服务,而且知道在何时和什么情况下更新服务?)
About -q个向导Q就像它名称意味的那Pq回关于 bundle内容的信息?
Dispatcher -q是一U元向导Qmeta-wizardQ。服务空间调用dispatcher向导定位一个bundle的其他向对{?
当一?JES服务器启动的时候,服务I间q不是完全没有启动服务。JES定义一l核心服?可选)Q这些都是Q?JES服务器的l成部分。这些核心服务包含:
HTTP服务
日志 -记录错误和事件日?
日期 -_到秒的日?旉服务
q接理?-提供|络服务和Socketl定Q也处理q接接收?
U程理?-理服务器提供的U程。thread理器支持线E池q允许有效用线E上界的规范?
计划E序 -提供未来的事件计划安?(可用于告诉服务器某某动作必须在某某事件发?)
RMI
SNMP
控制?-提供q程理服务器功?
Z HTTP的远E应用程序接口实?
Z RMI的远E应用程序接口实?
如果你把服务I间l构当成 JavaBean中的容器的话Q?JES变得容易理解了。在q种cL关系中,服务E序q?JavaBean。那么,正象lg容器提供一个环境供 JavaBeans实例化、运行一P服务I间是以实例化的服务的聚集地。服务空间管理安装、实例化、执行、终止以及卸载服务;它也提供应用E序接口供服务交互作用?
一、J2ME中需要的Java基础知识
现在有大部分人,都是从零开始学J2ME的,学习J2ME的时候,L从Java基础开始学习,而且现在讲Java基础的书c中都是以J2SE来讲基础Q这q学习造成了一些不必要的麻烦,下面J2ME中用到的和不需要的Java基础知识做一个简单的说明?br /> J2ME中用到的Java基础知识Q?br /> 1、Java语法基础Q包括基本数据类型、关键字、运符{等
2、面向对象的思想Q类和对象的概念Q承和多态等{?br /> 3、异常处?br /> 4、多U程
J2ME中没有用到的Java基础知识Q?br /> 1、JDK中javac和java命o的?br /> 2、Java基础中的很多cdJ2ME中没有,或者类中的Ҏ做了大量的精。所以徏议在J2ME中熟悉类库?br /> 3、Applet、AWT、Swingq些知识在J2ME中根本用不到?br /> 单说q么多,希望学J2ME的朋友们能少C些弯路,不之处希望大家U极指正和补充?/p>
二、J2ME中暂时无法完成的功能
列一些J2ME中暂时无法完成的功能Q希望大家能U极补充Q?br /> 1、在手机中不更改代码实现ULQ主要指游戏?br /> 2、动态修Ҏ钮文字?br /> 3、在Canvas上接受中文输入?br /> 4、操作本地资源、例如地址本、已收短信息{?br /> 5、制作破坏性的手机病毒?br /> 6、其他等待大家来补充?/p>
三、J2ME的跨q_?br /> J2ME技术源于JavaQ所以也hJVM的优势,可以在支持Java的^Cq行ULQ但是现在的J2ME技术在跨^C却做的很p糕Q我们来单看一下原因:
1、手机的屏幕寸不一Q?br /> q个主要在界面制作上。如果你使用的是高用户界面Q比如你做的是应用开发或者用L陆、用h册这L通用功能Ӟ一般没有什么问题?br /> 如果你用的是低U用L面,比如你做的是游戏Q那么你需要考虑q个问题了?br /> 2、厂商的扩展API不统一Q?br /> 例如Nokia的扩展APIcdUIpdQ在别的手机上或者没有实玎ͼ或者包名不同等{?br /> 3、手机^C实现的bugQ?br /> 例如Nokia?650在实现双~冲上有bugQ那么在q种机型上运行的软g׃能用双~冲。其他NOKIA上的一些bugQ可以参看:http://blog.csdn.net/Mailbomb/archive/2005/03/24/329123.aspx
4、手机性能问题?br /> 不同手机的可用内存、最大jar文g都有要求Q例如Nokia S40的大部分手机支持的最大jar文g?4KQ最大可用内容ؓ210K?br /> 所以现在的手机软gQ特别是游戏都提供支持的机型列表Q也才有了手机游戏移植h员的存在?/p>
四、学习J2ME可以从事的工作种c?br /> 现在J2ME技术可以说相当的火_q里介绍一些学好了J2ME之后可以从事的工作的U类Q?br /> 1、J2ME游戏开发h?br /> Ҏ游戏{划或者文档要求,在某U特定的机型(以Nokia S40或S60居多)开发游戏程序?br /> q是现在大部分J2MEE序员从事的工作?br /> 需要熟l掌握:高用户界面、低U用L面、线E,如果是网l游戏,q需要熟l网l编E?br /> 2、J2ME应用开发h?br /> 现在的移动应用还不是很多Q但是还是出C一些,特别是移动定位以及移动商务相关的内容?br /> 需要熟l掌握:高用户界面、线E和|络~程?br /> 3、J2ME游戏UL人员
参照源代码,可以在一个^C可以q行的游戏移植到其他q_上去。例如将Nokia S40的游戏移植到S60上,或者烦qT618{等?br /> 主要是控制屏q坐标,有些可能需要替换一些API?br /> 需要熟悉各q_之间的差异以及相关的技术参敎ͼ比如屏幕大小、最大jar文g寸{等?/p>
五、J2MEE序设计的几个原?br /> 1、用面向对象编E?br /> 虽然使用面向q程~程可以减小文g的尺寸,但是Z以后l护的方便和利于扩展Q还是要使用面向对象~程?br /> 2、用MVC模式
模型、界面和控制分离。现在很多的E序三者合一Q但是如果你做的E序比较大的话,q是你进行分R?br /> 3、自动存储用戯?br /> 使用RMS来存储用L信息Q例如存储用户上ơ输入的用户名、密码、用户对于系l的讑֮{,q样不仅可以减少用户的输入,而且对用户友好。很多程序甚臛_了自动登陆等?br /> 4、一些系l设|允许用户关闭。如背景音乐、背景灯昄{?br /> 5、将低用户界面的绘制动作放在一个独立的U程里面厅R?br /> 6、在需要大量时间才能完成的工作Ӟl用户一个等待界面?/p>
六、从模拟器到真机试
对于J2ME开发者来_模拟器给我们带来了很多方便,比如可以在模拟器中调试程序以及很方便的察看程序的效果Q但是模拟器也给我们带来了一些问题,比如模拟器实现的bug{等Q所以进行真机测试是必须的?br /> 1、ؓ什么要q行真机试Q?br /> 因ؓ模拟器程序可能存在bugQ以及真机的性能有限Q所以必进行真机测试?br /> 2、如何将E序传输到机器中Q?br /> 程序传输到机器中有如下方式Q?br /> a) OTA下蝲
b) 使用数据U传?br /> c) U外传输
d) 蓝牙
你可以根据条Ӟ选择合适的方式?br /> 3?真机试主要什么?
真机试的内容很多,主要试以下几个斚wQ?br /> a) E序的功?br /> b) E序的操作性,是否易操?br /> c) E序的大?比如Nokia S40pd的手机大部分接受的最大文件尺ؓ64K
d) E序q行速度Q速度是否可以忍受?/p>
七、从WTK到厂商SDK
对于J2ME爱好者来_基本上大安是从SUN的WTK(J2ME Wireless Toolkit)开始的Q但是对于实际应用来_仅仅使用WTK是远q不够的Q所以在学习q程中,必须完成从WTK到SDK的跨?br /> 1、厂商SDK的下载地址Q?br /> http://blog.csdn.net/Mailbomb/archive/2005/01/01/236606.aspx
2、厂商SDK和WTK有什么不同?
厂商SDK最单的理解是在WTK的基上增加了自己的模拟器和自q扩展API?br /> 也就是说Q你在用厂商的SDKӞ可以使用厂商的扩展类库,例如Nokia的UIcdQ和厂商自己的模拟器而已?br /> 每个厂商的扩展API都不多,而且不尽相同?br /> 3、如何用?
有些厂商SDK的用都和WTK相同Q例如SamSung?br /> Nokia提供了独立的界面来开发,但是q个界面在实际开发中使用不多?br /> 4、厂商SDK的问?br /> 厂商SDK实现q程中,有一些bugQ而且和真机实C一致。例如NOKIA的xN题等{?/p>
八、在J2ME中获得手机IMEI的方?br /> IMEI是Internation mobile entity identification的简Uͼ在手Z输入*#06#可以昄该数字,长度?5位,全球唯一Q永q不会冲H,所以可以作别用L一个标志?br /> 下面是在J2ME中获得IMEI的方法:
1、MOTOpd的手机可以通过dpȝ的IMEI属性获得,代码如下Q?br /> String imei = System.getProperty("IMEI");
2、SIEMENSpd的手机可以通过dpȝ的com.siemens.IMEI属性获得,代码如下Q?br /> String imei = System.getProperty("com.siemens.IMEI");
九、J2ME|络q接中显C问题的解决办法
在网l编E中Q有些时候会出现一些在没有接收到网l数据就昄界面的,造成界面昄不符合要求(例如公告昄Q会先显C公告的背景囄再显C公告信息)Q这里提一个简单的解决办法l大Ӟ
解决q种情况的方法分成三个步骤:
1、在需要显C的界面中,调用发送网l数据的Ҏ。每ơ显C时调用该构造方法,不调用Display的setCurrentҎ昄?br /> 2、显C等待界?例如q度条等)Q给用户提示Q在q行|络q接?br /> 3、在处理|络反馈的数据完以后Q调用Display的setCurrentҎ昄昄当前界面?/p>
十、增强J2ME的String能力——分割字W串
从JDK1.4以后QStringcM新增了splitҎ来实现字W串的分Ԍ但是在J2ME中却没有该方?MIDP2.0中也没有实现)Q但是在实际使用q程中,有些时候的要用到q种操作Q这里将我以前实现的一D代码和大家׃nQ?br />/**
* 分割字符Ԍ原理Q检字W串中的分割字符Ԍ然后取子?br />* @param original 需要分割的字符?br />* @paran regex 分割字符?br />* @return 分割后生成的字符串数l?br />*/
private static String[] split(String original,String regex)
{
//取子串的起始位置
int startIndex = 0;
//结果数据先攑օVector?br /> Vector v = new Vector();
//q回的结果字W串数组
String[] str = null;
//存储取子串时起始位置
int index = 0;
//获得匚w子串的位|?br /> startIndex = original.indexOf(regex);
//System.out.println("0" + startIndex);
//如果起始字符串的位置于字符串的长度Q则证明没有取到字符串末?br /> //-1代表取到了末?br /> while(startIndex < original.length() && startIndex != -1)
{
String temp = original.substring(index,startIndex);
System.out.println(" " + startIndex);
//取子?br /> v.addElement(temp);
//讄取子串的起始位置
index = startIndex + regex.length();
//获得匚w子串的位|?br /> startIndex = original.indexOf(regex,startIndex + regex.length());
}
//取结束的子串
v.addElement(original.substring(index + 1 - regex.length()));
//Vector对象转换成数l?br /> str = new String[v.size()];
for(int i=0;i
{
str[i] = (String)v.elementAt(i);
}
//q回生成的数l?br /> return str;
}
十一、J2ME在低U用L面上分行昄文字
在J2ME的低U用L面开发中Q经怼遇到需要在Canvas上显C大量的文字Q例如关于界面、游戏说明、游戏公告等信息。如果在设计Ӟ文字的内容和长度都固定Q既不利于修改也不利于维护。下面介l一个简单的ҎQ实C个简单、可l护性强的方式?br /> 实现ҎQ?br /> 1、将需要显C的所有信息做成一个字W串?br /> 2、编写一个将该字W串按照要求转换为字W串数组的方法?br /> 3、将转换后的数组以@环的方式昄在Canvas上?br /> 通过q样三个步骤Q则修改昄的信息时Q只需要修改包含显CZ息的字符串即可,自己书写的方法可以按照以前的标准重新分割新的字符丌Ӏ如果需要修Ҏ行显C的字符个数Q则只需要修改自׃写的Ҏ卛_?br /> 通过q样一U实现方式,可以很方便的实现昄一些比较长的文本信息,即是可变长度的字符串也没有问题?/p>
十二、J2ME中用记录存储系l?RMS)存储信息
在MIDP中,没有文g的概念,所以永久存储一般只能依靠记录存储系l实玎ͼ关于记录存储pȝ的简介,可以参看教程Q?a >http://www-900.ibm.com/developerWorks/cn/java/j-wi-rms/index.shtml
下面是一些记录存储系l的常用~码介绍Q?br /> 1、打开记录集:
打开记录集用RecordStore里面的静态方法openRecordStoreQ示例代码如下:
RecordStore rs = RecordStore.openRecordStore(“username?true);
q样打开了一个名UCؓrs的记录集Q其中username录集的名Uͼ该名U可以根据需要来取,W二个参C表是否则没有时创建新的记录集Qtrue代表在该记录集不存在Ӟ创徏新的记录集,false代表不创建?br /> 如果在打开记录集时Q该记录集不存在Q则抛出RecordStoreNotFoundException异常Q所以检记录集是否已创建可以用该异常?br /> 注意Q记录集打开以后记得关闭?br /> 2、向记录集中写入数据
2.1增加数据
向已l打开的记录集中添加数据,需要用addRecordҎQ示例代码:
byte[] bytes = {1,2,3};
int id = rs. addRecord(bytes,0,bytes.length);
该代码将字节数组bytes的全部内容写入到记录集中Q该Ҏ的返回gؓ该信息的idQ注意:id?开始,而不是从0开始?br /> 你可以@环用该Ҏ向记录集中写入多条数据?br /> 2.2修改数据
修改已经存在的记录集中指定id的数据,需要用setRecordҎQ示例代码:
byte[] bytes = {1,2,3};
rs. setRecord(1,bytes,0,bytes.length);
以上代码的作用是字节数lbytes的全部内容写入到id?的记录集rs中?br /> 该操作会覆盖已有的数据?br /> 说明Q有些时候,你需要将信息写入到记录集中的W一条记录中Q则可以l合以上两个ҎQ则W一ơ时向记录集增加数据Q以后来q行修改?br /> 3、从记录集中d数据
从记录集中读取已有数据,需要用getRecordҎQ示例代码:
byte[] bytes = rs. getRecord(1);
该代码从记录集rs中读取第一条数据,读取到的数据放在bytes数组中?br /> 在读取数据时Q可以获得记录集中id的个敎ͼ可以使用getNumRecordsҎ获得
l合代码为:
int number = rs. getNumRecords();
int id = 1;
if(id >0 && id < number)
{
byte[] bytes = rs. getRecord(1);
}
4、从记录集中删除记录
从记录集中删除记录的Ҏ有两U:逻辑删除和物理删除?br /> 逻辑删除是指l删除的记录打标记?br /> 物理删除是指从物理上删除该记录,但是该记录的id不能被重用,也就是说该id不会被l用。例如一个记录集中有5个记录,假设你删除了id?的数据,则剩余记录的id依然????。这l便历带来了一定的ȝ?br /> 5、便历记录集
便历记录集,卌问记录集中的所有数据,有两个方法,详见Q?br />http://gceclub.sun.com.cn/NASApp/sme/controller/teclist?tid=0103
6、其他操?br /> 删除记录?br /> 删除记录集不同于删除记录Q需要用deleteRecordStoreҎQ示例代码:
RecordStore. deleteRecordStore(“username?;
该代码删除名UCؓusername的记录集?/p>
十三、J2ME加密数据的一个第三方开源免费类库介l?br /> 在J2ME~程中,l常遇到一些数据在存储或者传输时需要加密,下面介绍一个第三方的加密类库的一些资料:
加密cd的官方主:http://www.bouncycastle.org/
介绍的文章:
中文Q?a >http://18900.motorola.com/ewa_portal/develope/jc_j2messl_5_1.jsp
英文Q?a >http://www.javaworld.com/javaworld/jw-12-2002/jw-1220-wireless.html
该文章的源代码包含用的一些方法?br /> 备注Q因cd提供的功能比较强大,所以类库的寸比较大,最后在发布旉要将cd中不需要的cd?/p>
十四、如何播攑֣?br /> 在J2ME中,处理声音需要用到Mobile Media API(MMAPI)Q该包是MIDP1.0的可选包Q在MIDP2.0中已l包含了q个包。所以如果你使用MIDP1.0的话Q请认你的q行环境是否支持?br /> 一般手机支持的声音文g格式为wav、mid和mpg{。具体请查阅你的手机说明文档?br /> 在声韛_理中Q有很多处理的方式,q里说一下最常用的情况,播放JAR文g中的wav文g?br /> 播放声音文g的流E:
1、按照一定的格式d声音文g?br /> 播放JAR文g中的声音文g一般是声x件处理成的形式。示例代码:
InputStream is = this.getClass().getResourceAsStream("/Autorun.wav");
其中Autorun.wav文g位于JAR文g的根目录下,如果位于别的目录Q需要加上目录名Uͼ?res /Autorun.wav?br /> 2、将d到的内容传递给播放器?br /> 流信息传递给播放器,播放器按照一定的格式来进行解码操作,CZ代码Q?br /> Player player = Manager.createPlayer(is,"audio/x-wav");
其中W一个参Cؓ对象,W二个参Cؓ声音文g的格式?br /> 3、播攑֣韟?br /> 使用Player对象的startҎQ可以将声音播放出来Q示例代码:
player.start()Q?br /> 在播攑֣x也可以设定声x攄ơ数Q可以用PlayercM的setLoopCountҎ来实玎ͼ具体可查阅API文档?br /> 下面是在NOKIA S60模拟器中试通过。代码如下:
package sound;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import java.io.*;
public class SoundMIDlet extends MIDlet
{
private Player player = null;
/** Constructor */
public SoundMIDlet()
{
try
{
InputStream is = this.getClass().getResourceAsStream("/Autorun.wav");
player = Manager.createPlayer(is,"audio/x-wav");
}
catch(IOException e)
{
System.out.println("1:" + e);
}
catch(MediaException e)
{
System.out.println("2:" + e);
}
catch(Exception e)
{
System.out.println("3:" + e);
}
}
/** Main method */
public void startApp()
{
if(player != null)
{
try
{
player.start();
}
catch(MediaException e)
{
System.out.println("4:" + e);
}
}
}
/** Handle pausing the MIDlet */
public void pauseApp()
{
}
/** Handle destroying the MIDlet */
public void destroyApp(boolean unconditional)
{
}
}
十五、J2ME 3D~程的一些资?br /> 随着J2ME技术的发展Q以及硬仉度的提升,3D游戏E序慢慢的变成LQ最q想学习q一块的~程Q所以收集了一些资料,和大家一起分享:
1、JSR184
JSR184是Nokia公司赯的一个关?D API的规范,下蝲地址为:
http://www.forum.nokia.com/main/1,,1_0_10,00.html#jsr184
2、Nokia?D~程资料
http://www.forum.nokia.com/main/1,6566,21,00.html
3?D引擎
一个简单的开放源代码?D游戏引擎
http://www.j2me.com.cn/Soft_Show.asp?SoftID=19
国内一个合作开?D引擎的项目:
http://gceclub.sun.com.cn/NASApp/sme/jive/thread.jsp?forum=11&thread=8593
4、一?D游戏产品
http://games.sina.com.cn/newgames/2004/04/040217696.shtml
5、支?D的开发工?br /> 当前一些高端的手机支持3D开发,支持3D开发的开发工具中Q通用的有SUN的J2MEWTK2.2。专用的是厂商提高的支持JSR184的SDK?/p>
十六?D~程——第一?DE序
参考WTK2.2提供的demoQ完成了W一?DE序Q虽然很单,而且有些问题q不是很清楚Q还是把代码׃n出来和愿意学习J2ME 3D~程的朋友一起学习?br /> 关于代码的编译和q行说明如下Q?br /> 1、以下代码在J2ME WTK2.2下面~译通过?br /> 2、代码分Z个文ӞFirst3DCanvas.java和First3DMIDlet.java?br /> 3、用J2ME WTK2.2建立新的工程Q主MIDletcMؓQfirst3d. First3DMIDlet
4、将代码保存在你的工E目录下的first3d目录下?br /> 5、将J2ME WTK安装目录下的apps\Demo3D\res\com\superscape\m3g\wtksamples\retainedmode\content目录中的swerve.m3g文g复制C的工E目录下的res目录下?br /> 6、你的工E徏立后Q设|工E,通过WTK界面中的“设|”按钮打开讄H口Q在“API选择”中Q设|“目标^台”ؓQ自定义Q“简档”ؓ“MIDP2.0”;“配|”ؓ“CLDC1.1”;选中“Mobile 3D Graphics for J2ME(JSR184)”?br /> 7、这样你可以编译和q行以下代码了?br /> 源代码如下:
// First3DMIDlet.java
package first3d;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class First3DMIDlet extends MIDlet
{
private First3DCanvas displayable = new First3DCanvas();
public void startApp()
{
Display.getDisplay(this).setCurrent(displayable);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
}
// First3Dcanvas.java
package first3d;
import javax.microedition.lcdui.*;
import javax.microedition.m3g.*;
import java.util.*;
/**
* W一?DE序
*/
public class First3DCanvas extends Canvas implements Runnable
{
/**World对象*/
private World myWorld = null;
/**Graphics3D对象*/
private Graphics3D g3d = Graphics3D.getInstance();
/**Camera对象*/
private Camera cam = null;
private int viewport_x;
private int viewport_y;
private int viewport_width;
private int viewport_height;
private long worldStartTime = 0;
//重绘旉
private int validity = 0;
public First3DCanvas()
{
//启动重绘界面的线E?br /> Thread thread = new Thread(this);
thread.start();
try
{
//导入3D囄
myWorld = (World) Loader.load("/swerve.m3g")[0];
viewport_x = 0;
viewport_y = 0;
viewport_width = getWidth();
viewport_height = getHeight();
cam = myWorld.getActiveCamera();
//讄cam对象
float[] params = new float[4];
int type = cam.getProjection(params);
if (type != Camera.GENERIC)
{
//calculate window aspect ratio
float waspect = viewport_width / viewport_height;
if (waspect < params[1])
{
float height = viewport_width / params[1];
viewport_height = (int) height;
viewport_y = (getHeight() - viewport_height) / 2;
}
else
{
float width = viewport_height * params[1];
viewport_width = (int) width;
viewport_x = (getWidth() - viewport_width) / 2;
}
}
worldStartTime = System.currentTimeMillis();
}
catch (Exception e) {}
}
protected void paint(Graphics g)
{
//清除背景
g.setColor(0x00);
g.fillRect(0, 0, getWidth(), getHeight());
//?D对象l定
g3d.bindTarget(g);
g3d.setViewport(viewport_x, viewport_y, viewport_width, viewport_height);
long startTime = System.currentTimeMillis() - worldStartTime;
validity = myWorld.animate((int)startTime);
try
{
g3d.render(myWorld);
}
finally
{
g3d.releaseTarget();
}
}
public void run()
{
try
{
while(true)
{
//重绘囑Ş
repaint(viewport_x, viewport_y, viewport_width, viewport_height);
}
}
catch(Exception e){}
}
}
十七、在J2ME|络~程中用CMWAP代理
在中国移动提供的|络q接中,分ؓCMNET和CMWAP两种Q其中CMNET可以无限制的讉K互联|络Q资Ҏ较贵。CMWAPcM一个HTTP的代码,只能讉K支持HTTP的应用,但是资费便宜Q稳定性比较差?br /> 在实际的J2ME|络~程中,一般需要提供以CMWAP代理的方式连接网l,在J2ME中,q接的代码和直接q接有所不同Q代码如下:
HttpConnection http = (HttpConnection)Connector.open((");
http.setRequestProperty("X-Online-Host",ServerName);
例如你需要访问的地址为:http://www.test.com/login/loginServlet则上面的代码׃ؓQ?br /> HttpConnection http = (HttpConnection)Connector.open((" http.setRequestProperty("X-Online-Host"," 在实际用过E中Q只需要用实际需要访问的地址的域名或者IP来代替ServerNameQ例如示例中的“www.test.com”,使用后箋的地址cM替代码中的urlQ例如示例中的“login/loginServlet”,可以实际的使用CMWAP代理来进行连接了?/p>
十八、J2ME中的旉处理全攻?br /> 旉处理在程序开发中相当常见Q下面对于时间处理做一个简单的说明?br /> 一、时间的表达方式
旉在J2ME中有两种表达方式Q?br /> 1、以和GMT1970q??号午?2点和现在相差的毫U数来代?br /> q种方式适合比较两个旉之间的差倹{?br /> 2、以对象的Ş式来表达
二、时间处理的相关c?br /> 旉处理在J2ME中涉及三个类Q?br /> 1、Systemc?br /> long time = System. currentTimeMillis();
使用该方法可以获得当前时_旉的表达方式ؓ上面提到的第一U?br /> 2、Datec?br /> Date date = new Date();
获得当前旉Q用对象的形式来进行表达?br /> 3、Calendarc?br /> Calendar calendar = Calendar. getInstance();
三、时间处理的具体操作
1、以上三U表达方式的转换Q?br /> a)Systemc获得的旉转换为Date对象
Date date = new Date(System. currentTimeMillis());
b)Datecd的对象{换ؓCalendarcd的对?br /> Calendar calendar = Calendar. getInstance();
Date date = new Date();
calendar.setTime(date);
2、用Calendar完成一些日期操作:
Calendar是时间处理中最常用也是功能最强大的类Q可以用它来获得某个旉的日期、星期几{信息?br /> 获得日期Q?br /> Calendar calendar = Calendar. getInstance();
…?br /> int day = calendar.get(Calendar. DATE);
获得日期、年份、星期的操作和这个类伹{?br /> 需要注意的是:Calendar中表C月份的数字和实际相?Q即1月用数字0表示Q?月用数字1表示Q…?2月用数字11表示?/p>
十九、J2ME中随机数字处理全ȝ
在程序中生成随机数字Q用处比较,如h工智能领域等{,q里对于在J2ME中生成随机数的操作进行一个简单的整理Q希望对大家能有帮助?br /> J2ME和J2SE不同Q不能用Mathcȝrandom来生成随机数字,只能使用java.util包的RandomcL生成随机数字?br /> 1、创建Randomcd的对象:
Random random = new Random();
Random random = new Random(10010010);
以上两种是创建Random对象的方式,W一U用默认构造方法,和以下的代码作用完全{hQ?br /> Random random = new Random(System. currentTimeMillis());
相当与用当前时间作为种子数字来q行创徏?br /> W二U方式通过自己来指定种子数字来q行创徏?br /> 大家可以Ҏ需要用以上两U方式的MU?br /> 2、生成随机数字:
创徏好了随机对象以后Q我们就可以来生成随机数字了Q?br /> 生成随机整数Q?br /> int k = random.nextInt();
生成随机长整敎ͼ
long l = random.nextLong();
3、生成指定范围的数字Q?br /> 例如生成0-10之间的随机数字:
int k = random.nextInt();
int j = Math.abs(k % 10);
首先生成一个随机整数kQ然后用k?0取余Q最后用MathcȝabsҎ取绝对|获得0-10之间的随机数字?br /> 获得0-15之间的随机数Q类|
int k = random.nextInt();
int j = Math.abs(k % 15);
获得10-20之间的随机数字:
int k = random.nextInt();
int j = Math.abs(k % 10) + 10;
二十、在J2ME手机~程中用字?br /> 在J2ME手机~程中,可以通过使用字体cZ—Font在低U用L面中Q获得更好的表现效果Q那么如何用FontcdQ?br /> 首先Q由于手备的限制Q手Z支持的字体类型很有限Q所以在J2ME中只能用手机支持的默认字体来构造Fontcd象。下面是创徏Fontcȝ对象时用的ҎQ?br /> getFont(int face,int style,int size);
例如Q?br /> Font font = Font.getFont(Font.FACE_SYSTEM,Font.STYLE_BOLD,Font. SIZE_MEDIUM);
无论哪一个参敎ͼ都只能用系l设|的数|q些数值具体的大小在不同的手机上可能不同。下面对于其中的三个参数的取值做详细的介l:
face参数指字体的外观Q其的取|
FACE_MONOSPACE——等宽字?br /> FACE_PROPORTIONAL——均衡字?br /> FACE_SYSTEM——系l字?br /> style参数指字体的样式Q其的取|
STYLE_BOLD——粗?br /> STYLE_ITALIC——斜?br /> STYLE_PLAIN——普?br /> STYLE_UNDERLINED——下划线
STYLE_BOLD | STYLE_ITALIC——粗斜体
STYLE_UNDERLINED | STYLE_BOLD——带下划U粗?br /> STYLE_UNDERLINED | STYLE_ITALIC——带下划U斜?br /> STYLE_UNDERLINED | STYLE_ITALIC | STYLE_BOLD——带下划U的_斜?br /> size参数指字体的大小Q其的取|
SIZE_SMALL——小
SIZE_MEDIUM——中
SIZE_LARGE——大
通过上面的参数的|可以l合Z需要的字体对象?br /> 下面是一些常用的字体操作Q?br /> 1. 获得pȝ的默认字体:
Font font = Font.getDefaultFont();
2. 在panitҎ内部Q假设Graphics参数的名UCؓgQ则获得当前字体的方法是Q?br /> Font font = g.getFont();
3. 在panitҎ内部Q假设Graphics参数的名UCؓgQ则讄当前字体的方法是Q?br /> g.setFont(font);
其中fontZ构造好的字体对象?br /> 4. 在MIDP2.0中,List可以讄每行的字体格式,Ҏ是:
list.setFont(0,font);
则上面的代码是将list中的W一行设|ؓfontcd的字体?/p>
二十一、在J2ME手机E序开发中使用颜色
在J2ME手机开发过E中Q需要经常用到颜色来q行l制Q增强程序的表现效果Q下面就介绍一下如何用颜艌Ӏ?br /> ׃J2ME技术比较简单,所以没有实C门的颜色c,而只是用RGB的概忉|代表颜色。这里简单介l一下RGB的概念,颜色是由U?Red)、绿(Green)、蓝(Blue)三原色组成的Q所以可以用这三个颜色的组合来代表一U具体的颜色Q其中R、G、B的每个数值都位于0-255之间。在表达颜色的时候,卛_以用三个数字来表达Q也可以使用一个格式如0X00RRGGBBq样格式的十六进制来表达Q下面是常见颜色的表辑Ş式:
U色Q?255,0,0)?x00FF0000
l色Q?0,255,0)?x0000FF00
蓝色Q?255,255,255)?x00FFFFFF
其他颜色也可以通过上面的方式组合出来?br /> 知道了颜色的表达方式以后Q下面来介绍一下如何在J2MEE序中用颜Ԍ涉及的方法均在GraphicscMQ有以下几个Q?br /> 1.getColor()Q?br /> 获得当前使用的颜Ԍq回值是0x00RRGGBB格式的数字。例如:
int color = g.getColor();
其中g为Graphicscd的对象?br /> 2.setColor(int RGB)Q?br /> 讄使用的颜艌Ӏ例如:
g.setColor(0x00ff0000);
3.setColor(int red, int green, int blue)
和上面的Ҏ作用一P例如Q?br /> g.setColor(255,0,0);
在设|了Graphics使用的颜色以后,再进行绘制的时候,可以绘制指定的颜色了?/p>
二十二、在J2ME联网应用中获得客L的手机号?br /> 在J2MEE序开发过E中Qؓ了一定的需要,l常需要来获得用户的手机号码,但是q个功能却在标准的J2MEcd中没有提供?br /> 在用中国移动的CMWAP方式q接|络Ӟ中国Ud会将用户的手机号码放在一个名UCؓx-up-calling-line-id的头信息中,可以通过d该头信息Q获得用L手机LQ具体代码如下:
String usermphone = http.getHeader("x-up-calling-line-id");
其中http是HttpConnctioncd的对象?/p>
二十三、用J2ME发送手机短信息
在程序中Q发送短信息的方式一般有三种Q?br /> 1?使用E序在网l上发送短信息Q例如各大网站的短信业务。这U方式是通过E序信息发送给q营商的|关服务器,然后通过q营商的|络发送给手机?br /> 2?在计机中,通过数据U连接到手机Q然后通过手机来发送短信息。这U方式是通过使用AT指o来实现。爱立信手机的AT指o你可以在以下地址扑ֈQ?a >http://mobilityworld.ericsson.com.cn/development/download_hit.asp
3?通过在手Zq行的程序来发送短信息。这个正是本文实现的方式?br /> 在J2ME中,如果惛_送短信息Q需要用WMA包,MIDP2.0中已l包含,MIDP1.0中可以通过厂商提供的扩展API实现Q和WMA的类库基本一栗?br /> 下面是用WMA向指定手机号码发送短信息的一个方法,很简单。当然WMA也提供了其他的方式来发送更多的内容?/p>
// SMSUtil.java
package my.util;
import javax.wireless.messaging.*;
import javax.microedition.io.*;
/**
* 发送文本短信息的方?br />*/
public class SMSUtil
{
/**
* l指定号码发送短信息
* @param content 短信息内?br /> * @param phoneNumber 手机L
* @return 发送成功返回trueQ否则返回false
*/
public static boolean send(String content,String phoneNumber)
{
//q回?br /> boolean result = true;
try
{
//地址
String address = "sms://+" + phoneNumber;
//建立q接
MessageConnection conn = (MessageConnection)Connector.open(address);
//讄短信息类型ؓ文本Q短信息有文本和二进制两U类?br /> TextMessage msg = (TextMessage)conn.newMessage(MessageConnection.TEXT_MESSAGE);
//讄信息内容
msg.setPayloadText(content);
//发?br /> conn.send(msg);
}
catch(Exception e)
{
result = false;
//未处?br /> }
return result;
}
}
二十四、用简单的J2MEE序试MIDlet的生命周?br /> 在MIDletE序学习中,生命周期是一个比较抽象的概念。其实生命周期就是一个简单的规定Q规定了MIDlet中的每个ҎQ什么时候被pȝ调用。下面是一个示例代码,在每个方法的内部都输Z条语句,可以ҎE序的输出结果来验证各方法被调用的顺序,具体代码如下Q?/p>
//文g名:LifeCircleMIDlet.java
import javax.microedition.midlet.*;
/**
* 试MIDlet的生命周?br />*/
public class LifeCircleMIDlet extends MIDlet
{
/**
* 默认构造方?br /> */
public LifeCircleMIDlet()
{
System.out.println("默认构造方?);
}
/**
* 启动Ҏ
*/
public void startApp()
{
System.out.println("startAppҎ");
}
/**
* 暂停Ҏ
*/
public void pauseApp()
{
System.out.println("pauseAppҎ");
}
/**
* 销毁方?br /> * @param b
*/
public void destroyApp(boolean b)
{
System.out.println("destroyAppҎ");
}
}
在J2WTK中运行该E序Ӟ可以使用览器中的“MIDlet”菜单中的暂停和恢复菜单Q模拟暂停事件?/p>
二十五、用OTA来发布你的程?br /> 众所周知QJ2MEE序发布的Ş式主要有QOTA、数据线传输、红外和蓝牙传输{。这里简单说说如何通过OTA来发布你的程序?br /> OTA是Over The Air的简写,也就是通过|络下蝲Q这是主要的发布形式之一。现在的癑֮都是采用这UŞ式?br /> 使用OTA来发布程序,需要如下几个步骤:
1、在你的WEB服务器上d对于jad和jar文g的MIME支持?br /> 后缀?jad
MIMEcd:text/vnd.sun.j2me.app-descriptor
后缀?jar
MIMEcd:application/java-archive
2、发布WML面Q?br /> 例如你的jar文g名test.jadQ则最单的下蝲面是:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.3//EN"
" <wml>
<card id="card1" title="Download Midlet">
<a href="test.jad">test</a>
</card>
</wml>
你可以将以上代码保存在WEB服务器上Q例如保存ؓtext.wml
3、修改jad文gQ?br /> 在jad文g中增加 MIDlet-Jar-URL: http://domain/directory/test.jar
其中?a href="http://domain/directory/test.jar">http://domain/directory/test.jarZ的jar文g的\径?br /> l过上面的设|,你就可以你的wml面路径作ؓ你的WAP下蝲面发布了。用户只需要在手机上输入这个\径就可以讉K和下载你的程序了?br />作者BlogQ?/strong>http://blog.csdn.net/Mailbomb/