2006年8月1日
#
--sunfruit
在Linux Enterprise 5安裝Oracle的時候有時候會報錯例如“makefile '/oracle/product/10.2.0/db_1/rdbms/lib/ins_rdbms.mk' 的目標 'all_no_orcl' 時出錯”
經過多次測試是缺少相關的系統包文件,至少我的環境中確實是這樣,只要是安裝了下面的包文件,這個錯誤可以避免(版本號在不同環境中略有不同)
compat-libstdc++-33-3.2.3-61
compat-libstdc++-296-2.96-138
gcc-4.1.1-52.el5
gcc-c++-4.1.1-52.el5
glibc-2.5-12
glibc-common-2.5-12
glibc-devel-2.5-12
glibc-headers-2.5-12
libgcc-4.1.1-52.el5
make-3.81-1.1
binutils-2.17.50.0.6-2.el5
openmotif-2.3.0-0.3.el5
以上的安裝包由于在安裝Linux的選擇的包以及具體的環境不同可能,可能有些不需要,不過如果出現了ins_rdbms.mk的錯誤,可以按照這個方法嘗試一下
注:以上的安裝在Linux Enterprise 5的安裝盤中都包含,不需要額外在網上下載
-sunfruit
如果在安裝Oracle10g的時候沒有選擇字符集,則按照下面的3部進行字符集的修改就可以完全正常的使用Oracle10g(注:關于下面的1、2、3均是轉貼,后面添加了個人在使用的時候一些心得)
1、檢查服務器上Oracle數據庫的字符集
sqlplus /nolog
SQL> connect / as sysdba
連接成功.
SQL> desc props$
列名 可空值否 類型
------------------------------- -------- ----
NAME NOT NULL VARCHAR2(30)
VALUE$ VARCHAR2(2000)
COMMENT$ VARCHAR2(2000)
SQL> col value$ format a40
SQL> select name,value$ from props$;
NAME VALUE$
------------------------------ -------------------------
DICT.BASE 2
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_DATE_FORMAT DD-MON-YY
NLS_DATE_LANGUAGE AMERICAN
NLS_CHARACTERSET ZHS16GBK
NLS_SORT BINARY
NLS_CALENDAR GREGORIAN
NLS_RDBMS_VERSION 7.3.4.0.0
GLOBAL_DB_NAME ORACLE.WORLD
EXPORT_VIEWS_VERSION 3
NLS_CHARACTERSET和NLS_CHAR_CTERSET這個參數應該是ZHS16GBK,如不是,改為它。
SQL*Plus中修改方法:
SQL> update props$ set value$='ZHS16GBK' where name='NLS_CHARACTERSET';
2、確認字符集是否修改的不徹底。
SELECT DISTINCT (NLS_CHARSET_NAME(CHARSETID)) CHARACTERSET,
DECODE(TYPE#, 1,
DECODE(CHARSETFORM, 1, 'VARCHAR2', 2, 'NVARCHAR2', 'UNKOWN'),
9,
DECODE(CHARSETFORM, 1, 'VARCHAR', 2, 'NCHAR VARYING', 'UNKOWN'),
96,
DECODE(CHARSETFORM, 1, 'CHAR', 2, 'NCHAR', 'UNKOWN'),
112,
DECODE(CHARSETFORM, 1, 'CLOB', 2, 'NCLOB', 'UNKOWN')) TYPES_USED_IN
FROM SYS.COL$
WHERE CHARSETFORM IN (1, 2)
AND TYPE# IN (1, 9, 96, 112);
3、如果上面的查詢的確顯示有多個字符集的設定,則進行如下處理:
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
ALTER SYSTEM SET AQ_TM_PROCESSES=0;
ALTER DATABASE OPEN;
COL VALUE NEW_VALUE CHARSET
SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_CHARACTERSET';
COL VALUE NEW_VALUE NCHARSET
SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_NCHAR_CHARACTERSET';
--INTERNAL_USE是沒有寫在文檔中的參數,用以強制完成字符集一致化
ALTER DATABASE CHARACTER SET INTERNAL_USE &CHARSET;
ALTER DATABASE NATIONAL CHARACTER SET INTERNAL_USE &NCHARSET;
SHUTDOWN IMMEDIATE;
STARTUP;
-- 再次啟動數據庫一遍
SHUTDOWN IMMEDIATE;
STARTUP;
**************注意****************
本人在使用上面的方法設置以后Oracle10g可以正常使用,在導入dmp,sql文件的時候則需要在Linux中做如下設置
sql文件,dmp文件在導入oracle的時候需要設置字符集
export LANG=zh_CN.GBK //這個是linux的字符集設置
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK //這個是oracle的字符集設置
--sunfruit
在applet上疊加層有2種方式
第一:使用iframe的方式
第二:使用window.createPopup();方式
<div></div>的方式我是沒有試成功,無論如何設置,div都在applet的下面,要是哪位實驗成功了,經驗也共享一下
由于window.createPopup()主要用于創建右鍵菜單,由于其的一些特性,例如在其他地方點擊,該window.createPopup()對象消失,所以疊加層方式使用window.createPopup()并不合適,使用iframe制作疊加層的效果更好更好
效果圖如下
演示:
http://www.fruitres.cn/applet.jsp,可以從該演示頁直接下載js代碼,或者到代碼下載頁下載
代碼下載:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1084563770&number=0
更多下載:
http://www.fruitres.cn/
--sunfruit
代碼演示了基于ArcIMS開發包進行地圖開發的基礎方法
代碼不是單獨的可執行類,所以只看private void initMap()方法和public BufferedImage render()方法即可。MapBoundModel不用深究可以理解為等同Envelope類即可
在確定以下三個值
private String host = "172.17.8.99";
private int port = 5300;
private String servicename = "sde";
設置正確并且調用refresh();方法也沒有異常拋出,但是getStream();返回null值得時候,則需要調整AXL文件添加<OUTPUT method="stream"/>
重新發布,具體AXL內容請注冊后下載加密附件查看
查看代碼直接下載demo文件即可
地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1257242391&number=0
更多下載:
http://www.fruitres.cn
--sunfruit
Geotools是一個開源的Java GIS工具包,可利用它來開發符合標準的地理信息系統。Geotools提供了OGC(Open Geospatial Consortium)規范的一個實現來作為他們的開發
代碼演示了基于GeoTools開源包進行地圖開發的基礎方法
代碼不是單獨的可執行類,所以只看private void init()方法和public BufferedImage render()方法即可。MapBoundModel不用深究可以理解為等同Envelope類即可
直接下載demo文件即可
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=627152979&number=0
更多下載:
http://www.fruitres.cn
--sunfruit
3sNews最新消息,微軟現在也已經成為OGC主要成員之一,OGC組織也對微軟的加入感到高興,他們最需要做的事情當然是把Virtual Earth 和 SQL Server 2008納入OpenGIS兼容標準.微軟是隨Google之后加入OGC協會的。而2大巨頭的加入也標志著空間信息產業已進入發展的快車道,逐漸成為IT主流。
Google Earth的KML文件格式是開放地理協會的最佳應用之一,這一協會包含345個成員,涵蓋公司、政府機構和大學等角色,他們的目標是建立一個OpenGIS標準.2007年春季,Google遞交了Keyhole Markup Language (KML 2.1)--Google Earth的最新格式,通過了OGC的驗證,很快,它在世界范圍內引起了廣泛的興趣,OGC也將他們加入了 -- 地理標示語言(GML)中.
原貼:http://www.3snews.net/index.php/action_viewnews_itemid_16707.html
--sunfruit
這是一個自定義圖層的演示程序,演示向自定義圖層添加POI、修改自定義圖層中的POI、隱藏/顯示自定義圖層,關于圖層和POI的定義參考原來發布的例子
通過這個demo可以了解數據部署方式,需要的lib文件,至于其它的就是Swing+Graphics的基本應用,所以不提供源代碼
Swing+Graphics的用法請參考原來發布的作品
《基本繪圖演示源代碼,繪制矩形》
《JAVA開發的俄羅斯方塊源代碼》
JDK 1.5.0
功能:放大、縮小、平移
地圖底圖圖層控制
自定義圖層控制
參考MapXtremeJava48_DG_CHS.pdf,在MapInfo網站下載,或是安裝開發包以后在文檔目錄中提供
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=2087703113&number=0
更多下載:
http://www.fruitres.cn/
--sunfruit
使用MapInfo地圖引擎的自定義圖層的代碼
主要思路是自己建立系統圖層,該圖層和MapInfo引擎圖層無關(關于MapInfo引擎圖層控制請參考原來發布的例子),例如建立自己的ATM機圖層,要求可以控制顯示、隱藏ATM機圖層。那么需要建立一個圖層類和一個ATM機類,當然最好是接口的設計,這樣在2D繪制的時候可以很容易的進行擴展添加其他的圖層
注:不包含地圖數據和Lib包
參考MapXtremeJava48_DG_CHS.pdf,在MapInfo網站下載,或是安裝開發包以后在文檔目錄中提供
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1367471068&number=0
更多下載:
http://www.fruitres.cn/
--sunfruit
上次發布了一個使用MapInfo地圖引擎進行圖層控制源代碼,這次照例寫了一個java程序來演示圖層控制
通過這個demo可以了解數據部署方式,需要的lib文件,至于其它的就是Swing+Graphics的基本應用,所以不提供源代碼
Swing+Graphics的用法請參考原來發布的作品
《基本繪圖演示源代碼,繪制矩形》
《JAVA開發的俄羅斯方塊源代碼》
JDK 1.5.0
功能:放大、縮小、平移
地圖底圖圖層控制
參考MapXtremeJava48_DG_CHS.pdf,在MapInfo網站下載,或是安裝開發包以后在文檔目錄中提供
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=842314737&number=0
更多下載:
http://www.fruitres.cn
--sunfruit
使用MapInfo地圖引擎控制地圖圖層的代碼
該代碼只是對地圖引擎底圖數據圖層的控制,對于自定義圖層和添加新的圖層,以后會出相關的代碼
注:不包含地圖數據和Lib包
參考MapXtremeJava48_DG_CHS.pdf,在MapInfo網站下載,或是安裝開發包以后在文檔目錄中提供
直接下載demo文件即可
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=196483354&number=0
更多下載:
http://www.fruitres.cn/
--sunfruit
Oracle® Spatial
User’s Guide and Reference
10g Release 2 (10.2)
非常好的Oracle10g Spatial(10.2)的指南和參考文檔
英文的
目錄有3部分20多個章節,這里只節選了第二章節的目錄
2 Spatial Data Types and Metadata
2.1 Simple Example: Inserting, Indexing, and Querying Spatial Data..... 2-1
2.2 SDO_GEOMETRY Object Type ...... 2-5
2.2.1 SDO_GTYPE.......... 2-5
2.2.2 SDO_SRID.......... 2-7
2.2.3 SDO_POINT .......... 2-7
2.2.4 SDO_ELEM_INFO......... 2-7
2.2.5 SDO_ORDINATES ........ 2-10
2.2.6 Usage Considerations ......... 2-11
2.3 SDO_GEOMETRY Methods......... 2-11
2.4 SDO_GEOMETRY Constructors........ 2-13
2.5 Geometry Examples........ 2-14
2.5.1 Rectangle......... 2-14
2.5.2 Polygon with a Hole......... 2-15
2.5.3 Compound Line String ....... 2-17
2.5.4 Compound Polygon ......... 2-19
2.5.5 Point........... 2-20
2.5.6 Oriented Point .......... 2-21
2.5.7 Type 0 (Zero) Element......... 2-23
2.5.8 Several Geometry Types........ 2-25
2.6 Geometry Metadata Views ........ 2-29
2.6.1 TABLE_NAME......... 2-30
2.6.2 COLUMN_NAME......... 2-30
2.6.3 DIMINFO........ 2-30
2.6.4 SRID........... 2-31
2.7 Spatial Index-Related Structures ....... 2-31
2.7.1 Spatial Index Views ....... 2-31
2.7.1.1 xxx_SDO_INDEX_INFO Views....... 2-31
2.7.1.2 xxx_SDO_INDEX_METADATA Views..... 2-32
2.7.2 Spatial Index Table Definition ....... 2-34
2.7.3 R-Tree Index Sequence Object ....... 2-35
2.8 Unit of Measurement Support ........ 2-35
直接下載demo文件即可
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1500381985&number=0
更多下載:
http://www.fruitres.cn
--sunfruit
前一段時間寫了一個基于MapInfo地圖引擎生成地圖圖片的例子,這次寫了一個java程序來使用MapInfo地圖引擎瀏覽地圖,可以看到加載地圖數據以后,進行放大、縮小、平移的操作,拋去網絡因素,速度方面還是可以接受的
通過這個demo可以了解數據部署方式,需要的lib文件,至于其它的就是Swing+Graphics的基本應用,所以不提供源代碼
Swing+Graphics的用法請參考原來發布的作品
《基本繪圖演示源代碼,繪制矩形》
《JAVA開發的俄羅斯方塊源代碼》
JDK 1.5.0
功能:放大、縮小、平移
參考MapXtremeJava48_DG_CHS.pdf,在MapInfo網站下載,或是安裝開發包以后在文檔目錄中提供
下載地址:http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=254133506&number=0
更多下載:http://www.fruitres.cn/
-sunfruit
使用MapInfo地圖引擎生成地圖圖片的代碼,不包含地圖數據和Lib包
直接下載demo文件即可
參考MapXtremeJava48_DG_CHS.pdf,在MapInfo網站下載,或是安裝開發包以后在文檔目錄中提供
JDK1.5
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=447120696&number=0
更多下載:
http://www.fruitres.cn/
--sunfruit
WINXP中選擇多個磁盤進行碎片整理雖然bux一個批命令文件,可以順序執行多個磁盤的碎片整理工作,這樣雖然不能在WIN XP中選擇多個磁盤進行碎片整理,利用這個方法也可以一次執行多個磁盤的碎片整理,比較實用
下載地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1942443612&number=0
更多資料:
http://www.fruitres.cn/
--sunfruit
該文檔非常好的Oracle維護學習文檔
文檔目錄如下
監控SQL
常用戶用SQL
查詢表結構
表空間使用狀況
查詢某個模式下面數據不為空的表
客戶端主機信息
安裝Oracle以后,經常使用的修改表空間的SQL代碼
查看回滾段名稱及大小
PL/SQL入門教程
在from后面使用變量
Oracle常用數據字典
在Oracle中實現數據庫的復制
SQL*PLUS環境輸入'&字符'的方法
簡說創建用戶
簡說Oracle啟動及關閉數據庫實例
簡說Oracle數據庫導出(exp)/導入(imp)
實例:Oracle導出Excel文件
實例:Oracle導出HTM文件
查看數據庫保留字
數據字典及某些字段意義
下載地址
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=244294537&number=0
更多資料
http://www.fruitres.cn/
--sunfruit
源代碼四個文件,在這個DEMO中可以看到
如何使用2D組件,如何把JPanel做為畫板使用,如何使用Canvas替代JPanel,如何設置2D組件的線寬
這個只是針對像入門的新手,高手就不需要看了,免得扔雞蛋
下載地址
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1016162856&number=0
更多工具和文檔請到
http://www.fruitres.cn/
--sunfruit
將Oracle表結構導出成Html頁面的工具
1.0.4
增加了導出觸發器、存儲過程、函數的功能,并且修改了不能導出KEYS全部類型的BUG
1.0.3
增加了導出Sequences、View的功能,并且導出界面默認導出到安裝文件夾中的export目錄中
1.0.2
增加了導出KEYS、INDEX的功能
1.0.1
增加了導出完畢以后直接點擊打開文件夾、打開文件的查看方式,并且支持Linux/Win 平臺
1.0.0
導出為html文件,導出完畢后直接查看index.html文件即可。導出的內容包括各個表的名稱索引和注釋以及表中各個字段的明細和注釋
下載地址
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=942025602&number=0 下載版本1.0.4
更多工具和文檔請到
http://www.fruitres.cn/
--sunfruit
設置圖片的每個象素Alpha,使得圖片透明,核心代碼如下
private void setAlpha(ByteArrayOutputStream os) {
/**
* 增加測試項
* 讀取圖片,繪制成半透明
*/
try {
ImageIcon imageIcon = new ImageIcon(os.toByteArray());
BufferedImage bufferedImage = new BufferedImage(imageIcon.getIconWidth(),imageIcon.getIconHeight()
, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2D = (Graphics2D) bufferedImage.getGraphics();
g2D.drawImage(imageIcon.getImage(), 0, 0,
imageIcon.getImageObserver());
//循環每一個像素點,改變像素點的Alpha值
int alpha = 100;
for (int j1 = bufferedImage.getMinY(); j1 < bufferedImage.getHeight(); j1++) {
for (int j2 = bufferedImage.getMinX(); j2 < bufferedImage.getWidth(); j2++) {
int rgb = bufferedImage.getRGB(j2, j1);
rgb = ( (alpha + 1) << 24) | (rgb & 0x00ffffff);
bufferedImage.setRGB(j2, j1, rgb);
}
}
g2D.drawImage(bufferedImage, 0, 0, imageIcon.getImageObserver());
//生成圖片為PNG
ImageIO.write(bufferedImage, "png", new File(圖片路徑));
}
catch (Exception e) {
e.printStackTrace();
}
}
--sunfruit
在openConnection之前加上
Properties prop = System.getProperties();
System.getProperties().put("proxySet","true");
// 設置http訪問要使用的代理服務器的地址
prop.setProperty("http.proxyHost","xxxxxxx");
// 設置http訪問要使用的代理服務器的端口
prop.setProperty("http.proxyPort","xxxxx");
然后并不像有的貼子說的那樣添加
prop.setProperty("http.proxyUser","xxxxxxxx");
prop.setProperty("http.proxyPassword","xxxxx");
添加這樣的內容是不行的,而是將"username:password"進行base64編碼,具體代碼如下
String authentication = "username:password";
String encodedLogin = new BASE64Encoder().encode(authentication.getBytes());
httpurlconnection.setRequestProperty("Proxy-Authorization", " Basic " + encodedLogin);
注意BASE64Encoder().encode(authentication.getBytes());這里不能使用BASE64Encoder().encodeBuffer(authentication.getBytes())否則報錯
具體原因參考sun公布的JDK BUG , Bug ID: 4615330
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4615330
??? --sunfruit
??? 前一段時間發了一個“
[原創]JAVAMAIL發送郵件正文是html格式并且顯示圖片還帶有附件的郵件”的文章,后來經過深入的使用該功能發現幾個要點說明如下:
????第一:正文必須第一個添加到Multipart對象中
??? 第二:IMGX? X是序號,該序號必須從1開始,并且只能為數字
??? 以上兩點缺少一點都不會成功
??? 另:ByteArrayDataSource? 這個類在舊的javamail開發包中沒有,需要新的javamail開發包
??? --sunfruit
??? 有三個壓縮包需要下載,下載以后放在同一個目錄中解壓即可
????
下載包1????
下載包2????
下載包3
??? --sunfruit
???沒有什么說的,源代碼如下
public void sendMail(int id) {
??????? //數據初始化
??????? Transport transport; //發郵件時使用
??????? String part = GetResource.getXMLValue("mail_port"); //端口
??????? String mailhost = GetResource.getXMLValue("mail_server"); //服務器
??????? String user = GetResource.getXMLValue("mail_user"); //用戶名
??????? String password = GetResource.getXMLValue("mail_password"); //密碼
??????? String from = GetResource.getXMLValue("mail_from"); //發件mail
??????? String strname = GetResource.getXMLValue("mail_strname"); //在地址欄上顯示的名字
??????? String to = GetResource.getXMLValue("mail_to"); //收件人
??????? String cc = ""; //抄送人
??????? String bcc = ""; //密送人
??????? String title = GetResource.getXMLValue("mail_title"); //標題
??????? byte[] bytes = null;
??????? try {
??????????? bytes = FileIO.readFile(GetResource.getXMLValue("mail_contentpath")); //正文
??????? } catch (IOException ex) {
??????????? log.error("",ex);
??????? }
??????? String fromat = GetResource.getXMLValue("mail_fromat");
??????? //獲得下載需要的key
??????? byte[] bkeydes=null;
??????? try {
??????????? bkeydes=FileIO.readFile(GetResource.getXMLValue("附件地址"));
??????? } catch (IOException ex) {
??????????? log.error("",ex);
??????? }
??????? try {
??????????? Properties props = System.getProperties(); //獲得系統屬性
??????????? props.put("mail.smtp.host", mailhost); //設置SMTP主機
??????????? props.put("mail.smtp.auth", "true"); //設置身份驗證為真,若須身份驗證則必須設為真
??????????? //獲得郵件會話對象
??????????? Session session = null;
??????????? //需要驗證
??????????? session = Session.getDefaultInstance(props,
???????????????????????????????????????????????? new SmtpAuthenticator(user,
??????????????????? password));
??????????? //創建MIME郵件對象
??????????? MimeMessage mimeMsg = new MimeMessage(session);
??????????? //設置發信人
??????????? if (strname != null && !strname.equals("")) {
??????????????? mimeMsg.setFrom(new InternetAddress(from, strname));
??????????? } else {
??????????????? mimeMsg.setFrom(new InternetAddress(from));
??????????? }
??????????? //設置收信人
??????????? if (!to.equals("")) {
??????????????? mimeMsg.setRecipients(Message.RecipientType.TO,
????????????????????????????????????? InternetAddress.parse(to));
??????????? }
??????????? //設置抄送人
??????????? if (!cc.equals("")) {
??????????????? mimeMsg.setRecipients(Message.RecipientType.CC,
????????????????????????????????????? InternetAddress.parse(cc));
??????????? }
??????????? //設置暗送人
??????????? if (!bcc.equals("")) {
??????????????? mimeMsg.setRecipients(Message.RecipientType.BCC,
????????????????????????????????????? InternetAddress.parse(bcc));
??????????? }
??????????? //設置郵件主題
??????????? sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
//??????????? mimeMsg.setSubject("=?GB2312?B?" + enc.encode(title.getBytes()) +
//?????????????????????????????? "?=");
??????????? mimeMsg.setSubject(title, "GBK");
??????????? //設置郵件內容
??????????? //mimeMsg.setText(content, "gb2312");
??????????? //發送日期
??????????? mimeMsg.setSentDate(new Date());
??????????? /*******************附件 begin××××××××××××××××××××××××*/
??????????? BodyPart mbp = null; //正文
??????????? //MimeBodyPart mbp1 = null; //附件
??????????? mbp = new MimeBodyPart();
??????????? //設置郵件正文格式(html/text)
??????????? if (fromat.equals("0") || fromat.equals("")) {
??????????????? //普通格式
??????????????? mbp.setText(new String(bytes,"GBK"));
??????????? } else {
??????????????? //網頁格式
??????????????? mbp.setDataHandler(new DataHandler(new String(bytes,"GBK"),
??????????????????????? "text/html;charset=gb2312"));
??????????? }
??????????? //!!!!注意必須為new MimeMultipart("related"); 這樣的實例化才能發送html正文顯示圖片的郵件 "related"
??????????? //new MimeMultipart(); 這樣實例化則不能發送html正文顯示圖片的郵件 只可以發送其他的郵件
??????????? Multipart mp = new MimeMultipart("related");
??????????? if (mbp != null)
??????????????? mp.addBodyPart(mbp); //正文
??????????? //附件key.des
??????????? BodyPart messageBodyPart = new MimeBodyPart();
??????????? ByteArrayDataSource fileds = new ByteArrayDataSource(bkeydes,"application/octet-stream");
??????????? messageBodyPart.setDataHandler(new DataHandler(fileds));
??????????? //解決附件中文問題
??????????? //mbp1.setFileName(fileds.getName(),"gb2312");
??????????? messageBodyPart.setFileName(MimeUtility.encodeWord("key.des", "GB2312", null));
??????????? mp.addBodyPart(messageBodyPart);
??????????? //附件 圖標
??????????? messageBodyPart = new MimeBodyPart();
??????????? bytes=FileIO.readFile(GetResource.getXMLValue("img_logo"));
??????????? fileds = new ByteArrayDataSource(bytes,"application/octet-stream");
??????????? messageBodyPart.setDataHandler(new DataHandler(fileds));
??????????? //解決附件中文問題
//??????????? messageBodyPart.setFileName("fruitrsource_touming.jpg");
//??????????? messageBodyPart.setHeader("Content-ID", "fruitrsource_touming");
??????????? messageBodyPart.setFileName("1.jpg");
??????????? //!!!!注意這里是"<IMG1>" 帶有尖括號 而在正文的html里面則是src="cid:IMG1"
??????????? messageBodyPart.setHeader("Content-ID", "<IMG1>");
??????????? mp.addBodyPart(messageBodyPart);
??????????? // 添加 Multipart到Message中
??????????? mimeMsg.setContent(mp);
??????????? mimeMsg.saveChanges();
??????????? transport = session.getTransport("smtp");
??????????? transport.connect(mailhost, Integer.parseInt(part), user, password);
??????????? //發送郵件
??????????? //transport.send(mimeMsg, mimeMsg.getAllRecipients());
??????????? //transport.send(mimeMsg);
??????????? transport.sendMessage(mimeMsg, mimeMsg.getAllRecipients());
??????????? //System.out.println("mail send!");
??????????? transport.close();
??????? }
??????? catch (Exception e) {
??????????? e.printStackTrace();
??????? }
??? }
class SmtpAuthenticator extends Authenticator {
??????? //SMTP身份驗證
??????? public SmtpAuthenticator(String username, String password) {
??????????? this.username = username;
??????????? this.password = password;
??????? }
??????? public PasswordAuthentication getPasswordAuthentication() {
??????????? return new PasswordAuthentication(this.username, this.password);
??????? }
??????? String username = null;
??????? String password = null;
??? }
--sunfruit
JAVA編寫的使用像素RGB值還原圖像的方法,代碼如下
public class ImageData {
private int[][] idata=null;
public ImageData() {
readImageData();
writeImageData();
}
private int[][] readImageData()
{
try {
//imagedata的鍵值就是一個存儲了圖像像素RGB值得文本文件
RandomAccessFile randomAccessFile = new RandomAccessFile(new File(GetResource.getXMLValue("imagedata")),"r");
long filesize=randomAccessFile.length();
long filepointer=0;
DynArrayInt dynArrayInt=null;
ArrayList arrayList=new ArrayList();
int n1=0;
while(filesize>filepointer)
{
filepointer=randomAccessFile.getFilePointer();
String str1=randomAccessFile.readLine();
if(str1!=null)
{
String[] str2=str1.split(" ");
String[] str3=str2[1].split(",");
dynArrayInt=new DynArrayInt();
for(int i=0;i<str3.length;i++)
{
if(!str3[i].equals(""))
{
int pix = Integer.parseInt(str3[i], 16);
dynArrayInt.addInt(pix);
}
}
if(n1==0)
n1=dynArrayInt.getSize();
arrayList.add(dynArrayInt);
}
}
idata=new int[arrayList.size()][dynArrayInt.getSize()];
for(int i=0;i<idata.length;i++)
{
DynArrayInt dynArrayInt1=(DynArrayInt)arrayList.get(i);
for(int j=0;j<idata[i].length;j++)
{
idata[i][j]=dynArrayInt1.getInt(j);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return idata;
}
private void writeImageData()
{
BufferedImage bufferedImage=new BufferedImage(idata[0].length,idata.length,BufferedImage.TYPE_INT_RGB);
for(int i=0;i<idata.length;i++)
{
for(int j=0;j<idata[i].length;j++)
{
bufferedImage.setRGB(j,i,idata[i][j]);
}
}
Random random=new Random(System.currentTimeMillis());
String ext="jpg";
String filepath = System.getProperty("java.io.tmpdir") + random.nextInt(99999) + "." + ext;
try {
ImageIO.write(bufferedImage, ext, new File(filepath));
System.out.println("文件已經生成,路經為" + filepath);
}
catch (IOException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
ImageData imagedata = new ImageData();
}
}
其中的“GetResource”,“DynArrayInt”為自定義類,GetResource得功能就是解析獲取properties文件的內容,DynArrayInt是一個整形動態數組,可以在以前的文章里獲得“DynArrayInt”的源代碼,所謂的圖像的像素值就是如下數據
px1 44525b,44555d,475a61,47585d,44555a,46575c,44575d,43565c,42575e,43585f,445960,435962,435964,425861,425861,41585e,3f555e,3e545f,3d5261,3c5162,3d4f59,3d4f59,3f515b,40525c,40525c,40525c,40525c,3f515b,3f515b,3f4f5a,3f4f5c,40505d,3f515f,3f515f,3d5060,3c4f5f,3b505f,3a4e58,3e525e,3e525e,3d5060,415466,3f5264,405363,405460,41555f,42575d,42575e,43585f,445862,445862,445864,445864,445866,445866,415760,435962,445a63,455b66,455b66,445a67,455b68,485d6c,4c626f,4d636e,4e646f,4d636c,4b616a,51686e,536a70,566d73,5c7378,6f7c7a,7c8987,86908f,8d9796,8e9696,949a9a,969c9c,9ca2a2,a1a3a1,a8aaa8,aeb1ac,b2b5b0,b9bcb7,c5c8c2,cccfc9,ced1c9,d6d9d1,d5d8d3,d7dad5,dadbd5,dcddd7,dcdbd4,ddddd3,e0ddd4,e0ddd4,deddd6,deddd6,deddd6,dfded7,dfded7,e0dfd8,e0dfd8,e0dfd8,e0dfd8,e3dfd9,e3dfd9,e3dfd9,e3dfd9,e4e0da,e4e0da,e4e0da,e5e1db,e5e1db,e2e1da,e2e1da,e3e2db,e3e2db,e4e3dc,e4e3dc,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e6e5de,e6e5de,e6e5de,e6e5de,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e4e1d8,e5e2d9,e5e2d9,e4e1d8,e2dfd6,e3e0d7,e2dfd6,e1ded5,e1ded5,e2dfd6,e2dfd6,e2dfd6,e2dfd6,e1ded5,e1ded5,e1ded5,e1ded5,e1ded5,dedad4,dedad4,dcdbd4,dcdbd4,dbdcd4,dbdcd4,d9dcd4,d9dcd4,dedad4,dedad4,ddd9d3,ddd9d3,dbd7d1,dbd7d1,dcd8d2,dcd8d2,dbd7d1,d5d8d0,d5d8d0,d7d8d0,d7d8d0,d7d6cf,d9d5cf,d9d5cf,d9d5cf,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d5d4cd,d5d4cd,d5d7cc,d5d7cc,d5d7cc,d5d7cc,d4d6cb,d4d6cb,d5d7cc,d5d7cc,d5d7cc,d2d7d0,d2d7d0,d3d6d0,d3d6d0,d4d5cf,d4d5cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d0d3cb,d2d3cb,d3d2cb,d6d0cb,d6d0cb,d4d3cc,d3d4cc,d2d5cd,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d0d3cb,d0d3cb,d2d3cb,d2d3cb,d3d2cb,d3d2cb,d5d1cb,d5d1cb,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d1d0c9,d1d0c9,d2d1ca,d2d1ca,d3d2cb,d3d2cb,d3d2cb,d3d2cb
px0 44525b,44575e,465b62,485a5a,455757,46585a,44575b,42555b,41565d,435761,445862,445a63,435966,435964,42595f,42595e,40575c,3f5560,3d5263,3c5066,3a4c58,3b4d59,3d4f5b,3e505a,40525c,40525c,40535a,3f5259,3f5259,3f5057,3f5057,405158,3f5259,3f5259,3d515b,3c505a,3b515c,3a4e5a,3e5260,3e5260,3d5060,415468,3f5266,3f5262,405462,405460,42575d,42575e,43585f,435761,41555f,425662,445864,465a68,465a68,415760,425861,445a63,445a65,445a65,465c69,475d6a,475c6b,44596a,485d6e,4c6373,4d6474,4a616f,4b6472,4b6472,496270,4c6573,5f6c6e,677476,6f7c7e,798587,818a8d,899295,8f969a,91989c,949895,9a9e9b,9ea39d,a1a6a0,a9aea8,aeb3ac,babfb8,c0c5bc,ccd1c8,cfd2cd,d2d5d0,d5d6d0,d7d8d2,dcdbd4,ddddd3,e0ddd4,e0ddd4,deddd6,deddd6,deddd6,dfded7,dfded7,e0dfd8,e0dfd8,e0dfd8,e0dfd8,e3dfd9,e3dfd9,e3dfd9,e3dfd9,e4e0da,e4e0da,e4e0da,e5e1db,e5e1db,e2e1da,e2e1da,e3e2db,e3e2db,e4e3dc,e4e3dc,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e6e5de,e6e5de,e6e5de,e6e5de,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e4e1d8,e4e1d8,e4e1d8,e3e0d7,e2dfd6,e2dfd6,e2dfd6,e1ded5,e1ded5,e2dfd6,e2dfd6,e2dfd6,e2dfd6,e1ded5,e1ded5,e1ded5,e1ded5,e1ded5,ddd9d3,ddd9d3,dbdad3,dbdad3,dadbd3,dadbd3,d8dbd3,d8dbd3,dedad4,dedad4,ddd9d3,ddd9d3,dad6d0,dbd7d1,dcd8d2,dcd8d2,dbd7d1,d5d8d0,d5d8d0,d5d8d0,d7d8d0,d6d7cf,d7d6cf,d7d6cf,d9d5cf,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d5d4cd,d5d4cd,d6d8cd,d6d8cd,d6d8cd,d6d8cd,d4d6cb,d4d6cb,d5d7cc,d5d7cc,d5d7cc,d3d8d1,d3d8d1,d4d7d1,d4d7d1,d4d5cf,d4d5cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d0d3cb,d2d3cb,d3d2cb,d6d0cb,d7d1cc,d4d3cc,d4d5cd,d2d5cd,d4d2d1,d4d2d1,d4d2d1,d4d2d1,d4d2d1,d3d1d0,d3d1d0,d3d1d0,d3d1d0,d0d3cb,d0d3cb,d2d3cb,d2d3cb,d3d2cb,d3d2cb,d5d1cb,d5d1cb,d3d2cd,d3d2cd,d3d2cd,d3d2cd,d3d2cd,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d1d0c9,d1d0c9,d2d1ca,d2d1ca,d4d3cc,d4d3cc,d4d3cc,d4d3cc
以上數據表示圖像有兩行像素,每一行有260個像素組成,每個像素的RGB值就是44525b
--sunfruit

求上圖1點到其他各點的最短路徑,依據圖論知識建立矩陣模型,進一步得到代碼如下
public class ShortPathA {
private static int[][]
a = {
{0, 50, 10, 100000, 45, 100000}, {100000, 0, 15, 100000, 10, 100000}, {20, 100000, 0, 15, 100000, 100000}, {
100000, 20, 100000, 0, 35, 100000}, {100000, 100000, 1000000, 30, 0, 100000}, {100000, 100000, 100000, 3, 100000, 0}
};
private static boolean[] mark = new boolean[a.length];
public ShortPathA() {
int Vo = 0; //源點
//源點到其他各點的距離
int[] b = new int[a.length];
DynArrayInt S = new DynArrayInt();
for (int i = 0; i < a.length; i++) {
mark[i] = false;
//b[i] = a[Vo][i];
}
int best = -1;
mark[0] = true;
b[0] = 0; //{0為源點}
while (best != 0) {
best = 0;
int best_j = 0;
for (int i = 0; i < b.length; i++)
{
if (mark[i]) //{對每一個已計算出最短路徑的點}
{
for (int j = 0; j < b.length; j++) {
if ( (!mark[j]) && (a[i][j] > 0)) {
if ( (best == 0) || (b[i] + a[i][j] < best)) {
best = b[i] + a[i][j];
best_j = j;
}
}
}
}
}
if (best > 0) {
b[best_j] = best;
mark[best_j] = true;
}
}
System.out.println(java.util.Arrays.toString(b));
}
public static void main(String[] args) {
ShortPathA shortpath = new ShortPathA();
}
}
--sunfruit
當通過URLConnection第一次和服務器發起POST或是GET請求以后,通過getHeaderField方法獲得SessionID,具體方法為:
String session_value=getHeaderField("Set-Cookie");
第二次發起POST或是GET請求的時候需要把剛才獲得的SessionID放置在請求的頭部然后再提交,這樣就能服務器就會認為是同一個Session請求了,具體方法為:
setRequestProperty("Cookie", session_value);
如果取得Set-Cookie的值比較長,取值到分號前即可 如 JSESSIONID=575F7196EDB52825D288F4061C66BC29;
這樣就實現了保存session向服務器提交請求
??? --sunfruit

上圖求一筆畫的路徑,利用圖論的相關知識可以得到程序如下:
public class OnePath {
??? private static int[][]
??????????? links = { {0,1,1,0,0,0,1,0}, {1,0,0,1,0,0,0,1}, {1,0,0,1,1,1,0,0},
??????????? {0,1,1,0,1,1,0,0}, {0,0,1,1,0,1,1,0}, {0,0,1,1,1,0,0,1}, {1,0,0,0,1,0,0,0}, {0,1,0,0,0,1,0,0}
??? };
??? public OnePath() {
??????? int sum = 0;
??????? //存放每個點的度
??????? int[] point = new int[links[0].length];
??????? for (int i = 0; i < links[0].length; i++) {
??????????? int[] templink = links[i];
??????????? for (int j = 0; j < links[0].length; j++) {
??????????????? point[i] += templink[j];
??????????? }
??????????? sum += point[i];
??????? }
??????? //計算度數是奇數點的個數,如果大于2則不能一筆畫
??????? int odt = 0;
??????? int start = -1;
??????? for (int i = 0; i < point.length; i++) {
??????????? int mod = point[i] % 2;
??????????? if (mod > 0) {
??????????????? //if(start==-1)
??????????????????? start = i;
??????????????? odt++;
??????????? }
??????? }
??????? if(odt>2)
??????? {
????????? System.out.println("該圖不能一筆畫");
????????? return;
??????? }
??????? int r = 0;
??????? //從一個奇數點開始計算
??????? int nowd=start;
??????? System.out.print(nowd+1);
??????? while (sum > 0) {
??????????? r=0;
??????????? //對于起點nowd 檢查當前的點r 是否合適
??????????? //links[nowd][r]==0 判斷是否有可以走的沒有用過的線路
??????????? //(point[r]<=1 && sum!=2) 判斷是否是最后一次,如果不是最后一次,那么避開度數是1的點
??????????? while (links[nowd][r]==0 || (point[r]<=1 && sum!=2)) {
??????????????? r++;
??????????? }
??????????? links[nowd][r]=0; //已經用過的線路
??????????? links[r][nowd]=0; //已經用過的線路 links[nowd][r] links[r][nowd]互為往返路線,用過1->2那么2->1也作廢了
??????????? sum=sum-2; //總度數減2 因為從1->2 消耗了1的度和2的度
??????????? point[nowd]--; //起點和終點的度都減1 1->2 那么1的度和2的度都減1
??????????? point[r]--; //起點和終點的度都減1 1->2 那么1的度和2的度都減1
??????????? nowd =r; //設置新的起點
??????????? System.out.print("->"+(r+1));
??????? }
??? }
??? public static void main(String[] args) {
??????? new OnePath();
??? }
}
如果你使用JavaScript編程,你或許會懷疑它是否包含了面向對象(OO)的結構。實際上,JavaScript的確支持面向對象的架構――在某種程度上。本文將通過一個可擴展向量圖形(SVG)的實例來說明JavaScript的OO結構。
我如何在類中定義方法和屬性?
OO開發的一個基本方面是類及其相應的方法和/或屬性的使用。JavaScript通過function關鍵字支持類(及其屬性)的使用。下面的代碼定義了一個叫做Figure的JavaScript類:
function Figure() {
this.centerX=0;
this.centerY=0;
this.area=0;
this.transform = transform; // methods are defined like this
function transform(moveX,moveY,angle) {
this.centerX += moveX;
this.centerY += moveY;
} }
這個Figure類有三個屬性:centerX,centerY,和area。另外,它還有一個方法叫做transform()。前三行是這個類的構造器。
但是它看起來不像一個類
你會想Figure()看起來不像一個類,而更像一個JavaScript的函數。那么為什么Figure()定義的是個類?
嚴格的說,Figure()函數沒有定義一個類,但是它仿造了一個。它實際上創建了一個對象,在括號里的代碼使這個對象的構造器。JavaScript的對象支持是很基礎的,它并不區分類和對象。
這就引到了問題為什么Figure()函數創建的是一個對象。對象是可以有屬性和方法的?;旧?,因為Figure()函數同時包含了屬性和方法,它就是個對象。在JavaScript里,所有的函數即是對象又是可調用的代碼塊。這不像它聽起來的那樣容易被誤解。要創建一個Figure()類/對象,你只用使用以下句法:
MyFigure = new Figure();
你也可以把Figure()函數當作代碼塊調用,就像這樣:
figValue = Figure();
變量figValue沒有被定義是因為代碼塊Figure()沒有返回任何值。如果你把return(this.area)加到函數的最后一行,figValue就會有個值0。所以figValue是個類型數字,MyFigure是對象 Rectangle的實例。
為什么所有的變量前面都一個“this”?
這個關鍵字this表示這是對象的實例變量,可以使用MyFigure.centerX從對象外部訪問。要讓變量成為私有變量,去掉前綴this就行了。this.transform = transform這一行讓方法成為公用方法。這個方法通過MyFigure.transform(100,100,0)調用。
這些類有層次之分嗎?
另一個好問題的是JavaScript的類是否有層次之分。回答是肯定有。我們來仔細看看是怎么做到分層的。我們可以定義一個Rectangle子類,并把Figure作為父類:
function Rectangle(startX, startY, endX, endY) {
this.width = endX - startX;
this.height = endY - startY;
this.centerX = (endX + startX)/2;
this.centerY = (endY + startY)/2;
this.computeArea = computeArea;
function computeArea() {
this.area = this.width*this.height;
} }
Rectangle.prototype = new Figure();
Rectangle對象是用4個自變量創建的,前四行是構造器。 Rectangle類包含了一個方法: computeArea()。最后一行Rectangle.prototype = new Figure();,把Rectangle類定義為從Figure類繼承來的子類。
然我來解釋一下prototype(原型)。每個對象構造器都有prototype屬性;這是用來給所有的對象增加新屬性和方法的。這就是為什么原型被用來實現繼承:child.prototype = new parent();。通過原型,父對象的所有屬性和方法都被添加到子對象上。
要注意this.centerX,this.centerY,和area是Rectangle類中所使用的屬性,但是它們是 Figure父類的屬性。和Rectangle類相似,Circle類可以被定義成Figure類的原型。這種父子關系可以按你需要來定義深度;你可以創建另一個Rectangle的子類。
我如何創建一個類的實例?
在JavaScript里創建一個類的實例很容易:
rect = new Rectangle(100,100,900,800);
這就創建了Rectangle類型的一個對象。Rectangle的構造器在屬性width, height, centerX, 和centerY中填入了值。rect.area屬性的值是零(0)。使用這個命令就能調用area方法:
rect.computeArea();
rect.area的值現在是560,000。要調用transform方法使用:
rect.transform(100,200,0);
父和子對象的屬性可以像這樣訪問到:
var ar = rect.area;
var wi = rect.width;
我能超越屬性和方法嗎?
就像你在Java中的一樣,你可以超越屬性和方法。在子類中定義的屬性或者方法可以超越同名的父類的屬性和方法。
和全局變量互動
JavaScript也支持全局變量的使用。在以下代碼段中測試一下g_area變量的范圍:
<HTML>
<SCRIPT>
var g_area = 20;
function Figure() {
…
this.area=g_area;
…
}
function Rectangle(){ … }
Rectangle.prototype = new Figure();
function test(){
g_area = 40;
rect = new Rectangle();
alert(rect.area);
}
</SCRIPT>
<BODY onLoad = 'test()'/>
</BODY>
</HTML>
rect.area的值是20(不是你預計的40),這是因為Rectangle對象是Figure對象的原型,這種關系在test()被調用以前就被定義了。要使用g_area的新值,你需要用以下的方法:
function test() {
g_area = 40;
Rectangle.prototype = new Figure();
rect = new Rectangle();
alert(rect.area);
}
對于所有的Rectangle的新實例,這將改變area屬性的值?;蛘撸憧梢允褂眠@種方法:function test() {
g_area = 40;
rect = new Rectangle();
Rectangle.prototype.area = g_area;
alert(rect.area);
}
這將改變Rectangle所有現存的以及新實例的area屬性的值。
結論
為了效仿OO開發,JavaScript提供了必需的繼承、封裝和超越屬性,盡管它不支持接口和方法的過載。如果你是剛接觸到OO開發,用它試試。OO概念允許開發者將一組數據和相關操作集中入一個對象。這在管理瀏覽器事件和管理瀏覽器內SVG圖時很有用。
摘要: --sunfruit如何讓你的網站排名靠前??????? 網站做好了,怎樣才能讓你的網站在各大搜索引擎中排名靠前呢?網上的帖子很多,通過搜索和總結,整理出了一套自己行之有效的方法,寫出來供大家參考?????? 成功案例推薦:http://sooboo.com.cn/?????? 還在繼續整理中,而且我們的網站也在不斷優化中.........1、?網站建好后首先到各大搜索引擎免費登錄你的網站http...
閱讀全文
??? --sunfruit
???很多時候需要上傳附件到服務器,一般采用在頁面放置<input type="file" name="upload" value=""> 的方式讓用戶選擇要上傳的文件進行上傳,使用的是HTTP協議,這樣的方式很方便開發也簡單,不過如果上傳的附件比較大的時候,會出現IE響應很慢的情況,如果用戶急性子,多點幾下上傳的按鈕,那么就會導致IE不響應的情況,這個時候如果在文件上傳得過程中,給用戶一個動態的提示甚至是一個上傳的進度條,效果就會好多了,這樣就會用到Ajax技術了,讓Ajax以一個固定的間隔時間檢查上傳情況然后在頁面以文字或是圖片的方式體現出來就行了。
???在使用Ajax進行附件上傳進度查詢的時候也想過,直接使用Ajax進行附件上傳,在實現過程中發現問題比較多,所以就使用了變通的方式:使用標準的附件上傳方式,結合Ajax進行上傳的進度檢查
???主要的代碼如下:
???Ajax的封裝
???/**
? * 創建 XMLHttpRequest 對象
? */
? function getXMLHttpRequest()
? {
??? var http_request;
??? if (window.XMLHttpRequest) {
????? //非IE瀏覽器框架創建 XMLHttpRequest 對象
????? http_request = new XMLHttpRequest();
????? if(http_request.overrideMimeType)
????? {
??????? http_request.overrideMimeType('text/xml');
????? }
??? }else if (window.ActiveXObject){
????? // 創建 XMLHttpRequest 對象
????? try {
??????? http_request = new ActiveXObject("Msxml2.XMLHTTP");
????? } catch (e1) {
??????? try {
????????? http_request = new ActiveXObject("Microsoft.XMLHTTP");
??????? } catch (e2) {
????????? // 不能創建 XMLHttpRequest 對象
??????? }
????? }
??? }
??? return http_request;
? }
/**
?? * Get請求
?? */
? function sendGetDictate(http_request,url)
? {
??? req.open("GET", url, true);
??? http_request.send(null);
? }
以上是Ajax的的基礎部分,下面說文件上傳部分的檢查部分,文件上傳本身的流程不變,只是在提交上傳以后,需要執行setTimeout(checkupload,500); 這樣的方法 checkupload 方法要自己編寫,例如
function checkupload()
? {
??? req=getXMLHttpRequest();
??? req.onreadystatechange = setActiveContent;
??? sendGetDictate(req,"/manager/servlet/imageservlet?tag=ajaxuploadfilecheck&WARE_ID=609187669&nocache="+Math.random(),"name=111");
? }
然后需要編寫setActiveContent方法,例如
var count=0; //防止無限循環,并且在頁面提交上傳得時候設置為0
function setActiveContent()
? {
??? if (req.readyState == 4) {
????? if (req.status == 200) {
??????? var rettext=req.responseText; //這里是servlet返回的內容,檢查上傳得狀態,可以在javabean或是servlet里面設置全局的靜態變量來表明上傳狀態
??????? if(rettext=="-1")
??????? {
????????? //停止循環
????????? alert("服務器更新錯誤");
??????? }
??????? else if(rettext=="0")
??????? {
????????? //繼續循環檢查
????????? if(count<6)
????????? {
??????????? setTimeout("checkupload()",500);
??????????? count++;
????????? }
????????? else
????????? {
??????????? alert("上傳失敗");
????????? }
??????? }
????????else if(rettext=="1")
??????? {
????????? alert("文件上傳成功");
??????? }
????? }
??? }
? }
?
基本流程就是這樣了,至于文字表現上傳過程還是進度條表現,就看自己了
Painting in AWT and Swing
Good Painting Code Is the Key to App Performance
By Amy Fowler
在圖形系統中, 窗口工具包(windowing toolkit)通常提供一個框架以便相對容易地創建一個圖形用戶接口(GUI),在正確的時間、正確的屏幕位置顯示一個正確的圖像位。
AWT (abstract windowing toolkit,抽象窗口工具包) 和Swing都提供這種框架。但是實現這種框架的APIs對一些開發人員來講不是很好理解 -- 這就導致一些程序的運行達不到預期的效果。
本文詳細地解釋AWT和Swing的繪畫機制,目的是幫助開發人員寫出正確的和高率的GUI繪畫代碼。然而,這篇文章只包括一般的畫圖機制(即,在什么地方和什么時間去呈現),而不介紹Swing的圖形API怎樣去呈現圖形。想學習怎樣去顯示漂亮的圖形,請訪問Java 2D 網站。
繪畫系統的演變
當最初的、為JDK1.0使用的AWT API發布時,只有重量級(heavyweight)部件("重量級" 的意思是說該部件有它自己的、遮光(opaque)的、與生俱來的窗體)。這樣就使得AWT在很大程度上依賴于本地平臺的繪畫系統。這樣的安排需要開發人員寫代碼的時候要考慮到很多細節問題,象重畫檢測(damage detection)、剪切(clip)計算、以及Z軸次序等。隨著JDK 1.1中輕量級(lightweight)部件的引入("輕量級" 部件重用了與它最接近的重量級祖先的本地窗體),需要AWT能在共享的代碼里為輕量級部件實現繪畫處理。因此,重量級和輕量級部件在它們各自的繪畫處理方法有著微妙的差別。
在JDK 1.1之后,當發布了Swing工具的時候,引入了它自己的繪畫風格。Swing的繪畫機制在很大程度上類似并且依賴于AWT,但是,也有它自己的觀點,還帶來了新的API,使得應用程序可以容易地定制繪畫工作。
在AWT中繪畫
去理解AWT繪畫API怎樣工作,有助于我們搞明白是什么觸發了窗口環境中的繪畫操作。AWT中有兩種繪畫操作:系統觸發的繪畫,和程序觸發的繪畫
系統觸發的繪畫操作
在系統觸發的繪畫操作中,系統需要一個部件顯示它的內容,通常是由于下列中的原因:
部件第一次在屏幕上顯示
部件的大小改變了
部件顯示的內容受損需要維護。(比如,先前擋住部件的其它物體移走了,于是部件被擋住的部分曝露出來。
程序觸發的繪畫操作
在程序觸發的繪畫操作,是部件自己決定要更新自身的內容,因為部件內部的狀態改變了。(比如,監測到鼠標按鈕已經按下,那么它就需要去畫出按鈕"被按下"時的樣子>
畫圖的方法
不管是誰觸發了畫圖請求,AWT都是利用"回調"機制來實現繪畫,這個機制對于“重量級”和“輕量級”的部件都是相同的。這就意味著程序應該在一個特定的可覆蓋的方法中放置那些表現部件自身的的代碼,并且在需要繪畫的時候,工具包就會調用這個方法。這個可覆蓋的方法在java.awt.Component中聲明:
??? public void paint(Graphics g)
當AWT調用這個方法時,作為參數的、負責在這個特定的部件上繪畫的Graphics對象是在之前已經配置了的,擁有恰當的狀態值。
Graphics的顏色 值被設置為部件的前景。
Graphics的字體 設置為部件的字體。
Graphics的平移(translation) 也給設定,使用坐標(0,0)定位部件的左上角。
Graphics的裁剪框(clip rectangle)設置為部件需要畫圖的區域。
程序必須使用這個Graphics(或者其派生類)對象來呈現繪畫,并且可以根據自己的需要任意改變Graphics對象的屬性值。
這里是一個回調繪畫的簡單例子,在部件的范圍內呈現一個實體園:
??? public void paint(Graphics g) {
??????? // 根據部件的范圍,動態計算圓的尺寸信息。
??????? Dimension size = getSize();
??????? // 直徑
??????? int d = Math.min(size.width, size.height);
??????? int x = (size.width - d)/2;
??????? int y = (size.height - d)/2;
??????? // 畫圓(顏色已經預先設置為部件的前景顏色)
??????? g.fillOval(x, y, d, d);
??????? g.setColor(Color.black);
??????? g.drawOval(x, y, d, d);
??? }
初次接觸AWT的開發人員可以看看PaintDemo example,那里介紹了一個在AWT程序中怎樣使用畫圖回調方法的例子。
一般情況下,程序應該避免把繪畫代碼放置在回調方法paint()的范圍之外。為什么呢?因為paint方法之外的繪畫代碼可能會在不適合畫圖的時候被調用 -- 例如,在部件變為可見之前或者已經在使用一個有效的Graphics。同時,不推薦在程序中直接調用paint()。
為了使能夠由程序觸發繪畫操作,AWT提供了下面的java.awt.Component的方法,這樣程序就可以提出一個異步的繪畫請求:
??? public void repaint()
??? public void repaint(long tm)
??? public void repaint(int x, int y, int width, int height)
??? public void repaint(long tm, int x, int y,
?????????????????? int width, int height)
下面的代碼顯示了一個簡單的鼠標監聽器的例子,當鼠標按下和抬起的時候,使用repaint()來觸發“假想按鈕”的更新操作。
MouseListener l = new MouseAdapter() {
??????????? public void mousePressed(MouseEvent e) {
??????????????? MyButton b = (MyButton)e.getSource();
??????????????? b.setSelected(true);
??????????????? b.repaint();????????????
??????????? }
??????????? public void mouseReleased(MouseEvent e) {
??????????????? MyButton b = (MyButton)e.getSource();
??????????????? b.setSelected(false);
??????????????? b.repaint();????????????
??????????? }
??????? };
如果部件要呈現復雜的圖形,就應該使用帶參數的repaint()方法,通過參數來指定需要更新的區域。一個比較常見的錯誤是總是調用無參數的repaint()來提出重畫請求,這個方法會重畫整個部件,經常導致一些不必要的畫圖處理。
paint() vs. update()
為什么我們要區分繪畫操作是"系統觸發" 還是"程序觸發"呢?因為在“重量級”部件上,AWT對這兩種請求的在處理上稍有不同(“輕量級”的情況將在后面介紹),并且不幸的是與此相關的代碼非常復雜,難以更改。
對于“重量級”部件,這兩種方式的繪畫產生于兩條不同的途徑,取決于是“系統觸發”還是“程序觸發”。
系統觸發的繪畫
下面介紹“系統觸發”的繪畫操作是怎么產生的:
AWT確定是一部分還是整個部件需要繪畫。
AWT促使事件分派線程調用部件的paint()方法。
程序觸發的繪畫
由程序觸發的繪畫的產生如下所示:
程序確定是一部分還是全部部件需要重畫以對應內部狀態的改變。
?
程序調用部件的repaint(),該方法向AWT登記了一個異步的請求 -- 當前部件需要重畫。
?
AWT促使事件分派線程去調用部件的update() 方法。
注意: 在最初的重畫請求處理完成之前,如果在該部件上有多次對repaint()的調用,那么這些調用可以被合并成對update()的一次調用。決定什么時候應該合并多次請求的運算法則取決于具體的實現。如果多次請求被合并,最終被更新的區域將是所有這些請求所要求更新的區域的聯合(union)。
?
如果部件沒有覆蓋(override)update()方法,update()的默認實現會清除部件背景(如果部件不是“輕量級”),然后只是簡單地調用paint()方法。
因為作為默認的最終結果都是一樣的(paint()方法被調用),很多開發人員完全不知道一個分離的update() 方法的意義。確實,默認的update()的實現最終會轉回到對paint()方法的調用,然而,如果需要,這個更新操作的 "鉤子(hook)"可以使程根據不同的情況來處理程序觸發的繪畫。程序必須這么設想,對paint()的調用意味著Graphics的裁剪區"損壞"了并且必須全部重畫;然而對update()的調用沒有這種含義,它使程序做增量的繪畫。
如果程序希望只把要增加的內容敷蓋于已存在于該部件的像素位之上,那么就使用增量畫圖操作。UpdateDemo example 示范了一個利用update()的優點做增量繪畫的程序。
事實上,大多數GUI部件不需要增量繪畫,所有大部分程序可以忽略update()方法,并且簡單地覆蓋(override)paint()來呈現部件的當前狀態。這就意味著不管“系統觸發”還是“程序觸發”,在大多數部件上的表現從其本質上講是是等價的。
繪畫與輕量級部件
從應用開發人員的觀點看,“輕量級”的繪畫API基本上和“重量級”一樣(即,你只需要覆蓋paint()方法,同樣,調用repaint()方法去觸發繪圖更新)。然而,因為AWT的“輕量級”部件的框架全部使用普通Java代碼實現,在輕量級部件上繪畫機制的實現方式有一些微妙的不同。
?
“輕量級”部件是怎樣被繪制的
“輕量級”部件需要一個處在容器體系上的“重量級”部件提供進行繪畫的場所。當這個“重量級”的“祖宗”被告知要繪制自身的窗體時,它必須把這個繪畫的請求轉化為對其所有子孫的繪畫請求。這是由java.awt.Container的paint()方法處理的,該方法調用包容于其內的所有可見的、并且與繪畫區相交的輕量級部件的paint()方法。因此對于所有覆蓋了paint()方法的Container子類(“輕量級”或“重量級”)需要立刻做下面的事情:
?? public class MyContainer extends Container {
??????? public void paint(Graphics g) {
??? // paint my contents first...
??? // then, make sure lightweight children paint
??? super.paint(g);
??????? }
??? }
如果沒有super.paint(),那么容器(container)的輕量級子孫類就不會顯示出來(這是一個非常普遍的問題,自從JDK1.1初次引進“輕量級”部件之后)。
這種情況相當于注釋掉了默認的Container.update()方法的執行,從而不能 使用遞歸去調用其輕量級子孫類的update()或者paint()方法。這就意味著任何使用update()方法實現增量繪畫的重量級Container子類必須確保其輕量級子孫在需要時,能夠被它的遞歸操作所調用從而實現重畫。幸運的是,只有少數幾個重量級的容器(Container)需要增量繪圖,所以這個問題沒有影響到大多數的程序。
?
輕量級與系統觸發型的畫圖
為輕量級部件實現窗體行為(顯示、隱藏、移動、改變大小等)的輕量級框架的代碼全部用Java代碼寫成。經常的,在這些功能的Java實現中,AWT必須明確地吩咐各個輕量級部件執行繪畫(實質上講這也是系統觸發的繪畫,盡管它不是源于本地的 操作系統)。而輕量級框架使用repaint()方法來吩咐部件執行繪畫,這是我們前面解釋過的,將導致一個update()的調用而不是直接地對paint()的調用。因此,對于輕量級,系統觸發型的畫圖操作可以遵循下面的兩種途徑:
系統觸發的繪畫要求產生于本地系統(例如,輕量級的重量級祖先第一次現身的時候),這導致對paint()的直接調用。
?
系統觸發型的繪圖要求產生于輕量框架(例如,輕量級部件的尺寸改變了),這導致對update()的調用,該方法進而默認地調用paint()。
簡單地講,這意味著輕量級部件在update()和paint()之間沒有實質的差別,進一步講這又意味著“增量的繪圖技術”不能用到輕量級部件上。
輕量級部件與透明
因為輕量級部件"借用"了本屬于其“重量級”祖先的屏幕,所以它們支持“透明”的特征。這樣做是因為輕量級部件是從底往上繪畫,因此如果輕量級部件遺留一些或者全部它們祖先的像素位而沒有畫,底層的部件就會"直接顯示。"出來。這也是對于輕量級部件,update()方法的在默認實現將不再清除背景的原因。
LightweightDemo 例程示范了輕量級部件的透明特征。
"靈活巧妙地"繪畫方法
當AWT嘗試著使呈現部件的處理盡可能高效率時,部件自身paint()的實現可能對整體性能有重大的影響。影響這個處理過程的兩個關鍵領域是:
使用裁剪區來縮小需要呈現的范圍。
應用內部的版面布局信息來縮小對子部件的籠罩范圍(僅適用于輕量級).。
如果你的部件很簡單 -- 比如,如果是一個按鈕 -- 那么就不值得花費氣力去改善它的呈現屬性,使它僅僅去繪畫與修剪區相交的部分;不理會Graphics的裁剪區直接繪制整個部件反而更劃算。然而,如果你創建的部件界面很復雜,比如文本部件,那么迫切需要你的代碼使用裁剪信息來縮小需要繪圖的范圍。
更進一步講,如果你寫了一個容納了很多部件的復雜的輕量級容器,其中的部件和容器的布局管理器,或者只是容器的布局管理器擁有布局的信息,那么就值得使用所知道的布局信息來更靈活地確定哪個子部件需要繪畫。Container.paint()的默認實現只是簡單地按順序遍歷子部件,檢查它是否可見、是否與重換區域相交 -- 對于某幾個布局管理這種操作就顯得不必要的羅嗦。比如,如果容器在100*100的格子里布置部件,那么格子的信息就可以用來更快得確定這10,000個部件中哪個與裁剪框相交,哪個就確實需要繪制。
AWT繪畫準則
AWT為繪制部件提供了一個簡單的回調API。當你使用它是,要遵循下面的原則:
對于大多數程序,所有的客戶區繪畫代碼應該被放置在部件的paint()方法中。
?
通過調用repaint()方法,程序可以觸發一個將來執行的paint()調用,不能直接調用paint()方法。
?
對于界面復雜的部件,應該觸發帶參數的repaint()方法,使用參數定義實際需要更新的區域;而不帶參數調用會導致整個部件被重畫。
?
因為對repaint()的調用會首先導致update()的調用,默認地會促成paint()的調用,所以重量級部件應該覆蓋update()方法以實現增量繪制,如果需要的話(輕量級部件不支持增量繪制) 。
?
覆蓋了paint()方法的java.awt.Container子類應當在paint()方法中調用super.paint()以保證子部件能被繪制。
?
界面復雜的部件應該靈活地使用裁剪區來把繪畫范圍縮小到只包括與裁剪區相交的范圍。
在Swing中的繪畫
Swing起步于AWT基本繪畫模式,并且作了進一步的擴展以獲得最大化的性能以及改善可擴展性能。象AWT一樣,Swing支持回調繪畫以及使用repaint()促使部件更新。另外,Swing提供了內置的雙緩沖(double-buffering)并且作了改變以支持Swing的其它結構(象邊框(border)和UI代理)。最后,Swing為那些想更進一步定制繪畫機制的程序提供了RepaintManager API。
對雙緩沖的支持
Swing的最引人注目的特性之一就是把對雙緩沖的支持整個兒的內置到工具包。通過設置javax.swing.JComponent的"doubleBuffered"屬性就可以使用雙緩沖:
???? public boolean isDoubleBuffered()
??? public void setDoubleBuffered(boolean o)
當緩沖激活的時候,Swing的雙緩沖機制為每個包容層次(通常是每個最高層的窗體)準備一個單獨的屏外緩沖。并且,盡管這個屬性可以基于部件而設置,對一個特定的容器上設置這個屬性,將會影響到這個容器下面的所有輕量級部件把自己的繪畫提交給屏外緩沖,而不管它們各自的"雙緩沖"屬性值
默認地,所有Swing部件的該屬性值為true。不過對于JRootPane這種設置確實有些問題,因為這樣就使所有位于這個上層Swing部件下面的所有部件都使用了雙緩沖。對于大多數的Swing程序,不需要作任何特別的事情就可以使用雙緩沖,除非你要決定這個屬性是開還是關(并且為了使GUI能夠平滑呈現,你需要打開這個屬性)。Swing保證會有適宜的Graphics對象(或者是為雙緩沖使用的屏外映像的Graphics,或者是正規的Graphics)傳遞給部件的繪畫回調函數,所以,部件需要做的所有事情僅僅就是使用這個Graphics畫圖。本文的后面,在繪制的處理過程這一章會詳細解釋這個機制。
其他的繪畫屬性
為了改善內部的繪畫算法性能,Swing另外引進了幾個JComponent的相互有關聯的屬性。引入這些屬性為的是處理下面兩個問題,這兩個問題有可能導致輕量級部件的繪畫成本過高:
透明(Transparency): 當一個輕量級部件的繪畫結束時,如果該部件的一部分或者全部透明,那么它就可能不會把所有與其相關的像素位都涂上顏色;這就意味著不管它什么時候重畫,它底層的部件必須首先重畫。這個技術需要系統沿著部件的包容層次去找到最底層的重量級祖先,然后從它開始、從后向前地執行繪畫。
重疊的部件(Overlapping components): 當一個輕量級部件的繪畫結束是,如果有一些其他的輕量級部件部分地疊加在它的上方;就是說,不管最初的輕量級部件什么時候畫完,只要有疊加在它上面的其它部件(裁剪區與疊加區相交),這些疊加的部件必須也要部分地重畫。這需要系統在每次繪畫時要遍歷大量的包容層次,以檢查與之重疊的部件。
遮光性
?
在一般情況下部件是不透明的,為了提高改善性能,Swing增加了讀寫javax.swing.JComponent的遮光(opaque)屬性的操作:
??? public boolean isOpaque()
??? public void setOpaque(boolean o)
這些設置是:
true:部件同意在它的矩形范圍包含的里所有像素位上繪畫。
false:部件不保證其矩形范圍內所有像素位上繪畫。
遮光(opaque)屬性允許Swing的繪圖系統去檢測是否一個對指定部件的重畫請求會導致額外的對其底層祖先的重畫。每個標準Swing部件的默認(遮光)opaque屬性值由當前的視-感UI對象設定。而對于大多數部件,該值為true。
部件實現中的一個最常見的錯誤是它們允許遮光(opaque)屬性保持其默認值true,卻又不完全地呈現它們所轄的區域,其結果就是沒有呈現的部分有時會造成屏幕垃圾。當一個部件設計完畢,應該仔細的考慮所控制的遮光(opaque)屬性,既要確保透的使用是明智的,因為它會花費更多的繪畫時間,又要確保與繪畫系統之間的協約履行。
遮光(opaque)屬性的意義經常被誤解。有時候被用來表示“使部件的背景透明”。然而這不是Swing對遮光的精確解釋。一些部件,比如按鈕,為了給部件一個非矩形的外形可能會把“遮光”設置為false,或者為了短時間的視覺效果使用一個矩形框圍住部件,例如焦點指示框。在這些情況下,部件不遮光,但是其背景的主要部分仍然需要填充。
如先前的定義,遮光屬性的本質是一個與負責重畫的系統之間訂立的契約。如果一個部件使用遮光屬性去定義怎樣使部件的外觀透明,那么該屬性的這種使用就應該備有證明文件。(一些部件可能更合適于定義額外的屬性控制外觀怎樣怎樣增加透明度。例如,javax.swing.AbstractButton提供ContentAreaFilled屬性就是為了達到這個目的。)
另一個毫無價值的問題是遮光屬性與Swing部件的邊框(border)屬性有多少聯系。在一個部件上,由Border對象呈現的區域從幾何意義上講仍是部件的一部分。就是說如果部件遮光,它就有責任去填充邊框所占用的空間。(然后只需要把邊框放到該不透明的部件之上就可以了)。
如果你想使一個部件允許其底層部件能透過它的邊框范圍而顯示出來 -- 即,通過isBorderOpaque()判斷border是否支持透明而返回值為false -- 那么部件必須定義自身的遮光屬性為false并且確保它不在邊框的范圍內繪圖。
"最佳的"繪畫方案
部件重疊的問題有些棘手。即使沒有直接的兄弟部件疊加在該部件之上,也總是可能有非直系繼承關系(比如"堂兄妹"或者"姑嬸")的部件會與它交疊。這樣的情況下,處于一個復雜層次中的每個部件的重畫工作都需要一大堆的樹遍歷來確保'正確地'繪畫。為了減少不必要的遍歷,Swing為javax.swing.JComponent增加一個只讀的isOptimizedDrawingEnabled屬性:
??? public boolean isOptimizedDrawingEnabled()
這些設置是:
true:部件指示沒有直接的子孫與其重疊。
false: 部件不保證有沒有直接的子孫與之交疊。
通過檢查isOptimizedDrawingEnabled屬性,Swing在重畫時可以快速減少對交疊部件的搜索。
因為isOptimizedDrawingEnabled屬性是只讀的,于是部件改變默認值的唯一方法是在其子類覆蓋(override)這個方法來返回所期望的值。除了JLayeredPane,JDesktopPane,和JViewPort外,所有標準Swing部件對這個屬性返回true。
繪畫方法
適應于AWT的輕量級部件的規則同樣也適用于Swing部件 -- 舉一個例子,在部件需要呈現的時候就會調用paint() -- 只是Swing更進一步地把paint()的調用分解為3個分立的方法,以下列順序依次執行:
???? protected void paintComponent(Graphics g)
??? protected void paintBorder(Graphics g)
??? protected void paintChildren(Graphics g)
Swing程序應該覆蓋paintComponent()而不是覆蓋paint()。雖然API允許這樣做,但通常沒有理由去覆蓋paintBorder()或者paintComponents()(如果你這么做了,請確認你知道你到底在做什么!)。這個分解使得編程變得更容易,程序可以只覆蓋它們需要擴展的一部分繪畫。例如,這樣就解決先前在AWT中提到的問題,因為調用super.paint()失敗而使得所有輕量級子孫都不能顯示。
SwingPaintDemo例子程序舉例說明了Swing的paintComponent()回調方法的簡單應用。
繪畫與UI代理
大多數標準Swing部件擁有它們自己的、由分離的觀-感(look-and-feel)對象(叫做"UI代理")實現的觀-感。這意味著標準部件把大多數或者所有的繪畫委派給UI代理,并且出現在下面的途徑:
paint()觸發paintComponent()方法。
如果ui屬性為non-null,paintComponent()觸發ui.update()。
如果部件的遮光屬性為true,ui.udpate()方法使用背景顏色填充部件的背景并且觸發ui.paint()。
ui.paint()呈現部件的內容。
這意味著Swing部件的擁有UI代理的子類(相對于JComponent的直系子類),應該在它們所覆蓋的paintComponent方法中觸發super.paintComponent()。
??? public class MyPanel extends JPanel {
??????? protected void paintComponent(Graphics g) {
??? // Let UI delegate paint first
??? // (including background filling, if I'm opaque)
??? super.paintComponent(g);
??? // paint my contents next....
??????? }
??? }
如果因為某些原因部件的擴展類不允許UI代理去執行繪畫(是如果,例如,完全更換了部件的外觀),它可以忽略對super.paintComponent()的調用,但是它必須負責填充自己的背景,如果遮光(opaque)屬性為true的話,如前面在遮光(opaque)屬性一章講述的。
繪畫的處理過程
Swing處理"repaint"請求的方式與AWT有稍微地不同,雖然對于應用開發人員來講其本質是相同的 -- 同樣是觸發paint()。Swing這么做是為了支持它的RepaintManager API (后面介紹),就象改善繪畫性能一樣。在Swing里的繪畫可以走兩條路,如下所述:
(A) 繪畫需求產生于第一個重量級祖先(通常是JFrame、JDialog、JWindow或者JApplet):
事件分派線程調用其祖先的paint()
?
Container.paint()的默認實現會遞歸地調用任何輕量級子孫的paint()方法。
?
當到達第一個Swing部件時,JComponent.paint()的默認執行做下面的步驟:
如果部件的雙緩沖屬性為true并且部件的RepaintManager上的雙緩沖已經激活,將把Graphics對象轉換為一個合適的屏外Graphics。
調用paintComponent()(如果使用雙緩沖就把屏外Graphics傳遞進去)。
調用paintChildren()(如果使用雙緩沖就把屏外Graphics傳遞進去),該方法使用裁剪并且遮光和optimizedDrawingEnabled等屬性來嚴密地判定要遞歸地調用哪些子孫的paint()。
如果部件的雙緩沖屬性為true并且在部件的RepaintManager上的雙緩沖已經激活,使用最初的屏幕Graphics對象把屏外映像拷貝到部件上。
注意:JComponent.paint()步驟#1和#5在對paint()的遞歸調用中被忽略了(由于paintChildren(),在步驟#4中介紹了),因為所有在swing窗體層次中的輕量級部件將共享同一個用于雙緩沖的屏外映像。
(B) 繪畫需求從一個javax.swing.JComponent擴展類的repaint()調用上產生:
JComponent.repaint()注冊一個針對部件的RepaintManager的異步的重畫需求,該操作使用invokeLater()把一個Runnable加入事件隊列以便稍后執行在事件分派線程上的需求。
?
該Runnable在事件分派線程上執行并且導致部件的RepaintManager調用該部件上paintImmediately(),該方法執行下列步驟:
使用裁剪框以及遮光和optimizedDrawingEnabled屬性確定“根”部件,繪畫一定從這個部件開始(處理透明以及潛在的重疊部件)。
如果根部件的雙緩沖屬性為true,并且根部件的RepaintManager上的雙緩沖已激活,將轉換Graphics對象到適當的屏外Graphics。
調用根部件(該部件執行上述(A)中的JComponent.paint()步驟#2-4)上的paint(),導致根部件之下的、與裁剪框相交的所有部件被繪制。
如果根部件的doubleBuffered屬性為true并且根部件的RepaintManager上的雙緩沖已經激活,使用原始的Graphics把屏外映像拷貝到部件。
注意:如果在重畫沒有完成之前,又有發生多起對部件或者任何一個其祖先的repaint()調用,所有這些調用會被折疊到一個單一的調用 回到paintImmediately() on topmostSwing部件 on which 其repaint()被調用。例如,如果一個JTabbedPane包含了一個JTable并且在其包容層次中的現有的重畫需求完成之前兩次發布對repaint()的調用,其結果將變成對該JTabbedPane部件的paintImmediately()方法的單一調用,會觸發兩個部件的paint()的執行。
這意味著對于Swing部件來說,update()不再被調用。
雖然repaint()方法導致了對paintImmediately()的調用,它不考慮"回調"繪圖,并且客戶端的繪畫代碼也不會放置到paintImmediately()方法里面。實際上,除非有特殊的原因,根本不需要超載paintImmediately()方法。
同步繪圖
象我們在前面章節所講述的,paintImmediately()表現為一個入口,用來通知Swing部件繪制自身,確認所有需要的繪畫都能適當地產生。這個方法也可能用來安排同步的繪圖需求,就象它的名字所暗示的,即一些部件有時候需要保證它們的外觀實時地與其內部狀態保持一致(例如,在JScrollPane執行滾定操作的時候確實需要這樣并且也做到了)。
程序不應該直接調用這個方法,除非有合理實時繪畫需要。這是因為異步的repaint()可以使多個重復的需求得到有效的精簡,反之直接調用paintImmediately()則做不到這點。另外,調用這個方法的規則是它必須由事件分派線程中的進程調用;它也不是為能以多線程運行你的繪畫代碼而設計的。關于Swing單線程模式的更多信息,參考一起歸檔的文章"Threads and Swing."
RepaintManager
Swing的RepaintManager類的目的是最大化地提高Swing包容層次上的重畫執行效率,同時也實現了Swing的'重新生效'機制(作為一個題目,將在其它文章里介紹)。它通過截取所有Swing部件的重畫需求(于是它們不再需要經由AWT處理)實現了重畫機制,并且在需要更新的情況下維護其自身的狀態(我們已經知道的"dirty regions")。最后,它使用invokeLater()去處理事件分派線程中的未決需求,如同在"Repaint Processing"一節中描述的那樣(B選項).
對于大多數程序來講,RepaintManager可以看做是Swing的內部系統的一部分,并且甚至可以被忽略。然而,它的API為程序能更出色地控制繪畫中的幾個要素提供了選擇。
?
"當前的"RepaintManager
RepaintManager設計 is designed to be dynamically plugged, 雖然 有一個單獨的接口。下面的靜態方法允許程序得到并且設置"當前的"RepaintManager:
???? public static RepaintManager currentManager(Component c)
??? public static RepaintManager currentManager(JComponent c)
??? public static void
???????? setCurrentManager(RepaintManager aRepaintManager)
更換"當前的"RepaintManager
總的說來,程序通過下面的步驟可能會擴展并且更換RepaintManager:
??? RepaintManager.setCurrentManager(new MyRepaintManager());
你也可以參考RepaintManagerDemo ,這是個簡單的舉例說明RepaintManager加載的例子,該例子將把有關正在執行重畫的部件的信息打印出來。
擴展和替換RepaintManager的一個更有趣的動機是可以改變對重畫的處理方式。當前,默認的重畫實現所使用的來跟蹤dirty regions的內部狀態值是包內私有的并且因此不能被繼承類訪問。然而,程序可以實現它們自己的跟蹤dirty regions的機制并且通過超載下面的方法對重畫需求的縮減:
???? public synchronized void
????? addDirtyRegion(JComponent c, int x, int y, int w, int h)
??? public Rectangle getDirtyRegion(JComponent aComponent)
??? public void markCompletelyDirty(JComponent aComponent)
??? public void markCompletelyClean(JComponent aComponent) {
addDirtyRegion()方法是在調用Swing部件的repaint()的之后被調用的,因此可以用作鉤子來捕獲所有的重畫需求。如果程序超載了這個方法(并且不調用super.addDirtyRegion()),那么它改變了它的職責,而使用invokeLater()把Runnable放置到EventQueue ,該隊列將在合適的部件上調用paintImmediately()(translation: not for the faint of heart).
從全局控制雙緩沖
RepaintManager提供了從全局中激活或者禁止雙緩沖的API:
???? public void setDoubleBufferingEnabled(boolean aFlag)
??? public boolean isDoubleBufferingEnabled()
這個屬性在繪畫處理的時候,在JComponent的內部檢查過以確定是否使用屏外緩沖顯示部件。這個屬性默認為true,但是如果程序希望在全局范圍為所有Swing部件關閉雙緩沖的使用,可以按照下面的步驟做:
??? RepaintManager.currentManager(mycomponent).
????????????????? setDoubleBufferingEnabled(false);
注意:因為Swing的默認實現要初始化一個單獨的RepaintManager實例,mycomponent參數與此不相關。
Swing繪畫準則
Swing開發人員在寫繪畫代碼時應該理解下面的準則:
對于Swing部件,不管是系統-觸發還是程序-觸發的請求,總會調用paint()方法;而update()不再被Swing部件調用。
?
程序可以通過repaint()觸發一個異步的paint()調用,但是不能直接調用paint()。
?
對于復雜的界面,應該調用帶參數的repaint(),這樣可以僅僅更新由該參數定義的區域;而不要調用無參數的repaint(),導致整個部件重畫。
?
Swing中實現paint()的3個要素是調用3個分離的回調方法:
paintComponent()
paintBorder()
paintChildren()
Swing部件的子類,如果想執行自己的繪畫代碼,應該把自己的繪畫代碼放在paintComponent()方法的范圍之內。(不要放在paint()里面)。
?
Swing引進了兩個屬性來最大化的改善繪畫的性能:
opaque: 部件是否要重畫它所占據范圍中的所有像素位?
optimizedDrawingEnabled: 是否有這個部件的子孫與之交疊?
?
如故Swing部件的(遮光)opaque屬性設置為true,那就表示它要負責繪制它所占據的范圍內的所有像素位(包括在paintComponent()中清除它自己的背景),否則會造成屏幕垃圾。
把一個部件設置為遮光(opaque)同時又把它的optimizedDrawingEnabled屬性設置為false,將導致在每個繪畫操作中要執行更多的處理,因此我們推薦的明智的方法是同時使用透明并且交疊部件。
使用UI代理(包括JPanel)的Swing部件的擴展類的典型作法是在它們自己的paintComponent()的實現中調用super.paintComponent()。因為UI代理可以負責清除一個遮光部件的背景,這將照顧到#5.
Swing通過JComponent的doubleBuffered屬性支持內置的雙緩沖,所有的Swing部件該屬性默認值是true,然而把Swing容器的遮光設置為true有一個整體的構思,把該容器上的所有輕量級子孫的屬性打開,不管它們各自的設定。
強烈建議為所有的Swing部件使用雙緩沖。
界面復雜的部件應該靈活地運用剪切框來,只對那些與剪切框相交的區域進行繪畫操作,從而減少工作量。
總結
不管AWT還是Swing都提供了方便的編程手段使得部件內容能夠正確地顯示到屏幕上。雖然對于大多數的GUI需要我們推薦使用Swing,但是理解AWT的繪畫機制也會給我們帶來幫助,因為Swing建立在它的基礎上。
關于AWT和Sing的特點就介紹到這里,應用開發人員應該盡力按照本文中介紹的準則來撰寫代碼,充分發揮這些API功能,使自己的程序獲得最佳性能。
?