技術改變世界

           

          Swing第三刀:做套ERP,要配得上我的登錄界面!

          在《Swing第一刀》和《Swing第二刀》中提到了一個我正在折騰的一個ERP界面小框架,不少童鞋表示灰常感興趣,這里繼續和大家分享一個新的小進展:一個全新的登錄界面。也許可以改變一些你對Swing和UI設計的看法。

          為啥又是登錄界面

          登錄界面是一個軟件系統中最簡單的一個界面,也是最重要的一個界面。為什么?因為它是用戶看到的第一個界面;因為它是用戶每天都要看的界面。要想讓用戶愛上你的軟件,一個美觀、耐看、友好的登錄界面是一個重要的前提。下功夫做一個好的登錄界面,你的系統就成功了一半。這就是為什么很多美工初學者總是喜歡從模仿、創作登錄界面開始。

          不信咱仔細瞅瞅。瞧,這是你做的登錄界面:



          抓圖來自網絡,版權歸作者所有

          瞧,這是我的登錄界面(來自本文案例):



          美工設計效果圖



          程序實際運行圖

          哪個更討人喜歡?你是鳳姐,我是志玲姐;你是芙蓉姐姐,我是Lady Gaga姐,不大好比哦。嗯,正如你所說,是“各有所長”吧!不過,除非你有嚴重的自虐傾向,否則你的選擇應當跟我一樣。

          估計有人已經在叫了:“你這是Swing做的?”“用JNI了?”“這透明圓角,這透明文字,這陰影…咋弄的?”“快扔源碼!”“Flex做這個太容易了”“花里胡哨有P用?”…你看,程序員的“老毛病”又來了。

          不錯,這個界面是Swing做的;也沒有JNI(從來也沒整過JNI);代碼也極其簡單,就是窗戶紙一捅就破;也談不上多么高深技術;也無意和Flex、.NET之類的技術相比較。我只是想讓大家更多的了解Java和Swing的能力,以及我們對待技術和UI的態度。如果急吼吼的看一下代碼run一下demo “哦!”一下,繼續扭頭堆代碼,那我就算是白費這勁了。

          Swing變冷了,還是變熱了

           

          JavaEye上最近關于Swing的帖子絡繹不絕,不少人驚呼:Swing似乎難道又要熱了?面對這樣的疑問,國內著名Java專家、《程序員》雜志特約撰稿人、美國密蘇里州立大學計算機科學與技術學院客座教授、JavaEye創始人Robbin卻表現的很坦然,他告訴我說“對于我們Swing程序員來說,這是很正常的事情。”在Robbin看來,這是再正常不過的現象了。“Swing本來就很強大, 而最近頻頻出現的熱帖也不是因為Swing發展加快了,只是媒體關注變多了。我使用Swing已經30多年了,在我上大學讀書開始就接觸Swing,”Robbin說,“毫無疑問,我相信Swing會取得成功”。“美洲大陸已經存在上億年了,只是哥倫布發現了它而已。所以,不必驚呼新大陸很美很新鮮,只能怪我們很傻很無知。”

          (注:以上訪談純屬自編自導個人杜撰,如有雷同,純屬巧合。)

          Swing做界面,是快刀還是鈍刀

           

          用Swing做一個上面的登錄界面,需要多少時間?答絕不可能是幾分鐘。而要做到“技驚四座、一鳴驚人”,用“鬼斧神工”搬的精致來徹底討好和吸引用戶,就必須下足功夫。這往往不是程序員一個人在戰斗!美工師、程序員、熟悉業務的系統工程師…需要一起雕章琢句、廢寢忘食。然而,好的設計帶來的回報也會遠遠超過你的想象。所以,討論所謂**技術更“快”和更“慢”毫無意義,當初快如閃電的PB、Delphi如今也難覓蹤跡。所以,程序的價值和回報不在于速度,更多的在于我們的態度:精雕細琢成大器,還是粗制濫造堆垃圾呢?

          程序員:我們并不在意程序是否好看

           

          真的嗎?你真的這樣看嗎?真是很可怕的一件事情。當然,在Java領域里,只要一提到J2EE、企業應用,大家就會立刻想到業務、重構、集群、云計算、松耦合、SOA、線程池、EJB、JPA、Hibernate、Spring、JBPM、SSH、JBoss、Lucene、Seam數之不盡的buzzword和framework。很多程序員浸淫在各種框架之中樂此不疲、無法自拔,甚至從來沒有寫下過例如“public class MyClass extends *** implements ***”親手create過一個自己的class,實在是挺可怕。軟件說到底是拿來“用”的,不是用來“學”的。再多的框架和技術,最終還是要為用戶服務,和用戶交互就要有好的UI。UI無非就是User Interface,是一切between在用戶和機器之間的東西。忽視UI技術和UI設計,確實對一個程序員的成長是不利的。

          程序員:我們需要左腦還是右腦

           

          美國的斯佩里和日本角田等人的研究表明,左腦支配右半身的神經和感覺,是理解語言的中樞,主要完成語言、邏輯、分析、代數的思考認識和行為,它是進行有條不紊的條理化思維,即邏輯思維,是程序員的典型活躍區域,也是我們的“國家級重點保護區”。喜歡踢足球的程序員們注意了:盡量不要頭球;用也要盡量用右腦;用左腦最好戴個摩托頭盔先,別把吃飯的家什整壞了。右腦支配左半身的神經和感覺,是沒有語言中樞的啞腦,但有接受音樂的中樞,主要負責可視的、綜合的、幾何的、繪畫的思考認識和行為,也就是負責鑒賞繪畫、觀賞自然風光、欣賞音樂,憑直覺觀察事物,縱觀全局,把握整體。右腦具有類別認識能力、圖形認識、空間認識、繪畫認識、形象認識能力,是形象思維。看上去右腦似乎也恰恰是咱們程序員的軟肋。但這能很好的解釋為什么程序員彈吉他蓬蓬作響卻像彈棉花,拉二胡一身汗還是像驢叫,跟大媽吵架被罵的插不上嘴,這不能怨我們自己,原來是有生理原因的。

          但是從事邏輯思維的程序員恰恰還需要有很強的創造力。而右腦在創造性工作中具有不可替代的作用。美國科學家在《思維的藝術》一書中,將創造過程分為四個階段,即準備階段、醞釀階段、閃光階段和驗證階段。這其中,直覺和頓悟是創造的泉源,但是它必須經過語言的描述和邏輯的檢驗才具有價值。左右腦的這種協同關系是創造力的真正基礎和源泉。如果我們一味的抱怨右腦發育不良,甚至自認右腦天生腦殘,那就只能一生一世做一個不折不扣的coder編碼機器了。

          勤奮和聰明,哪個更重要

           

          這個話題有點大。作為程序員,不管缺了哪樣,都是致命的,還不如早點放棄這條路的好。這論調似乎很沒新意,如果非要選擇一個的話,我選擇勤奮。不聰明,哪怕做不了程序員還可以干其他的嘛,比如當個官員、干個城管、踢個足球啥的(僅限國足,男隊)。但要是不勤奮,恐怕就一事無成了,想做貪官都難(不勤奮你以為就能貪的到啊)。勤奮又聰明,再加上那么一點點激情,你就厲害了!

          細節是魔鬼:登錄還是登陸?

           

          這真是一個問題。Google一下看,很多人都在爭論和分析是“登陸”還是“登錄”,這似乎不僅僅是一個軟件問題,而變成了一個語言學術研究問題。《北京晚報》還在2007年5月專門發表解青的署名文章《“登陸網站”還是“登錄網站”》進行深入的分析。經過一系列復雜的公式推導邏輯運算,作者最終得出的結論是應當使用“登陸網站”。我們程序員語言能力一向很差,雖然“登陸網站”還說得過去,但是軟件的login界面如果用“登陸”那我們就會感覺有點點怪了,總讓人想起彪炳史冊的偉大的“諾曼底”這個地方。毫無疑問,“登錄”是更正確的選擇。別說程序員不用這樣較真,別說這個跟我程序員無關。如果哪個大型軟件的login界面用“登陸”兩個字,請你立刻鄙視它、嘲笑它、諷刺它、挖苦它、打擊它、永遠別買它、永遠離開它,離啊離開它,離~開~它!不服氣現在就重啟系統看看Windows界面用的什么,去Google一下主流軟件抓圖用的是什么。如果你做的軟件真的由此而不能賣出去,此事也與你無關嗎?細節決定成敗,細節就是魔鬼,這不是口號。你不提防它,它就吃掉你。

          Swing與不規則窗體的“哥德巴赫猜想”

           

          不管你信不信,“如何用Swing做不規則窗體”是近10年來Swing被最常問到的幾個問題之一。Google上搜索“Swing 不規則 窗體”有將近10萬個中文結果,而且從1999年就有人在致力于這一問題的解決方法,至今甚至已經出現多種不同“流派”的解決方案。

          第一招:抓屏法


          最早出現的比較靠譜的一個解決方法出現在《Swing Hacks》一書中。《Swing Hacks》一書是著名的圖書出版商O’Reilly在2007年出版的Swing技巧圖書,作者是Swing開發組成員Joshua Marinacci,他也是目前JavaFX開發組成員。在這本書的41章中介紹了一種制作透明窗體和不規則窗體的方法。由于當時的JDK功能所限,Java本身并未提供任何直接的API來制作半透明或不規則窗體,所以這一技巧是利用Robot來截取屏幕原有內容生成內存圖片,然后將圖片顯示在Swing的窗體中作為背景,“欺騙”大家的眼睛誤以為是窗口“透”過去了。這一招確實體現了程序員的聰明才智。

          不過這一方法的缺陷是,當窗口被移動一下后就會露餡,所謂“透明”區域的抓圖不知道位置變化,也不會隨之改變。所以在該書的例子程序中,作者又將JFrame的setUndecorated設置為true去掉了標題欄,讓你無法移動窗口;再啟動了一個線程,每250毫秒就重新抓屏一次,更新Swing窗口的圖片背景。不過由于Swing窗口顯示出來后,它本身又遮擋了屏幕后面的物體,作者只好先frame.hide()把窗口隱藏一下,然后馬上抓圖,然后再frame.show恢復窗口顯示。透明效果是勉強出來了,但是程序在那里有事沒事的一直忽隱忽現,真是夠怪異的,效率、實時性也都慘不忍睹。

          以下是相關代碼:

           1 public   void  run( )  {  
           2
           3       try   {  
           4
           5           while ( true {  
           6
           7              Thread.sleep( 250 );  
           8
           9               long  now  =   new  Date( ).getTime( );  
          10
          11               if (refreshRequested  & amp; & amp;  
          12
          13                  ((now  -  lastupdate)  & gt;  1000 ))  {  
          14
          15                   if (frame.isVisible( ))  {  
          16
          17                      Point location  =  frame.getLocation( );  
          18
          19                      frame.hide( );  
          20
          21                      updateBackground( );  
          22
          23                      frame.show( );  
          24
          25                  frame.setLocation(location);  
          26
          27                      refresh( );  
          28
          29                  }
            
          30
          31                  lastupdate  =  now;  
          32
          33                  refreshRequested  =   false ;  
          34
          35                  }
            
          36
          37              }
            
          38
          39          }
            catch  (Exception ex)  {  
          40
          41              p(ex.toString( ));  
          42
          43              ex.printStackTrace( );  
          44
          45          }
            
          46
          47      }
           
          48

          《Swing Hacks》中介紹的方法代碼片段

          第二招:AWTUtilities.setWindowShape法


          隨著Sun公司對JavaFX技術的猛醒和大力持續投入,JDK從6就開始從底層為JavaFX的未來做好準備,提供更多底層功能支撐。作為“酷”、“炫”的UI技術的先鋒,窗口透明、不規則窗口自然是將來JavaFX不可缺少的元素和特性。所以,Sun在JDK6中提供了幾個新的函數,用來支持窗口透明度、窗口任意形狀:

          1 void  setWindowOpacity(Window window,  float  opacity)   // 設置窗口透明度  
          2 void  setWindowShape(Window window, Shape shape)    // 設置窗口形狀 

          這里有官方的具體介紹:

          http://java.sun.com/developer/technicalArticles/GUI/translucent_shaped_windows/

          setWindowOpacity方法提供了官方的、徹底的方法來生成不規則形狀窗體。不過依舊有以下幾個問題:

                  1,  AWTUtilities并非JDK公開類,將來可能會發生變化。當然除了編譯時的一個不爽的警告外,也不用過度擔心,即使將來API發生變化,相信Sun和Oracle也會妥善處理好。

                  2, 用Shape形狀定義的窗口邊緣粗糙,顯示效果差。使用setWindowShape函數對窗口設定形狀后,其窗口切割的邊緣并未做抗鋸齒(anti-alias)處理,也沒有相應的函數或參數進行控制,導致顯示效果粗糙。看看Sun自己做出來的例子: http://java.sun.com/developer/technicalArticles/GUI/translucent_shaped_windows/9.jpg ,一個簡單的橢圓Shape,其邊緣就已經粗糙不堪。更不用說更復雜的透明圖片邊緣了。本人經過好幾天的反復嘗試,發現其效果始終不甚理想(如圖),無論對圖片的透明邊緣如何精細處理,甚至直接new Shape,都完全達不到美工設計出來的效果圖。

                  3, 透明PNG圖片的邊緣Shape不好獲取。如果我們的窗體不是一個規則的、可定義的幾何形狀Shape,而是一個任意透明PNG圖片,該如何獲取圖片的透明邊緣Shape,進而設置window的不規則形狀呢?這確實是一個難題。在網上有人專門討論這一算法,基本上是讀取PNG圖片的每一個像素,獲得像素透明邊界點,對邊界點進行不斷的合并與逼近,最后形成一個最終Shape。TWaver的TWaverUtil工具類中就有一個getImageShape方法用來獲得任意圖片的邊緣shape。經反復測試驗證,就是采用了這種算法。不過這種算法的缺點很明顯:邊緣必須是連續的,甚至必須是“外凸”的;如果png圖片中間有一個透明的“洞”,甚至邊緣有一個凹陷透明區域,生成的Shape都無法準確反映出來。

          第三招:終極解決之道



          經過反復的研究探索,終于獲得了一個完美的解決方法:不用shape、不用抓圖、不用workaround,真正的、徹底的、完全的、隨意的在桌面上任意繪圖、涂鴉、撒野,真正的屬于程序員的Freedom!下面就來一起揭開這層窗戶紙吧!

          在程序中依次設置以下幾個參數:

                   設置窗口完全透明:AWTUtilities.setWindowOpaque(frame, false);

                   設置窗口無邊緣:frame.setUndecorated(true);

                   設置窗口的ContentPane為要顯示的Pane:frame.setContentPane(myPane);

                   在myPane中放置具體要顯示的內容,也可以重載paint方法進行Java2D繪制。這些paint會直接發生在桌面背景上。

          接下來,就是見證奇跡的時刻!



          (不好意思,暴露我的桌面了)

          通過上面方法,可以做一個任意大小、任意位置的window,在相應的桌面位置上隨意顯示Swing組件,或做任意Java2D畫圖。比如下面小例子可以在屏幕上直接畫一個紅色的立體矩形,而沒有顯示窗口:

           1 import com.sun.awt.AWTUtilities;  
           2
           3 import java.awt.Color;  
           4
           5 import java.awt.Graphics;  
           6
           7 import javax.swing.JFrame;  
           8
           9 import javax.swing.JPanel;  
          10
          11    
          12
          13 public class Test {  
          14
          15    
          16
          17     public static void main(String[] args) {  
          18
          19         JFrame frame = new JFrame();  
          20
          21         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
          22
          23         frame.setUndecorated(true);  
          24
          25         frame.setBounds(500500300300);  
          26
          27         AWTUtilities.setWindowOpaque(frame, false);  
          28
          29    
          30
          31         JPanel pane = new JPanel() {  
          32
          33    
          34
          35             @Override 
          36
          37             public void paint(Graphics g) {  
          38
          39                 super.paint(g);  
          40
          41    
          42
          43                 g.setColor(Color.red);  
          44
          45                 g.fill3DRect(1010100100true);  
          46
          47             }
            
          48
          49         }
          ;  
          50
          51    
          52
          53         frame.setContentPane(pane);  
          54
          55    
          56
          57         frame.setVisible(true);  
          58
          59     }
            
          60
          61 }
           
          62

          運行效果如下圖:


          窗口的拖拽移動

          窗口不再規則,窗口標題欄不再出現,如何移動窗口?按照其他類似軟件的習慣做法,應當允許用鼠標直接拖拽窗體任意位置進行窗口移動。做一個鼠標監聽器對窗口中的元素進行拖動監聽,對窗口進行相應拖動距離的移動:

           1private MouseAdapter moveWindowListener = new MouseAdapter() {  
           2
           3    
           4
           5         private Point lastPoint = null;  
           6
           7    
           8
           9         @Override 
          10
          11         public void mousePressed(MouseEvent e) {  
          12
          13             lastPoint = e.getLocationOnScreen();  
          14
          15         }
            
          16    
          17
          18         @Override 
          19
          20         public void mouseDragged(MouseEvent e) {  
          21
          22             Point point = e.getLocationOnScreen();  
          23
          24             int offsetX = point.x - lastPoint.x;  
          25
          26             int offsetY = point.y - lastPoint.y;  
          27
          28             Rectangle bounds = FreeLoginUI.this.getBounds();  
          29
          30             bounds.x += offsetX;  
          31
          32             bounds.y += offsetY;  
          33
          34             FreeLoginUI.this.setBounds(bounds);  
          35
          36             lastPoint = point;  
          37
          38         }
            
          39
          40 }

          41

          對窗體上的組件安裝這一listener,就可以對窗口中任意元素進行拖拽,直接拖動窗體四處晃悠了。

          圖片的切割

          要做好的界面,需要一個耐心、有創意的美工大力協助,例如圖片的切割就很重要。下圖展示了如何從效果圖進行具體切割素材:

          制作漸變背景Panel

           

          仔細觀察中間的輸入區域部分,其背景是有漸變設計的。其制作方法也很簡單:首先讓美工幫助制作一個一個像素寬、整個panel高度的小圖片作為素材;然后用這個圖片創建紋理Paint;最后用這個紋理對真個panel進行fill。


           1private JPanel inputPane = new JPanel() {  
           2
           3    
           4
           5         private String backgroundImageURL = FreeUtil.getImageURL("login_background.png");  
           6
           7         private TexturePaint paint = FreeUtil.createTexturePaint(backgroundImageURL);  
           8
           9    
          10
          11         @Override 
          12
          13         protected void paintComponent(Graphics g) {  
          14
          15             super.paintComponent(g);  
          16
          17             Graphics2D g2d = (Graphics2D) g;  
          18
          19             g2d.setPaint(paint);  
          20
          21             g2d.fillRect(00this.getWidth(), this.getHeight());  
          22
          23         }
            
          24
          25     }

          26

          肆虐你的桌面:六月飄雪!

          既然窗戶紙捅破了,在桌面上就隨意折騰吧。這幾天窗外酷熱難耐,咱們就來個桌面飄雪,也許可以望梅止渴,帶來絲絲清涼吧!

          先準備一個雪花的png透明圖片,然后在桌面上隨機生成50個雪花坐標,每次paint讓每個雪花的左右略微抖一下(snowX[i] += TWaverUtil.getRandomInt(5) – 3),垂直距離下墜5像素(snowY[i] += 5),再旋轉個小角度。然后,用一個線程不停的repaint窗口。

          雪花png圖片:

            1public class TestSnow {  
            2
            3    
            4     public static void main(String[] args) {  
            5
            6         final JFrame frame = new JFrame();  
            7
            8         frame.setAlwaysOnTop(true);  
            9
           10         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
           11
           12         frame.setUndecorated(true);  
           13
           14         frame.setExtendedState(JFrame.MAXIMIZED_BOTH);  
           15
           16         AWTUtilities.setWindowOpaque(frame, false);  
           17
           18    
           19
           20         final JPanel pane = new JPanel() {  
           21
           22    
           23
           24             private int[] snowX = null;  
           25
           26             private int[] snowY = null;  
           27
           28             private int[] angles = null;  
           29
           30             private int count = 50;  
           31
           32    
           33
           34             @Override 
           35
           36             public void paint(Graphics g) {  
           37
           38                 super.paint(g);  
           39
           40                 Rectangle bounds = frame.getBounds();  
           41
           42                 if (snowX == null{  
           43
           44    
           45
           46                     snowX = new int[count];  
           47
           48                     for (int i = 0; i < snowX.length; i++{  
           49
           50                         snowX[i] = TWaverUtil.getRandomInt(bounds.width);  
           51
           52                     }
            
           53
           54                     snowY = new int[count];  
           55
           56                     for (int i = 0; i < snowY.length; i++{  
           57
           58                         snowY[i] = TWaverUtil.getRandomInt(bounds.height);  
           59
           60                     }
            
           61
           62                     angles = new int[count];  
           63
           64                     for (int i = 0; i < snowY.length; i++{  
           65
           66                         angles[i] = TWaverUtil.getRandomInt(360);  
           67
           68                     }
            
           69
           70                 }
            
           71
           72    
           73
           74                 Graphics2D g2d = (Graphics2D) g;  
           75
           76                 Image image = TWaverUtil.getImage("/free/test/snow.png");  
           77
           78                 for (int i = 0; i < count; i++{  
           79
           80                     snowX[i] += TWaverUtil.getRandomInt(5- 3;  
           81
           82                     snowY[i] += 5;  
           83
           84                     angles[i] += i / 5;  
           85
           86                     snowY[i] = snowY[i] > bounds.height ? 0 : snowY[i];  
           87
           88                     angles[i] = angles[i] > 360 ? 0 : angles[i];  
           89
           90                     int x = snowX[i];  
           91
           92                     int y = snowY[i];  
           93
           94                     int angle = angles[i];  
           95
           96                     g2d.translate(x, y);  
           97
           98                     double angleValue = Math.toRadians(angle);  
           99
          100                     g2d.rotate(angleValue);  
          101
          102                     g2d.drawImage(image, 00null);  
          103
          104                     g2d.rotate(-angleValue);  
          105
          106                     g2d.translate(-x, -y);  
          107
          108                 }
            
          109
          110             }
            
          111
          112         }
          ;  
          113
          114    
          115
          116         frame.setContentPane(pane);  
          117
          118         frame.setVisible(true);  
          119
          120         Thread thread = new Thread() {  
          121
          122    
          123
          124             @Override 
          125
          126             public void run() {  
          127
          128                 while (true{  
          129
          130                     try {  
          131
          132                         Thread.sleep(10);  
          133
          134                     }
           catch (Exception ex) {  
          135
          136                         ex.printStackTrace();  
          137
          138                     }
            
          139
          140                     pane.repaint();  
          141
          142                 }
            
          143
          144             }
            
          145
          146         }
          ;  
          147
          148    
          149
          150         thread.start();  
          151
          152     }
            
          153
          154 }
           
          155


          快快運行代碼,讓雪花飄起來吧!


          如果愿意折騰,還可以修改代碼中的:

                   private int count = 50,調整雪花的數量; 
                   修改angles[i] += i / 5,調整雪花翻滾的速度; 
                   修改snowY[i] += 5,調整雪花下墜的速度; 
                   修改snowX[i] += TWaverUtil.getRandomInt(5) – 3,調整雪花左右擺動的速度;

          別說你不知道怎么結束程序啊,不會Alt+F4的話,你這個程序員肯定不合格了。

          秘密背后的秘密

          當把透明窗口Frame設置特別大以后(例如10000*10000),你會發現不但界面變得極其緩慢,而且還會內存溢出。Sun的秘密不言自明了:還是使用了BufferedImage。否則,鼠標點擊你畫的橢圓或桌面的圖標,它如何知道是點擊了窗體,還是操作了桌面?只能生成內存圖片,在里面進行像素判斷了。要挖掘再深入的秘密,我也不清楚了,自己繼續探索吧!

          代碼大家接著到我之前發表的TWaver中文社區下載吧。

          posted on 2010-08-06 19:29 老三 閱讀(5963) 評論(0)  編輯  收藏

          導航

          公告

          一個不錯的UI技術中文社區:http://twaver.servasoft.com/
          還有一個論壇:http://twaver.servasoft.com/forum/

          常用鏈接

          隨筆檔案

          最新隨筆

          搜索

          最新評論

          主站蜘蛛池模板: 潞西市| 涿州市| 西安市| 津南区| 常州市| 吉木萨尔县| 敦化市| 凤庆县| 绥化市| 阜南县| 龙州县| 柳江县| 巴青县| 巧家县| 留坝县| 普定县| 封开县| 资溪县| 临湘市| 花垣县| 襄垣县| 伊吾县| 紫阳县| 南昌县| 岳普湖县| 黄陵县| 建平县| 库车县| 会同县| 西丰县| 大方县| 乌拉特后旗| 托克逊县| 虞城县| 哈密市| 高平市| 包头市| 南雄市| 汉川市| 金堂县| 泽库县|