如何序列化的Java 2D Shape对象为XML? [英] How to serialize Java 2D Shape objects as XML?

查看:205
本文介绍了如何序列化的Java 2D Shape对象为XML?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

图形接口由Java的2D(的Arc2D 区<对象实施/ code>,的CubicCurve2D Ellipse2D的的GeneralPath 等)。

一些具体的对象都被标记为序列化键,可以存储和使用对象序列化恢复,但其他类似没有实现接口,并抛出错误。

但由于我们正在不断地警告说,这种幼稚的序列化是不能跨越的Java实现或版本不一定稳定,我倒是preFER使用某种形式的序列化是的。

这使我们存储/使用从XML还原 XMLEn codeR XMLDe codeR ,但能够处理更少了Java 2D 形状的对象。

有两个部分的结果可以看到下面。我们先从6形状和尝试存储/通过对象序列化和标准的XML序列化恢复。

我们将如何存储所有图形通过XML对象正确?

 进口java.awt中的*。
导入的java.awt.geom *。
进口java.awt.image.BufferedImage中;
进口的java.beans *。
进口java.io. *;
进口的java.util.ArrayList;
进口的javax.swing *。
进口javax.swing.border.TitledBorder中;公共类Serialize2D {    私人JPanel的用户界面;    Serialize2D(){
        initUI();
    }    公共无效initUI(){
        如果(UI!= NULL){
            返回;
        }
        UI =新JPanel(新的GridLayout(0,1));        INT [] xpoints = {205,295,205,295};
        INT [] ypoints中= {5,25,25,45};
        多边形多边形=新的多边形(xpoints,ypoints中,xpoints.length);        ArrayList的&LT;形状和GT;形状=新的ArrayList&LT;形状和GT;();
        INT W = 45;
        shapes.add(新Rectangle2D.Double(5,5,90,40));
        shapes.add(新Ellipse2D.Double(105,图5,90,40));
        shapes.add(多边形);
        shapes.add(新GeneralPath(新Rectangle2D.Double(5,55,90,40)));
        shapes.add(新Path2D.Double(新Rectangle2D.Double(105,55,90,40)));
        shapes.add(新所在区域(新Rectangle2D.Double(205,55,90,40)));        addTitledLabelToPanel(形状,原始形状);
        addTitledLabelToPanel(
                serializeToFromObject(图形),通过序列化对象);
        addTitledLabelToPanel(
                serializeToFromXML(图形),通过XML序列化);
    }    公共JComponent中的getUI(){
        返回UI;
    }    公众的ArrayList&LT;形状和GT; serializeToFromObject(ArrayList的&LT;形状和GT;形状){
        ArrayList的&LT;形状和GT;小水电站=新的ArrayList&LT;形状和GT;();
        尝试{
            ObjectOutputStream的OOS = NULL;
            ByteArrayOutputStream BAOS =新ByteArrayOutputStream();
            OOS =新的ObjectOutputStream(BAOS);
            对于(形状的形状:形状){
                尝试{
                    oos.writeObject(形状);
                }赶上(例外前){
                    通信System.err.println(ex.toString());
                }
            }
            oos.flush();
            oos.close();
            的System.out.println(长的OBJ:+ baos.toByteArray()长);
            ByteArrayInputStream的BAIS =新ByteArrayInputStream的(
                    baos.toByteArray());
            ObjectInputStream的OIS =新的ObjectInputStream(拜斯);            对象o = NULL;
            尝试{
                O = ois.readObject();
            }赶上(NotSerializableException前){
                通信System.err.println(ex.getMessage());
            }赶上(ClassNotFoundException的前){
                ex.printStackTrace();
            }
            而(O!= NULL){
                shps.add((形状)O);
                尝试{
                    O = ois.readObject();
                }赶上(NotSerializableException前){
                    通信System.err.println(ex.getMessage());
                }赶上(ClassNotFoundException的前){
                    ex.printStackTrace();
                }
            }
            返回小水电站;
        }赶上(IOException异常前){
            ex.printStackTrace();
        }
        返回小水电站;
    }    公众的ArrayList&LT;形状和GT; serializeToFromXML(ArrayList的&LT;形状和GT;形状){
        ArrayList的&LT;形状和GT;小水电站=新的ArrayList&LT;形状和GT;();
        尝试{
            ByteArrayOutputStream BAOS =新ByteArrayOutputStream();
            XMLEn codeR xmle =新XMLEn codeR(BAOS);
            对于(形状的形状:形状){
                xmle.writeObject(形状);
            }
            xmle.flush();
            xmle.close();            的System.out.println(长XML:+ baos.toByteArray()长);
            ByteArrayInputStream的拜斯
                    =新ByteArrayInputStream进行(baos.toByteArray());
            XMLDe codeR xmld =新XMLDe codeR(拜斯);
            形状的形状=(形状)xmld.readObject();
            而(形状!= NULL){
                shps.add(形状);
                尝试{
                    形状=(形状)xmld.readObject();
                }赶上(ArrayIndexOutOfBoundsException异常aioobe){
                    //我们读过的最后一个对象
                    外形= NULL;
                }
            }
            xmld.close();
        }赶上(例外前){
            ex.printStackTrace();
        }
        返回小水电站;
    }    私人最终静态字符串的getType(对象o){
        字符串s = o.getClass()的getName()。
        的String [] =零件s.split(\\\\);
        S =零件[parts.length - 1] .split(\\\\ $)[0];
        返回S;
    }    公共静态无效drawShapesToImage(
            ArrayList的&LT;形状和GT;形状的BufferedImage图像){
        Graphics2D的G = image.createGraphics();
        g.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);        g.setColor(Color.WHITE);
        g.fillRect(0,0,image.getWidth(),image.getHeight());
        对于(形状的形状:形状){
            字符串s =的getType(形状);
            g.setColor(Color.GREEN);
            g.fill(形状);
            g.setColor(Color.BLACK);
            g.draw(形状);
            矩形R = shape.getBounds();
            INT X = r.x + 5;
            INT Y = r.y + 16;
            如果(r.width * r.height!= 0){
                g.drawString(S,X,Y);
            }
        }        g.dispose();
    }    私人无效addTitledLabelToPanel(ArrayList的&LT;形状和GT;的形状,弦乐标题){
        INT W = 300;
        INT H = 100;
        BufferedImage的双向=新的BufferedImage(W,H,BufferedImage.TYPE_INT_RGB);
        drawShapesToImage(形状,BI);
        JLabel的L =新的JLabel(新的ImageIcon(BI));
        l.setBorder(新的TitledBorder(职称));
        ui.add(升);
    }    公共静态无效的主要(字串[] args){
        可运行R =新的Runnable(){            @覆盖
            公共无效的run(){
                Serialize2D SS =新Serialize2D();
                JOptionPane.showMessageDialog(NULL,ss.getUI());
            }
        };
        SwingUtilities.invokeLater(R);
    }
}


