TWaver - 專注UI技術

          http://twaver.servasoft.com/
          posts - 171, comments - 191, trackbacks - 0, articles - 2
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          用Swing定制流動的Link樣式

          Posted on 2012-07-27 16:52 TWaver 閱讀(1287) 評論(1)  編輯  收藏
          想想Java2D中給我們提供的線的樣式著實很少,除了直線,虛線,好像就沒有其他的什么樣式了,如果細心的童鞋還會發現,TWaver中倒是提供了一種比較特殊的連線,波浪曲折式的連線。

          這種波浪曲折的連線如果讓我們自己來實現也是有多種實現的方式,還記得之前幾篇文章中定制過的LinkUI么,也是各式各樣的方式,比如:

          五彩斑斕的Link



          流動點式的Link


          今天給大家介紹的是箭頭流動式的Link,何為箭頭流動,我們就先來看看效果圖:

          這是一個從from節點流向to節點的連線,連線是以一個一個箭頭組建而成,這樣的連線方式看上去比傳統的那種流動漂亮多了,也有不少客戶提及到這種樣式。本篇我將詳細給大家講解一下實現的細節。

          首先需要定制一個ArrowLink繼承于Link,在ArrowLink中需要給它定義幾個變量,例如:線的寬度、顏色、每段箭頭的長度、需要填充的流動箭頭的數量、透明度、是否是從from流向to以及偏移(用于顯示流動)等等。 不說這么多了,直接看代碼:

           1 public class ArrowLink extends Link {
           2 
           3     public ArrowLink() {
           4         super();
           5         init();
           6     }
           7 
           8     public ArrowLink(Object id) {
           9         super(id);
          10         init();
          11     }
          12 
          13     public ArrowLink(Node from, Node to) {
          14         super(from, to);
          15         init();
          16     }
          17 
          18     public ArrowLink(Object id, Node from, Node to) {
          19         super(id, from, to);
          20         init();
          21     }
          22 
          23     private void init() {
          24         this.putLinkColor(new Color(0, 0, 0, 0));
          25         this.putLinkOutlineWidth(0);
          26         this.setLinkType(TWaverConst.LINK_TYPE_PARALLEL);
          27         this.putLinkAntialias(true);
          28         this.putClientProperty("lineWidth", 3.0f);
          29         this.putClientProperty("lineColor", Color.blue);
          30         this.putClientProperty("offset", 0.0);
          31         this.putClientProperty("segmentLength", 8.0);
          32         this.putClientProperty("fillSegmentCount", 5);
          33         this.putClientProperty("defaultAlpha", 0.2);
          34         this.putClientProperty("from", true);
          35     }
          36 
          37     public String getUIClassID() {
          38            return ArrowLinkUI.class.getName();
          39         }
          40 
          41 }

          定制完連線之后,最主要的是需要重畫LinkUI,自定義ArrowLinkUI類繼承于LinkUI并重載paintBody方法,paintBody中我們需要畫出一個一個的箭頭,箭頭實現起來其實還是比較簡單的,我們能獲取Link的長度并且知道Link上每段的長度,就可以計算出需要繪制箭頭的數量。

          1 int count = (int)(length/segmentLength);

          根據箭頭的數量可以獲取到需要繪制箭頭的每個點的位置:

          1 List points = TWaverUtil.divideShape(this.path, count);

          獲取到這個位置之后,我們就可以以這個點為中心點,分別計算出箭頭的其他兩個點的位置:

          1 Point2D p0 = new Point.Double();
          2 transform.transform(point, p0);
          3 Point2D p1 =  new Point.Double();
          4 transform.transform(new Point.Double(point.getX() + segmentLength/2 * sign, point.getY() - segmentLength/2), p1);
          5 Point2D p2 =  new Point.Double();
          6 transform.transform(new Point.Double(point.getX() + segmentLength/2 * sign, point.getY() + segmentLength/2), p2);

          這樣一個箭頭就可以繪制出來了

          其他的箭頭也可以以同樣方式循環繪制出來,需要注意的是箭頭是需要隨著node的位置旋轉的,因此我們需要計算出箭頭旋轉的角度和旋轉點的位置:

          1 AffineTransform transform = new AffineTransform();
          2 transform.translate(point.getX(), point.getY());
          3 transform.rotate(angle);
          4 transform.translate(-point.getX(), -point.getY());

          最后還有流動的效果,這里我們設置了一個offset的參數,可以表示流動的偏移量,根據偏移量以及填充的流動箭頭的數量來確定當前這個箭頭的透明度:

          1 double alpha = (Double)this.element.getClientProperty("defaultAlpha");
          2 if(offset * count >= i && offset * count - fillSegmentCount <= i){
          3     alpha = 1 - (offset * count - i)/fillSegmentCount * 0.5;
          4 }

          完整繪制箭頭的代碼如下:

           1 public class ArrowLinkUI extends LinkUI {
           2 
           3     public ArrowLinkUI(TNetwork network, Link link) {
           4         super(network, link);
           5     }
           6 
           7     public void paintBody(Graphics2D g2d) {
           8         super.paintBody(g2d);
           9         this.drawFlowing(g2d);
          10     }
          11 
          12     private void drawFlowing(Graphics2D g2d) {
          13         double length = TWaverUtil.getLength(this.path);
          14         if(length < =0 ){
          15             return;
          16         }
          17 
          18         double segmentLength = (Double)this.element.getClientProperty("segmentLength");
          19         int count = (int)(length/segmentLength);
          20         List points = TWaverUtil.divideShape(this.path, count);
          21         if(points.size() < 2){
          22             return;
          23         }
          24         int fillSegmentCount = (Integer)this.element.getClientProperty("fillSegmentCount");
          25         double offset = (Double)this.element.getClientProperty("offset");
          26         Color lineColor = (Color)this.element.getClientProperty("lineColor");
          27         boolean from = (Boolean)this.element.getClientProperty("from");
          28         boolean fromLeft = this.getFromPoint().x <= this.getToPoint().x;
          29 
          30         g2d.setStroke(new BasicStroke((Float)this.element.getClientProperty("lineWidth")));
          31         for(int i=0; i
          32             Point2D point = (Point2D)points.get(i);
          33             Point2D point1, point2;
          34             double angle = 0;
          35             if(i == points.size()-1){
          36                 point1 = (Point2D)points.get(i-1);
          37                 point2 = point;
          38             } else {
          39                 point1 = point;
          40                 point2 = (Point2D)points.get(i+1);
          41             }
          42             angle = getAngle(point1, point2);
          43             int sign = (fromLeft && from || !fromLeft && !from) ? -1 : 1;
          44             if(angle == -Math.PI/2){
          45                 sign = point2.getY() > point1.getY() ? 1 : -1;
          46             }else if(angle == Math.PI/2){
          47                 sign = point2.getY() > point1.getY() ? -1 : 1;
          48             }
          49             double alpha = (Double)this.element.getClientProperty("defaultAlpha");
          50             if(offset * count >= i && offset * count - fillSegmentCount < = i){
          51                 alpha = 1 - (offset * count - i)/fillSegmentCount * 0.5;
          52             }
          53             g2d.setColor(new Color(lineColor.getRed(), lineColor.getGreen(), lineColor.getBlue(), (int)(255 * alpha)));
          54 
          55             AffineTransform transform = new AffineTransform();
          56             transform.translate(point.getX(), point.getY());
          57             transform.rotate(angle);
          58             transform.translate(-point.getX(), -point.getY());
          59 
          60             Point2D p0 = new Point.Double();
          61             transform.transform(point, p0);
          62             Point2D p1 =  new Point.Double();
          63             transform.transform(new Point.Double(point.getX() + segmentLength/2 * sign, point.getY() - segmentLength/2), p1);
          64             Point2D p2 =  new Point.Double();
          65             transform.transform(new Point.Double(point.getX() + segmentLength/2 * sign, point.getY() + segmentLength/2), p2);
          66             GeneralPath path = new GeneralPath();
          67             path.moveTo(p1.getX(), p1.getY());
          68             path.lineTo(p0.getX(), p0.getY());
          69             path.lineTo(p2.getX(), p2.getY());
          70             g2d.draw(path);
          71         }
          72     }
          73 
          74     private static double getAngle(Point2D p1, Point2D p2) {
          75         if(p1.getX() == p2.getX()){
          76             if(p2.getY() == p1.getY()){
          77                 return 0;
          78             }
          79             else if(p2.getY() > p1.getY()){
          80                 return Math.PI/2;
          81             }
          82             else{
          83                 return -Math.PI/2;
          84             }
          85         }
          86         return Math.atan((p2.getY() - p1.getY()) / (p2.getX() - p1.getX()));
          87     }
          88 }

          有了這種流動式的箭頭,我們就可以繪制出更多豐富多彩的界面,最后給出一個完整的例子供大伙學習參考:
          注:附件中還給出了另一種Link的實現效果。 見原文最下方

          評論

          # re: 用Swing定制流動的Link樣式[未登錄]  回復  更多評論   

          2015-05-28 22:16 by Neo
          如何設置Link的寬度呢?我的這個Link是連接兩個Group的。

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 吉首市| 太湖县| 宝兴县| 嫩江县| 循化| 贵港市| 灌南县| 周宁县| 凉山| 彭泽县| 郓城县| 大埔区| 屯留县| 越西县| 宣恩县| 永善县| 潞西市| 瓮安县| 岳阳市| 万源市| 辰溪县| 沭阳县| 循化| 台中市| 儋州市| 上思县| 兴隆县| 乐至县| 新乐市| 剑阁县| 海城市| 会泽县| 锦州市| 广饶县| 米林县| 班玛县| 故城县| 绥中县| 九台市| 丰镇市| 东阳市|