解决方案

不幸的是,天真的编码/ A 图形到XML使用的解码 XMLEn codeR / 德codeR 往往破坏了形状的所有重要信息

因此​​,要做到这一点,仍然采用上面提到的类,我们序列化和恢复适当建造的bean重新present形状如从 的PathIterator 这些bean是:


  • PathBean 存储 PathSegment ,形成了Java的2D <$ C $的形状对象的集合C>图形

  • PathSegment 存储路径的特定部分的细节(段型,缠绕规则和放大器; coords)使用。

SerializeShapes GUI

一个图形用户界面来演示存储和恢复的形状。


  • 点击<大骨节病>椭圆 Ellipse2D的),<大骨节病>长方形矩形)或<大骨节病>面)按钮几次。

  • 退出GUI。形状将被序列化到磁盘中。

  • 重新启动GUI。从最后一次随机抽取的形状会从磁盘和放大器可以恢复;重新出现在GUI中。

选定的形状将被填充在绿色,其他形状的红色。

 包serialize2d;进口java.awt中的*。
java.awt.event中导入*。
进口java.awt.font.FontRenderContext;
进口java.awt.geom.AffineTransform中;
导入的java.awt.geom *。
进口java.awt.image.BufferedImage中;
进口java.io.FileNotFoundException;
进口的java.util.ArrayList;
进口的java.util.Enumeration;
进口了java.util.Random;
进口java.util.Vector中;
进口的javax.swing *。
进口javax.swing.border.EmptyBorder中;
javax.swing.event中导入*。
/ **一个图形用户界面,可以很容易从画布添加/删除形状。
 它应该坚持运行之间的形状。 * /
公共类SerializeShapes {    JPanel的用户界面;
    JPanel的shapePanel;
    随机兰特;
    JPanel的shapeCanvas;
    DefaultListModel&LT;形状和GT; allShapesModel;
    的ListSelectionModel shapeSelectionModel;
    的RenderingHints RenderingHints的;    SerializeShapes(){
        initUI();
    }    公共无效initUI(){
        如果(UI!= NULL){
            返回;
        }
        RenderingHints的=新的RenderingHints(RenderingHints.KEY_DITHERING,
                RenderingHints.VALUE_DITHER_ENABLE);
        renderingHints.put(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        renderingHints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
                RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        renderingHints.put(RenderingHints.KEY_COLOR_RENDERING,
                RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        renderingHints.put(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
        renderingHints.put(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_NORMALIZE);
        的ui =新JPanel(新的BorderLayout(4,4));
        ui.setBorder(新EmptyBorder(4,4,4,4));        JPanel的控制=新JPanel(新的FlowLayout(FlowLayout.CENTER,4,4));
        ui.add(控制,BorderLayout.PAGE_START);
        shapeCanvas =新ShapeCanvas();
        ui.add(shapeCanvas);
        兰特=新的随机();        allShapesModel =新DefaultListModel&LT;形状和GT;();
        JList的&LT;形状和GT; allShapes =新的JList&LT;形状和GT;(allShapesModel);
        allShapes.setCellRenderer(新ShapeListCellRenderer());
        shapeSelectionModel = allShapes.getSelectionModel();
        shapeSelectionModel.setSelectionMode(
                ListSelectionModel.SINGLE_SELECTION);
        ListSelectionListener shapesSelectionListener
                =新ListSelectionListener(){                    @覆盖
                    公共无效的valueChanged(ListSelectionEvent E){
                        shapeCanvas.repaint();
                    }
                };
        allShapes.addListSelectionListener(shapesSelectionListener);        JScrollPane的shapesScroll =新JScrollPane的(
                allShapes,
                JScrollPane.VERTICAL_SCROLLBAR_​​ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_​​NEVER
        );
        // TODO解决这个黑客..
        shapesScroll.getViewport()设定preferredSize(新尺寸(60,200));
        ui.add(shapesScroll,BorderLayout.LINE_START);        行动addEllipse =新AbstractAction(椭圆){            @覆盖
            公共无效的actionPerformed(ActionEvent的五){
                INT W = rand.nextInt(100)+ 10;
                INT H = rand.nextInt(100)+ 10;
                INT X = rand.nextInt(shapeCanvas.getWidth() - W);
                INT Y = rand.nextInt(shapeCanvas.getHeight() - H);
                Ellipse2D的椭圆=新Ellipse2D.Double(X,Y,W,H);
                addShape(椭圆形);
            }
        };
        addEllipse.putValue(Action.MNEMONIC_KEY,KeyEvent.VK_E);        行动addRectangle =新AbstractAction(矩形){            @覆盖
            公共无效的actionPerformed(ActionEvent的五){
                INT W = rand.nextInt(100)+ 10;
                INT H = rand.nextInt(100)+ 10;
                INT X = rand.nextInt(shapeCanvas.getWidth() - W);
                INT Y = rand.nextInt(shapeCanvas.getHeight() - H);
                矩形矩形=新Rectangle2D.Double(X,Y,W,H);
                addShape(矩形);
            }
        };
        addRectangle.putValue(Action.MNEMONIC_KEY,KeyEvent.VK_R);        最终诠释faceStart = 128513;
        最终诠释faceEnd = 128528;
        最终诠释差异= faceEnd - faceStart;
        StringBuilder的SB =新的StringBuilder();
        对于(诠释计数= faceStart; COUNT&LT; = faceEnd;计数++){
            sb.append(Character.toChars(计数));
        }
        最后一个String = sb.toString();
        矢量&lt;字体和GT; compatibleFontList =新的矢量&lt;字体和GT;();
        GraphicsEnvironment中GE
                = GraphicsEnvironment.getLocalGraphicsEnvironment();
        字体[] =字体ge.getAllFonts();
        对于(字体字体:字体){
            如果(font.canDisplayUpTo(多个)℃,){
                compatibleFontList.add(字体);
            }
        }
        JComboBox的fontChooser =新的JComboBox(compatibleFontList);
        ListCellRenderer fontRenderer =新DefaultListCellRenderer(){            @覆盖
            公共组件getListCellRendererComponent(
                    JList的列表,对象的值,INT指数,
                    布尔isSelected,布尔cellHasFocus){
                组件C = super.getListCellRendererComponent(
                        列表值,指数,
                        isSelected,cellHasFocus);
                JLabel的L =(JLabel的)C;
                字体字型=(字体)值;
                l.setText(font.getName());
                返回升;
            }
        };
        fontChooser.setRenderer(fontRenderer);
        最终的ComboBoxModel&LT;字体和GT; fontModel = fontChooser.getModel();        BufferedImage的双向=新的BufferedImage(1,1,BufferedImage.TYPE_INT_RGB);
        Graphics2D的G = bi.createGraphics();
        最终的FontRenderContext的FontRenderContext = g.getFontRenderContext();        行动addFace =新AbstractAction(脸谱){            @覆盖
            公共无效的actionPerformed(ActionEvent的五){
                INT $ C $连接点= faceStart + rand.nextInt(差异);
                字符串文本=新的String(Character.toChars($ C $口岸系统));                字体字型=(字体)fontModel.getSelectedItem();
                区域面积=新Area(
                        font.deriveFont(80F)。
                        createGlyphVector(FontRenderContext中,文本)。
                        getOutline());
                矩形范围= area.getBounds();
                浮动X = rand.nextInt(
                        shapeCanvas.getWidth() - bounds.width) - bounds.x;
                浮Y = rand.nextInt(
                        shapeCanvas.getHeight() - bounds.height) - bounds.y;
                此举的AffineTransform =的AffineTransform。
                        getTranslateInstance(X,Y);
                area.transform(移动);
                addShape(区);
            }
        };
        addFace.putValue(Action.MNEMONIC_KEY,KeyEvent.VK_F);        操作删除=新AbstractAction(删除){            @覆盖
            公共无效的actionPerformed(ActionEvent的五){
                INT IDX = shapeSelectionModel.getMinSelectionIndex();
                如果(IDX℃,){
                    JOptionPane.showMessageDialog(
                            UI,
                            选择形状删除,
                            选择形状
                            JOptionPane.ERROR_MESSAGE);
                }其他{
                    allShapesModel.removeElementAt(IDX);
                    shapeCanvas.repaint();
                }
            }
        };
        delete.putValue(Action.MNEMONIC_KEY,KeyEvent.VK_D);        Controls.Add被(新的JButton(addEllipse));
        Controls.Add被(新的JButton(addRectangle));
        Controls.Add被(新的JButton(addFace));
        Controls.Add被(fontChooser);
        Controls.Add被(新的JButton(删除));        尝试{
            ArrayList的&LT;形状和GT;形状= deserializeShapes();
            对于(形状的形状:形状){
                allShapesModel.addElement(形状);
            }
        }赶上(例外前){
            通信System.err.println(如果第一次发射,这是预期!);
            ex.printStackTrace();
        }
    }    私人无效addShape(形状的形状){
        allShapesModel.addElement(形状);
        INT大小= allShapesModel.getSize() - 1;
        shapeSelectionModel.addSelectionInterval(尺寸,大小);
    }    类ShapeCanvas继承JPanel {        ShapeCanvas(){
            的setBackground(Color.WHITE);
        }        @覆盖
        公共无效的paintComponent(图形G){
            super.paintComponent方法(G);
            Graphics2D的G2 =(Graphics2D的)克;
            g2.setRenderingHints(RenderingHints的);
            行程行程=的新BasicStroke(1.5F);
            g2.setStroke(中风);
            INT IDX = shapeSelectionModel.getMinSelectionIndex();
            形状selectedShape = NULL;
            如果(IDX -1个){
                selectedShape = allShapesModel.get(IDX);
            }
            枚举EN = allShapesModel.elements();
            而(en.hasMoreElements()){
                形状的形状=(形状)en.nextElement();
                如果(shape.equals(selectedShape)){
                    g2.setColor(新颜色(0,255,0,191));
                }其他{
                    g2.setColor(新的色彩(255,0,0,191));
                }
                g2.fill(形状);
                g2.setColor(新的色彩(0,0,0,224));
                g2.draw(形状);
            }
        }        @覆盖
        公共尺寸的get preferredSize(){
            返回新尺寸(500,300);
        }
    }    公共JComponent中的getUI(){
        返回UI;
    }    公共静态无效的主要(字串[] args){
        可运行R =新的Runnable(){
            @覆盖
            公共无效的run(){
                SerializeShapes SE =新SerializeShapes();                JFrame的F =新的JFrame(序列化形状);
                f.addWindowListener(新SerializeWindowListener(SE));
                f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                f.setContentPane(se.getUI());
                f.setResizable(假);
                f.pack();
                f.setLocationByPlatform(真);
                f.setVisible(真);
            }
        };
        SwingUtilities.invokeLater(R);
    }    公共无效serializeShapes()抛出FileNotFoundException异常{
        ArrayList的&LT;形状和GT;形状
                =新的ArrayList&LT;形状和GT;();
        枚举EN = allShapesModel.elements();
        而(en.hasMoreElements()){
            形状的形状=(形状)en.nextElement();
            shapes.add(形状);
        }
        ShapeIO.serializeShapes(形状,this.getClass());
        尝试{
            Desktop.getDesktop()。打开(
                    ShapeIO.getSerializeFile(this.getClass()));
        }赶上(例外五){
            e.printStackTrace();
        }
    }    公众的ArrayList&LT;形状和GT; deserializeShapes()抛出FileNotFoundException异常{
        返回ShapeIO.deserializeShapes(this.getClass());
    }    类ShapeListCellRenderer扩展DefaultListCellRenderer {        @覆盖
        公共组件getListCellRendererComponent(
                JList的&LT ;?扩展对象&gt;列表,对象的值,
                INT指数,布尔isSelected,布尔cellHasFocus){
            组件C = super.getListCellRendererComponent(列表值,指数,
                    isSelected,cellHasFocus);
            JLabel的L =(JLabel的)C;
            形状的形状=(形状)值;
            ShapeIcon图标=新ShapeIcon(形状,40);
            l.setIcon(图标);
            l.setText();            返回升;
        }
    }    类ShapeIcon实现图标{        形状的形状;
        INT大小;        ShapeIcon(形状的形状,大小INT){
            this.shape =形状;
            this.size =大小;
        }        @覆盖
        公共无效的paintIcon(C组分,图形克,INT的x,int y)对{
            Graphics2D的G2 =(Graphics2D的)克;
            g2.setRenderingHints(RenderingHints的);
            矩形范围= shape.getBounds();
            INT x关= -bounds.x;
            INT YOFF = -bounds.y;
            双xRatio =(双)bounds.width /(双)的大小;
            双yRatio =(双)bounds.height /(双)的大小;
            双率= xRatio&GT; yRatio? xRatio:yRatio;
            的AffineTransform刻度= AffineTransform.getScaleInstance(1 /比,1 /比);
            移的AffineTransform = AffineTransform.getTranslateInstance(x关,YOFF);            的AffineTransform totalTransform =新AffineTransform();            totalTransform.concatenate(规模);
            totalTransform.concatenate(移);            B区=新的区域(形状).createTransformedArea(totalTransform);
            界限= b.getBounds();            g2.setColor(Color.BLACK);
            g2.fill(二);
        }        @覆盖
        公众诠释getIconWidth(){
            返回的大小;
        }        @覆盖
        公众诠释getIconHeight(){
            返回的大小;
        }
    }
}类SerializeWindowListener扩展WindowAdapter的{    SerializeShapes serializeShapes;    SerializeWindowListener(SerializeShapes serializeShapes){
        this.serializeShapes = serializeShapes;
    }    @覆盖
    公共无效的windowClosing(WindowEvent五){
        尝试{
            serializeShapes.serializeShapes();
        }赶上(FileNotFoundException异常前){
            ex.printStackTrace();
            System.exit(1);
        }
        System.exit(0);
    }
}

ShapeIO

从XML执行的I / O操作/

 包serialize2d;进口java.awt.Shape中;
进口的java.beans *。
进口java.io. *;
进口的java.util.ArrayList;公共类ShapeIO {    / **形状列表保存到文件系统。 * /
    公共静态无效serializeShapes(
            ArrayList的&LT;形状和GT;形状,类serializeClass)
            抛出FileNotFoundException异常{
        文件F = getSerializeFile(serializeClass);
        XMLEn codeR xmle =新XMLEn codeR(新的FileOutputStream(f)条);        ArrayList的&LT; PathBean&GT; pathSegmentsCollection =新的ArrayList&LT;&GT;();
        对于(形状的形状:形状){
            ArrayList的&LT; PathSegment&GT; pathSegments =
                    BeanConverter.getSegmentsFromShape(形状);
            PathBean为=新PathBean(pathSegments);
            pathSegmentsCollection.add(如);
        }        xmle.writeObject(pathSegmentsCollection);
        xmle.flush();
        xmle.close();
    }    / **从文件系统加载形状列表。 * /
    公共静态的ArrayList&LT;形状和GT; deserializeShapes(类serializeClass)
            抛出FileNotFoundException异常{
        文件F = getSerializeFile(serializeClass);
        XMLDe codeR xmld =新XMLDe codeR(新的FileInputStream(F));
        ArrayList的&LT; PathBean&GT; pathSegmentsCollection
                =(ArrayList的&LT; PathBean&GT;)xmld.readObject();
        ArrayList的&LT;形状和GT;形状=新的ArrayList&LT;形状和GT;();
        对于(PathBean pathSegments:pathSegmentsCollection){
            shapes.add(BeanConverter.getShapeFromSegments(pathSegments));
        }        返回的形状;
    }    / **提供一个独特的,可重复和放大器;一类可读/写路径。 * /
    公共静态文件getSerializeFile(类serializeClass){
        文件f =新的文件(System.getProperty(的user.home));
        的String [] = nameParts serializeClass.getCanonicalName()分裂(\\\\)。        F =新的文件(F,Java的);
        对于(字符串namePart:nameParts){
            F =新的文件(F,namePart);
        }
        f.mkdirs();
        F =新的文件(F,nameParts [nameParts.length-1] +的.xml);        返回F;
    }
}

BeanConverter

获取一个的PathIterator 图形,并将其转换为可序列化的bean。豆转换回的GeneralPath

 包serialize2d;进口java.awt.Shape中;
导入的java.awt.geom *。
进口的java.util.ArrayList;/ **工具类bean来/从形状转换。 * /
公共类BeanConverter {    / **转换的形状为可序列化的bean。 * /
    公共静态的ArrayList&LT; PathSegment&GT; getSegmentsFromShape(形状的形状){
        ArrayList的&LT; PathSegment&GT; shapeSegments =新的ArrayList&LT; PathSegment&GT;();
        对于(
                PI的PathIterator = shape.getPathIterator(NULL);
                !pi.isDone();
                pi.next()){
            双[] = COORDS新的双[6];
            INT pathSegmentType = pi.currentSegment(co​​ords)使用;
            INT windingRule = pi.getWindingRule();
            PathSegment为=新PathSegment(
                    pathSegmentType,windingRule,coords)使用;
            shapeSegments.add(如);
        }
        返回shapeSegments;
    }    / **可序列化的bean转换为形状。 * /
    公共静态形状getShapeFromSegments(PathBean shapeSegments){
        GP的GeneralPath =新GeneralPath();
        为(PathSegment shapeSegment:shapeSegments.getPathSegments()){
            双[] = COORDS shapeSegment.getCoords();
            INT pathSegmentType = shapeSegment.getPathSegmentType();
            INT windingRule = shapeSegment.getWindingRule();
            gp.setWindingRule(windingRule);
            如果(pathSegmentType == PathIterator.SEG_MOVETO){
                gp.moveTo(COORDS [0],COORDS [1]);
            }否则如果(pathSegmentType == PathIterator.SEG_LINETO){
                gp.lineTo(COORDS [0],COORDS [1]);
            }否则如果(pathSegmentType == PathIterator.SEG_QUADTO){
                gp.quadTo(COORDS [0],COORDS [1],COORDS [2],COORDS [3]);
            }否则如果(pathSegmentType == PathIterator.SEG_CUBICTO){
                gp.curveTo(
                        COORDS [0],COORDS [1],COORDS [2],
                        COORDS [3],COORDS [4],COORDS [5]);
            }否则如果(pathSegmentType == PathIterator.SEG_CLOSE){
                gp.closePath();
            }其他{
                通信System.err.println(意外的值!+ pathSegmentType);
            }
        }
        返回GP;
    }
}

PathBean

存储路径段的集合中seriallizable豆

 包serialize2d;导入的java.awt.geom *。
进口的java.util.ArrayList;PathSegment对象的集合/ ** PathBean存储
 构成一个形状的路径。 * /
公共类PathBean {    公众的ArrayList&LT; PathSegment&GT; pathSegments;    公共PathBean(){}    公共PathBean(ArrayList的&LT; PathSegment&GT; pathSegments){
        this.pathSegments = pathSegments;
    }    公众的ArrayList&LT; PathSegment&GT; getPathSegments(){
        返回pathSegments;
    }    公共无效setPathSegments(ArrayList的&LT; PathSegment&GT; pathSegments){
        this.pathSegments = pathSegments;
    }    @覆盖
    公共字符串的toString(){
        StringBuilder的SB =新的StringBuilder({);
        对于(PathSegment pathSegment:pathSegments){
            sb.append(\\ n \\ t的);
            sb.append(pathSegment.toString());
        }
        sb.append(\\ n);
        sb.append(});
        回归PathSegments:+ sb.toString();
    }
}

PathSegment

存储整个路径的一部分的路径段。

 包serialize2d;进口java.util.Arrays中;/ ** PathSegment豆存储在路径的一个区段的细节
 构成的Shape。 * /
公共类PathSegment {    公众诠释pathSegmentType;
    公众诠释windingRule;
    市民双[] COORDS;    公共PathSegment(){}    公共PathSegment(INT pathSegmentType,诠释windingRule,双[] coords)使用{
        this.pathSegmentType = pathSegmentType;
        this.windingRule = windingRule;
        this.coords = COORDS;
    }    公众诠释getPathSegmentType(){
        返回pathSegmentType;
    }    公共无效setPathSegmentType(INT pathSegmentType){
        this.pathSegmentType = pathSegmentType;
    }    公众诠释getWindingRule(){
        返回windingRule;
    }    公共无效setWindingRule(INT windingRule){
        this.windingRule = windingRule;
    }    市民双[] getCoords(){
        返回COORDS;
    }    公共无效setCoords(双[] coords)使用{
        this.coords = COORDS;
    }    @覆盖
    公共字符串的toString(){
        字符串SC =(COORDS =空!?Arrays.toString(COORDS));
        字符串s =的String.format(
                PathSegment:路径段类型:内容 - %d \\ t的
                +缠绕规则:内容 - %d \\ tcoords: - %S,
                getPathSegmentType(),getWindingRule(),SC);
        返回S;
    }
}

注释

此的目的是作为一个概念证明,而不是一个抛光的方法。


  • XML序列化的数据变成大的真正快速,它通常会被压缩。邮编COM pression可能刮胡子30-40折序列化对象或一个类文件的字节大小,但80-95%的折扣XML。在任何情况下,拉链可以很好地用于下一个点为好。

  • 对于那些我们希望提供给项目类型的序列化和恢复形状,的我们可能会 想要包含更多细节形状(例如填充颜色或纹理和绘制颜色或中风等)以及诸如图像或字体的其它数据。这也是邮编就派上用场了,因为我们可以把他们都在同一个档案,每一个COM pression的最好水平(例如,标准的XML,没有图片)。

一个压缩的源文件的档案这个答案可以从我的下载云驱动器。

The Shape interface is implemented by objects of Java 2D (Arc2D, Area, CubicCurve2D, Ellipse2D, GeneralPath etc..).

Some of the concrete objects are marked as Serializable and can be stored and restored using object serialization, but others like Area do not implement the interface and throw errors.

But since we are constantly warned that such naive serialization is not necessarily stable across Java implementations or versions, I'd prefer to use some form of serialization that is.

That leads us to storing/restoring from XML using XMLEncoder and XMLDecoder, but that is capable of handling even less of the Java 2D Shape objects.

Some results for both can be seen below. We start with 6 shapes, and attempt to store/restore them via object serialization and standard XML serialization.

How would we store all Shape objects correctly via XML?

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.beans.*;
import java.io.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.TitledBorder;

public class Serialize2D {

    private JPanel ui;

    Serialize2D() {
        initUI();
    }

    public void initUI() {
        if (ui != null) {
            return;
        }
        ui = new JPanel(new GridLayout(0, 1));

        int[] xpoints = {205, 295, 205, 295};
        int[] ypoints = {5, 25, 25, 45};
        Polygon polygon = new Polygon(xpoints, ypoints, xpoints.length);

        ArrayList<Shape> shapes = new ArrayList<Shape>();
        int w = 45;
        shapes.add(new Rectangle2D.Double(5, 5, 90, 40));
        shapes.add(new Ellipse2D.Double(105, 5, 90, 40));
        shapes.add(polygon);
        shapes.add(new GeneralPath(new Rectangle2D.Double(5, 55, 90, 40)));
        shapes.add(new Path2D.Double(new Rectangle2D.Double(105, 55, 90, 40)));
        shapes.add(new Area(new Rectangle2D.Double(205, 55, 90, 40)));

        addTitledLabelToPanel(shapes, "Original Shapes");
        addTitledLabelToPanel(
                serializeToFromObject(shapes), "Serialize via Object");
        addTitledLabelToPanel(
                serializeToFromXML(shapes), "Serialize via XML");
    }

    public JComponent getUI() {
        return ui;
    }

    public ArrayList<Shape> serializeToFromObject(ArrayList<Shape> shapes) {
        ArrayList<Shape> shps = new ArrayList<Shape>();
        try {
            ObjectOutputStream oos = null;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            for (Shape shape : shapes) {
                try {
                    oos.writeObject(shape);
                } catch (Exception ex) {
                    System.err.println(ex.toString());
                }
            }
            oos.flush();
            oos.close();
            System.out.println("length Obj: " + baos.toByteArray().length);
            ByteArrayInputStream bais = new ByteArrayInputStream(
                    baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);

            Object o = null;
            try {
                o = ois.readObject();
            } catch (NotSerializableException ex) {
                System.err.println(ex.getMessage());
            } catch (ClassNotFoundException ex) {
                ex.printStackTrace();
            }
            while (o != null) {
                shps.add((Shape) o);
                try {
                    o = ois.readObject();
                } catch (NotSerializableException ex) {
                    System.err.println(ex.getMessage());
                } catch (ClassNotFoundException ex) {
                    ex.printStackTrace();
                }
            }
            return shps;
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return shps;
    }

    public ArrayList<Shape> serializeToFromXML(ArrayList<Shape> shapes) {
        ArrayList<Shape> shps = new ArrayList<Shape>();
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            XMLEncoder xmle = new XMLEncoder(baos);
            for (Shape shape : shapes) {
                xmle.writeObject(shape);
            }
            xmle.flush();
            xmle.close();

            System.out.println("length XML: " + baos.toByteArray().length);
            ByteArrayInputStream bais
                    = new ByteArrayInputStream(baos.toByteArray());
            XMLDecoder xmld = new XMLDecoder(bais);
            Shape shape = (Shape) xmld.readObject();
            while (shape != null) {
                shps.add(shape);
                try {
                    shape = (Shape) xmld.readObject();
                } catch (ArrayIndexOutOfBoundsException aioobe) {
                    // we've read last object
                    shape = null;
                }
            }
            xmld.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return shps;
    }

    private final static String getType(Object o) {
        String s = o.getClass().getName();
        String[] parts = s.split("\\.");
        s = parts[parts.length - 1].split("\\$")[0];
        return s;
    }

    public static void drawShapesToImage(
            ArrayList<Shape> shapes, BufferedImage image) {
        Graphics2D g = image.createGraphics();
        g.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        g.setColor(Color.WHITE);
        g.fillRect(0, 0, image.getWidth(), image.getHeight());
        for (Shape shape : shapes) {
            String s = getType(shape);
            g.setColor(Color.GREEN);
            g.fill(shape);
            g.setColor(Color.BLACK);
            g.draw(shape);
            Rectangle r = shape.getBounds();
            int x = r.x + 5;
            int y = r.y + 16;
            if (r.width * r.height != 0) {
                g.drawString(s, x, y);
            }
        }

        g.dispose();
    }

    private void addTitledLabelToPanel(ArrayList<Shape> shapes, String title) {
        int w = 300;
        int h = 100;
        BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        drawShapesToImage(shapes, bi);
        JLabel l = new JLabel(new ImageIcon(bi));
        l.setBorder(new TitledBorder(title));
        ui.add(l);
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                Serialize2D ss = new Serialize2D();
                JOptionPane.showMessageDialog(null, ss.getUI());
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

解决方案

Unfortunately, naive encoding/decoding of a Shape to XML using XMLEncoder/Decoder often destroys all the vital information of the Shape!

So to do this, still using the above mentioned classes, we serialize and restore properly constructed beans that represent the parts of the shape as obtained from a PathIterator. These beans are:

  • PathBean which stores the collection of PathSegment objects that form the shape of the Java-2D Shape.
  • PathSegment which stores the details of a particular part of the path (segment type, winding rule & coords).

SerializeShapes GUI

A GUI to demonstrate storing and restoring shapes.

  • Click the Ellipse (Ellipse2D), Rectangle (Rectangle2D) or Face (Area) buttons a couple of times.
  • Exit the GUI. The shapes will be serialized to disk.
  • Restart the GUI. The randomly drawn shapes from last time will be restored from disk & reappear in the GUI.

The selected shape will be filled in green, other shapes in red.

package serialize2d;

import java.awt.*;
import java.awt.event.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Random;
import java.util.Vector;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;


/** A GUI to make it easy to add/remove shapes from a canvas. 
 It should persist the shapes between runs.  */
public class SerializeShapes {

    JPanel ui;
    JPanel shapePanel;
    Random rand;
    JPanel shapeCanvas;
    DefaultListModel<Shape> allShapesModel;
    ListSelectionModel shapeSelectionModel;
    RenderingHints renderingHints;

    SerializeShapes() {
        initUI();
    }

    public void initUI() {
        if (ui != null) {
            return;
        }
        renderingHints = new RenderingHints(RenderingHints.KEY_DITHERING,
                RenderingHints.VALUE_DITHER_ENABLE);
        renderingHints.put(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        renderingHints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
                RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        renderingHints.put(RenderingHints.KEY_COLOR_RENDERING,
                RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        renderingHints.put(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
        renderingHints.put(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_NORMALIZE);
        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));

        JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, 4, 4));
        ui.add(controls, BorderLayout.PAGE_START);
        shapeCanvas = new ShapeCanvas();
        ui.add(shapeCanvas);
        rand = new Random();

        allShapesModel = new DefaultListModel<Shape>();
        JList<Shape> allShapes = new JList<Shape>(allShapesModel);
        allShapes.setCellRenderer(new ShapeListCellRenderer());
        shapeSelectionModel = allShapes.getSelectionModel();
        shapeSelectionModel.setSelectionMode(
                ListSelectionModel.SINGLE_SELECTION);
        ListSelectionListener shapesSelectionListener
                = new ListSelectionListener() {

                    @Override
                    public void valueChanged(ListSelectionEvent e) {
                        shapeCanvas.repaint();
                    }
                };
        allShapes.addListSelectionListener(shapesSelectionListener);

        JScrollPane shapesScroll = new JScrollPane(
                allShapes,
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
        );
        // TODO fix this hack..
        shapesScroll.getViewport().setPreferredSize(new Dimension(60, 200));
        ui.add(shapesScroll, BorderLayout.LINE_START);

        Action addEllipse = new AbstractAction("Ellipse") {

            @Override
            public void actionPerformed(ActionEvent e) {
                int w = rand.nextInt(100) + 10;
                int h = rand.nextInt(100) + 10;
                int x = rand.nextInt(shapeCanvas.getWidth() - w);
                int y = rand.nextInt(shapeCanvas.getHeight() - h);
                Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
                addShape(ellipse);
            }
        };
        addEllipse.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_E);

        Action addRectangle = new AbstractAction("Rectangle") {

            @Override
            public void actionPerformed(ActionEvent e) {
                int w = rand.nextInt(100) + 10;
                int h = rand.nextInt(100) + 10;
                int x = rand.nextInt(shapeCanvas.getWidth() - w);
                int y = rand.nextInt(shapeCanvas.getHeight() - h);
                Rectangle2D rectangle = new Rectangle2D.Double(x, y, w, h);
                addShape(rectangle);
            }
        };
        addRectangle.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R);

        final int faceStart = 128513;
        final int faceEnd = 128528;
        final int diff = faceEnd - faceStart;
        StringBuilder sb = new StringBuilder();
        for (int count = faceStart; count <= faceEnd; count++) {
            sb.append(Character.toChars(count));
        }
        final String s = sb.toString();
        Vector<Font> compatibleFontList = new Vector<Font>();
        GraphicsEnvironment ge
                = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Font[] fonts = ge.getAllFonts();
        for (Font font : fonts) {
            if (font.canDisplayUpTo(s) < 0) {
                compatibleFontList.add(font);
            }
        }
        JComboBox fontChooser = new JComboBox(compatibleFontList);
        ListCellRenderer fontRenderer = new DefaultListCellRenderer() {

            @Override
            public Component getListCellRendererComponent(
                    JList list, Object value, int index,
                    boolean isSelected, boolean cellHasFocus) {
                Component c = super.getListCellRendererComponent(
                        list, value, index,
                        isSelected, cellHasFocus);
                JLabel l = (JLabel) c;
                Font font = (Font) value;
                l.setText(font.getName());
                return l;
            }
        };
        fontChooser.setRenderer(fontRenderer);
        final ComboBoxModel<Font> fontModel = fontChooser.getModel();

        BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = bi.createGraphics();
        final FontRenderContext fontRenderContext = g.getFontRenderContext();

        Action addFace = new AbstractAction("Face") {

            @Override
            public void actionPerformed(ActionEvent e) {
                int codepoint = faceStart + rand.nextInt(diff);
                String text = new String(Character.toChars(codepoint));

                Font font = (Font) fontModel.getSelectedItem();
                Area area = new Area(
                        font.deriveFont(80f).
                        createGlyphVector(fontRenderContext, text).
                        getOutline());
                Rectangle bounds = area.getBounds();
                float x = rand.nextInt(
                        shapeCanvas.getWidth() - bounds.width) - bounds.x;
                float y = rand.nextInt(
                        shapeCanvas.getHeight() - bounds.height) - bounds.y;
                AffineTransform move = AffineTransform.
                        getTranslateInstance(x, y);
                area.transform(move);
                addShape(area);
            }
        };
        addFace.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_F);

        Action delete = new AbstractAction("Delete") {

            @Override
            public void actionPerformed(ActionEvent e) {
                int idx = shapeSelectionModel.getMinSelectionIndex();
                if (idx < 0) {
                    JOptionPane.showMessageDialog(
                            ui,
                            "Select a shape to delete",
                            "Select a Shape",
                            JOptionPane.ERROR_MESSAGE);
                } else {
                    allShapesModel.removeElementAt(idx);
                    shapeCanvas.repaint();
                }
            }
        };
        delete.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_D);

        controls.add(new JButton(addEllipse));
        controls.add(new JButton(addRectangle));
        controls.add(new JButton(addFace));
        controls.add(fontChooser);
        controls.add(new JButton(delete));

        try {
            ArrayList<Shape> shapes = deserializeShapes();
            for (Shape shape : shapes) {
                allShapesModel.addElement(shape);
            }
        } catch (Exception ex) {
            System.err.println("If first launch, this is as expected!");
            ex.printStackTrace();
        }
    }

    private void addShape(Shape shape) {
        allShapesModel.addElement(shape);
        int size = allShapesModel.getSize() - 1;
        shapeSelectionModel.addSelectionInterval(size, size);
    }

    class ShapeCanvas extends JPanel {

        ShapeCanvas() {
            setBackground(Color.WHITE);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHints(renderingHints);
            Stroke stroke = new BasicStroke(1.5f);
            g2.setStroke(stroke);
            int idx = shapeSelectionModel.getMinSelectionIndex();
            Shape selectedShape = null;
            if (idx > -1) {
                selectedShape = allShapesModel.get(idx);
            }
            Enumeration en = allShapesModel.elements();
            while (en.hasMoreElements()) {
                Shape shape = (Shape) en.nextElement();
                if (shape.equals(selectedShape)) {
                    g2.setColor(new Color(0, 255, 0, 191));
                } else {
                    g2.setColor(new Color(255, 0, 0, 191));
                }
                g2.fill(shape);
                g2.setColor(new Color(0, 0, 0, 224));
                g2.draw(shape);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(500, 300);
        }
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                SerializeShapes se = new SerializeShapes();

                JFrame f = new JFrame("Serialize Shapes");
                f.addWindowListener(new SerializeWindowListener(se));
                f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                f.setContentPane(se.getUI());
                f.setResizable(false);
                f.pack();
                f.setLocationByPlatform(true);
                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }

    public void serializeShapes() throws FileNotFoundException {
        ArrayList<Shape> shapes
                = new ArrayList<Shape>();
        Enumeration en = allShapesModel.elements();
        while (en.hasMoreElements()) {
            Shape shape = (Shape) en.nextElement();
            shapes.add(shape);
        }
        ShapeIO.serializeShapes(shapes, this.getClass());
        try {
            Desktop.getDesktop().open(
                    ShapeIO.getSerializeFile(this.getClass()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ArrayList<Shape> deserializeShapes() throws FileNotFoundException {
        return ShapeIO.deserializeShapes(this.getClass());
    }

    class ShapeListCellRenderer extends DefaultListCellRenderer {

        @Override
        public Component getListCellRendererComponent(
                JList<? extends Object> list, Object value,
                int index, boolean isSelected, boolean cellHasFocus) {
            Component c = super.getListCellRendererComponent(list, value, index,
                    isSelected, cellHasFocus);
            JLabel l = (JLabel) c;
            Shape shape = (Shape) value;
            ShapeIcon icon = new ShapeIcon(shape, 40);
            l.setIcon(icon);
            l.setText("");

            return l;
        }
    }

    class ShapeIcon implements Icon {

        Shape shape;
        int size;

        ShapeIcon(Shape shape, int size) {
            this.shape = shape;
            this.size = size;
        }

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHints(renderingHints);
            Rectangle bounds = shape.getBounds();
            int xOff = -bounds.x;
            int yOff = -bounds.y;
            double xRatio = (double) bounds.width / (double) size;
            double yRatio = (double) bounds.height / (double) size;
            double ratio = xRatio > yRatio ? xRatio : yRatio;
            AffineTransform scale = AffineTransform.getScaleInstance(1 / ratio, 1 / ratio);
            AffineTransform shift = AffineTransform.getTranslateInstance(xOff, yOff);

            AffineTransform totalTransform = new AffineTransform();

            totalTransform.concatenate(scale);
            totalTransform.concatenate(shift);

            Area b = new Area(shape).createTransformedArea(totalTransform);
            bounds = b.getBounds();

            g2.setColor(Color.BLACK);
            g2.fill(b);
        }

        @Override
        public int getIconWidth() {
            return size;
        }

        @Override
        public int getIconHeight() {
            return size;
        }
    }
}

class SerializeWindowListener extends WindowAdapter {

    SerializeShapes serializeShapes;

    SerializeWindowListener(SerializeShapes serializeShapes) {
        this.serializeShapes = serializeShapes;
    }

    @Override
    public void windowClosing(WindowEvent e) {
        try {
            serializeShapes.serializeShapes();
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        System.exit(0);
    }
}

ShapeIO

Performs the I/O to/from XML.

package serialize2d;

import java.awt.Shape;
import java.beans.*;
import java.io.*;
import java.util.ArrayList;

public class ShapeIO {

    /** Save the list of shapes to the file system. */
    public static void serializeShapes(
            ArrayList<Shape> shapes, Class serializeClass) 
            throws FileNotFoundException {
        File f = getSerializeFile(serializeClass);
        XMLEncoder xmle = new XMLEncoder(new FileOutputStream(f));

        ArrayList<PathBean> pathSegmentsCollection = new ArrayList<>();
        for (Shape shape : shapes) {
            ArrayList<PathSegment> pathSegments = 
                    BeanConverter.getSegmentsFromShape(shape);
            PathBean as = new PathBean(pathSegments);
            pathSegmentsCollection.add(as);
        }

        xmle.writeObject(pathSegmentsCollection);
        xmle.flush();
        xmle.close();
    }

    /** Load the list of shapes from the file system. */
    public static ArrayList<Shape> deserializeShapes(Class serializeClass) 
            throws FileNotFoundException {
        File f = getSerializeFile(serializeClass);
        XMLDecoder xmld = new XMLDecoder(new FileInputStream(f));
        ArrayList<PathBean> pathSegmentsCollection
                = (ArrayList<PathBean>) xmld.readObject();
        ArrayList<Shape> shapes = new ArrayList<Shape>();
        for (PathBean pathSegments : pathSegmentsCollection) {
            shapes.add(BeanConverter.getShapeFromSegments(pathSegments));
        }

        return shapes;
    }

    /** Provide an unique, reproducible & readable/writable path for a class. */
    public static File getSerializeFile(Class serializeClass) {
        File f = new File(System.getProperty("user.home"));
        String[] nameParts = serializeClass.getCanonicalName().split("\\.");

        f = new File(f, "java");
        for (String namePart : nameParts) {
            f = new File(f, namePart);
        }
        f.mkdirs();
        f = new File(f, nameParts[nameParts.length-1] + ".xml");

        return f;
    }
}

BeanConverter

Obtains a PathIterator from the Shape and converts it to a serializable bean. Converts the bean back into a GeneralPath.

package serialize2d;

import java.awt.Shape;
import java.awt.geom.*;
import java.util.ArrayList;

/** Utility class to convert bean to/from a Shape. */
public class BeanConverter {

    /** Convert a shape to a serializable bean.  */
    public static ArrayList<PathSegment> getSegmentsFromShape(Shape shape) {
        ArrayList<PathSegment> shapeSegments = new ArrayList<PathSegment>();
        for (
                PathIterator pi = shape.getPathIterator(null); 
                !pi.isDone(); 
                pi.next()) {
            double[] coords = new double[6];
            int pathSegmentType = pi.currentSegment(coords);
            int windingRule = pi.getWindingRule();
            PathSegment as = new PathSegment(
                    pathSegmentType, windingRule, coords);
            shapeSegments.add(as);
        }
        return shapeSegments;
    }

    /** Convert a serializable bean to a shape.  */
    public static Shape getShapeFromSegments(PathBean shapeSegments) {
        GeneralPath gp = new GeneralPath();
        for (PathSegment shapeSegment : shapeSegments.getPathSegments()) {
            double[] coords = shapeSegment.getCoords();
            int pathSegmentType = shapeSegment.getPathSegmentType();
            int windingRule = shapeSegment.getWindingRule();
            gp.setWindingRule(windingRule);
            if (pathSegmentType == PathIterator.SEG_MOVETO) {
                gp.moveTo(coords[0], coords[1]);
            } else if (pathSegmentType == PathIterator.SEG_LINETO) {
                gp.lineTo(coords[0], coords[1]);
            } else if (pathSegmentType == PathIterator.SEG_QUADTO) {
                gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
            } else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
                gp.curveTo(
                        coords[0], coords[1], coords[2], 
                        coords[3], coords[4], coords[5]);
            } else if (pathSegmentType == PathIterator.SEG_CLOSE) {
                gp.closePath();
            } else {
                System.err.println("Unexpected value! " + pathSegmentType);
            }
        }
        return gp;
    }
}

PathBean

Stores a collection of path segments in a seriallizable bean.

package serialize2d;

import java.awt.geom.*;
import java.util.ArrayList;

/** PathBean stores the collection of PathSegment objects
 that constitute the path of a Shape. */
public class PathBean {

    public ArrayList<PathSegment> pathSegments;

    public PathBean() {}

    public PathBean(ArrayList<PathSegment> pathSegments) {
        this.pathSegments = pathSegments;
    }

    public ArrayList<PathSegment> getPathSegments() {
        return pathSegments;
    }

    public void setPathSegments(ArrayList<PathSegment> pathSegments) {
        this.pathSegments = pathSegments;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("{");
        for (PathSegment pathSegment : pathSegments) {
            sb.append(" \n\t");
            sb.append(pathSegment.toString());
        }
        sb.append(" \n");
        sb.append("}");
        return "PathSegments: " + sb.toString();
    }
}

PathSegment

Stores the path segment of one part of the entire path.

package serialize2d;

import java.util.Arrays;

/** PathSegment bean stores the detail on one segment of the path
 that constitutes a Shape. */
public class PathSegment {

    public int pathSegmentType;
    public int windingRule;
    public double[] coords;

    public PathSegment() {}

    public PathSegment(int pathSegmentType, int windingRule, double[] coords) {
        this.pathSegmentType = pathSegmentType;
        this.windingRule = windingRule;
        this.coords = coords;
    }

    public int getPathSegmentType() {
        return pathSegmentType;
    }

    public void setPathSegmentType(int pathSegmentType) {
        this.pathSegmentType = pathSegmentType;
    }

    public int getWindingRule() {
        return windingRule;
    }

    public void setWindingRule(int windingRule) {
        this.windingRule = windingRule;
    }

    public double[] getCoords() {
        return coords;
    }

    public void setCoords(double[] coords) {
        this.coords = coords;
    }

    @Override
    public String toString() {
        String sC = (coords != null ? "" : Arrays.toString(coords));
        String s = String.format(
                "PathSegment: Path Segment Type:- %d \t"
                + "Winding Rule:- %d \tcoords:- %s",
                getPathSegmentType(), getWindingRule(), sC);
        return s;
    }
}

Notes

This is intended as a proof of concept as opposed to a polished approach.

  • XML serialized data becomes big real fast, it would normally be zipped. Zip compression might shave 30-40% off the byte size of a serialized object or a class file, but 80-95% off XML. In any case, zip works well for the next point as well.
  • For the type of project where we wish to offer to serialize and restore shapes, we'll probably also want to include more details of the shapes (e.g. fill color or texture and draw color or stroke etc.) as well as other data like images or fonts. This is also where Zip comes in handy, since we can put them all in the same archive, each with best levels of compression (e.g. standard for the XML and none for images).

A zip archive of the source files in this answer can be downloaded from my cloud drive.

这篇关于如何序列化的Java 2D Shape对象为XML?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