java画板的设计和创建

首先创建一个画板类继承容器类,这样可以在画板类中重写容器的paint方法。

public class DrawFrame extends JPanel

再创建一个类写监听器的程序,以及一个类来写画板的内容对象参数保存的方法(可以在最小化以及伸缩窗口的时候使画面内容得到恢复)

public class DrawMouse implements MouseListener, ActionListener,MouseMotionListener 
public class Shape

我们可以看到我们会用到鼠标监听器,动作监听器,以及鼠标动作监听器。

我们先简单地介绍一下他们的作用:

1.鼠标监听器可以对于点击,按下,释放等事件做出响应,响应具体内容由程序员自己定义,发生相应的行为时即会触发对应方法,由此可见,监听器的作用很大,可以实现无限的可能。

2.动作监听器我们目前用于响应对按钮的点击事件,读取按钮上的名称或者颜色信息来实现相应的方法。

3鼠标动作监听器可以实现对鼠标进入界面,离开界面,界面内拖拽等功能的实现,极大丰富了界面特殊功能的实现。

接下来进入主程序,主程序很简单,只是一个窗体构造方法的调用。

public static void main(String[] args) {
            DrawFrame frame = new DrawFrame();
            frame.showUI();
        }
            javax.swing.JFrame jf = new javax.swing.JFrame();
            jf.setSize(800, 800);
            jf.getContentPane().setBackground(Color.WHITE);//设置背景色
            jf.setTitle("画板1.0");
            jf.setDefaultCloseOperation(3);
            // 设置居中显示
            jf.setLocationRelativeTo(null)            
            DrawMouse mouse = new DrawMouse();
            jf.setLayout(new BorderLayout());            
            JPanel jp1= new JPanel();
            jp1.setBackground(Color.green);
            jf.add(jp1,BorderLayout.NORTH);

在showUI里先是对一些基本组件的添加和创建,这里就不再一一赘述。值得一提的是其中容器的添加以及边框布局的规则。jf就是一个巨大的容器对象,一般来说,如果我们不定义流式布局,便会默认边框布局。边框布局可以把区域划分为5个部分,north,south,center,east,west。我们把按钮放在了上方,把画板主题部分放在中间,这样两个区域便互不干扰,是两个独立的容器。这些对象的父类便是JPanel类。

            this.setBackground(Color.WHITE);
            jf.add(this, BorderLayout.CENTER);

使用this即可调用,默认以容器类来创建对象(容器可以创建多个对象)。

String[] shape ={"直线","矩形","三角形","椭圆","任意边形","曲线","橡皮擦","迭代图像","递归"};
            for(int i=0;i<shape.length;i++){
                JButton jbu = new JButton(shape[i]);
                jp1.add(jbu);
                jbu.addActionListener(mouse);
            }
            Color[] color = {Color.RED,Color.BLUE,Color.BLACK};
            for(int i=0;i<color.length;i++){
                JButton jbu = new JButton();
                jbu.setBackground(color[i]);
                jbu.setPreferredSize(new Dimension(30, 30));
                jp1.add(jbu);
                jbu.addActionListener(mouse);
            }

循环创建按钮可以在多按钮需求的时候剩下不少力气。循环创建按钮并为其添加动作监听器。

            jf.setVisible(true);            
            //获取画笔对象:图形画在那个组件上,画笔就从该组件上获取
            //从窗体上获取画笔对象            
            Graphics g = this.getGraphics();   
            //给窗体添加鼠标监听器方法
            this.addMouseListener(mouse);
            this.addMouseMotionListener(mouse);
            mouse.setGr(g);
            mouse.setArrayShape(arrayShape);

窗体可视化并获取画笔对象后我们添加鼠标监听器。这里用到了传对象的方法,来使得在监听器的类中可以使用画笔和对象数组。对象数组的创建是为了保存每一个图画信息的坐标及颜色信息,便于复原。

    private Graphics gr;
    private int x1, y1, x2, y2, x3, y3;
    private double x;
    private double y;    
    private int xf,yf,xl,yl,x0,y0;
    private String name;
    private int flag=0;
    private int flagq=0;
    private int step=1;
    private int flag_line=0;
    private Color color = Color.black;
    private double a=-1.2;
    private double b=1.6;
    private double c=-1;
    private double d=-1.5;
    private int index=0;
    private Shape[] arrayShape;

在监听器里定义了很多属性,这些属性在编程实现特殊功能的时候十分有用,可作为开关,可用于传递对象,可作为方法内的参数来调用等,这里不一一赘述。

    public void mouseClicked(MouseEvent e) {        
        if ("任意边形".equals(name)){
            if(flag==0)
            {
                x0 = e.getX();
                y0 = e.getY();
                xf = e.getX();
                yf = e.getY();                
                flag=1;
            }
            else
            {
                xl = e.getX();
                yl = e.getY();
                gr.drawLine(xf, yf, xl, yl);
                set_shape(xf, yf, xl, yl,"直线",color);
                xf=xl;
                yf=yl;
                if(e.getClickCount()==2)
                {
                    flag=0;
                    gr.drawLine(x0, y0, xl, yl);
                    set_shape(x0, y0, xl, yl,"直线",color);
                }
            }          
        }
        if("三角形".equals(name)){
            System.out.println(step); 
               switch(step)
               {
               case 1:
                   x1=e.getX();
                   y1=e.getY();
                   step++;
                   break;
               case 2:
                   x2=e.getX();
                   y2=e.getY();
                   gr.drawLine(x1, y1, x2, y2);
                   step++;
                   break;
               case 3:
                   x3=e.getX();
                   y3=e.getY();
                   set_shape(x1,y1,x2,y2,"直线",color);
                   set_shape(x2,y2,x3,y3,"直线",color);
                   set_shape(x1,y1,x3,y3,"直线",color);
                   step=1;
                   gr.drawLine(x2, y2, x3, y3);
                   gr.drawLine(x1, y1, x3, y3);
                   break;           
               default:;
               }               
        }
        if("迭代图像".equals(name))
        {
            x=e.getX();
            y=e.getY();
            iterate(x,y); 
            //set_shape((int)x, (int)y, 0, 0,"迭代图像",color);
        }
        if("递归".equals(name))
        {
              int xa=1+(int)(Math.random()*800);
              int ya=1+(int)(Math.random()*800);
              int xb=1+(int)(Math.random()*800);
              int yb=1+(int)(Math.random()*800);
              int xc=1+(int)(Math.random()*800);
              int yc=1+(int)(Math.random()*800);
              int xp=1+(int)(Math.random()*800);              
              int yp=1+(int)(Math.random()*800);        
              int turn=1;
              System.out.println("xa"+xa+"ya"+ya+"xb"+xb+"yb"+yb+"xc"+xc+"yc"+yc+"xp"+xp+"yp"+yp);
              for(int i=0;i<10000;i++)
              {
                  turn=1+(int)(Math.random()*3);
                  switch(turn)
                  {
                  case 1:                      
                      xp=(xa+xp)/2;
                      yp=(ya+yp)/2;
                      gr.drawLine(xp, yp, xp, yp);
                      break;
                  case 2:                      
                      xp=(xb+xp)/2;
                      yp=(yb+yp)/2;
                      gr.drawLine(xp, yp, xp, yp);                      
                      break;
                  case 3:                      
                      xp=(xc+xp)/2;
                      yp=(yc+yp)/2;
                      gr.drawLine(xp, yp, xp, yp);                  
                      break    ;              
                  }                                
              }            
        }
    }

在click事件里获取坐标值,再判断当前name类型,实现画三角形和画任意边形等操作。click 双击还可自动补全缺线,结束多边形的绘制。因为鼠标事件的实现非常复杂,且与按下,释放联系紧密,单独介绍不便于一一解释,我们就提一些有趣的想法,然后剩下的就留读者思考和设计创造了。

public void mousePressed(MouseEvent e) {
        System.out.println("按下");
        if("矩形".equals(name)||"椭圆".equals(name))
        {
        x1 = e.getX();
        y1 = e.getY();
        }
        //System.out.println(name);
        if ("曲线".equals(name))
        {
            flagq=1;
            //System.out.println(flagq);
            xf=e.getX();
            yf=e.getY();
        }
        if("直线".equals(name))
        {
            x0=e.getX();
            y0=e.getY();            
            x1=e.getX();
            y1=e.getY();           
            flag_line=1;            
        }
    }
    public void mouseReleased(MouseEvent e) {
        System.out.println("释放");
        
        if("矩形".equals(name)||"椭圆".equals(name))
        {
        x2 = e.getX();
        y2 = e.getY();
        }
        /*if ("直线".equals(name)) {
            // 画线
            gr.drawLine(x1, y1, x2, y2);
        }*/
        if ("矩形".equals(name)) {
            gr.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
            set_shape(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2),"矩形",color);            
        }
        if("椭圆".equals(name)){
            gr.drawOval(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x1-x2),Math.abs(y1-y2));
            set_shape(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x1-x2),Math.abs(y1-y2),"椭圆",color);            
        }
        if ("曲线".equals(name))
        {
            flagq=0;
            System.out.println(flagq);
        }
        if("直线".equals(name))
        {
            flag_line=0;
            set_shape(x0,y0,x2,y2,"直线",color);
        }      
      }

按下和释放可以获取两个坐标,可以轻松地画出直线,矩形等形状,三角形和多边形的实现用到了click事件(这里关于x1,y1,x2,y2,x3,y3变量的使用十分巧妙,可以作为前一个点,和后一个点来理解,实现连续画多线)。

    public void actionPerformed(ActionEvent e) {              
        if("".equals(e.getActionCommand())){
            //获取当前事件源
            JButton jbu = (JButton)e.getSource();
            //获取按钮的背景色
            color = jbu.getBackground();
            //设置画笔颜色
            gr.setColor(color);
        }else{
            // 获取按钮内容
            name = e.getActionCommand();
        }        
        System.out.println("name = " + name);
    }

按钮功能的实现代码贴出。

   public void mouseDragged(MouseEvent e)
    {
        //System.out.print("x坐标:"+e.getX()+"  y坐标:"+e.getY());
        if (flagq==1)
        {
            xl=e.getX();
            yl=e.getY();
            gr.drawLine(xf, yf, xl, yl);
            set_shape(xf, yf, xl, yl,"直线",color);
            xf=xl;
            yf=yl;
        }
        if("橡皮擦".equals(name))
        {
            x1=e.getX();
            y1=e.getY(); 
            gr.setColor(Color.white);
            gr.fillRect(x1-5, y1-5, 10, 10);
            gr.setColor(color); 
            //gr.clearRect(x1-5, y1-5, 10, 10);
            set_shape(x1-5, y1-5, 10, 10,"橡皮擦",color);          
        }
        if("直线".equals(name)&&flag_line==1)
        {
            x2=e.getX();
            y2=e.getY();
            gr.setColor(Color.white);
            gr.drawLine(x0, y0, x1, y1);                    
            gr.setColor(color);                      
            gr.drawLine(x0, y0, x2, y2);            
            x1=x2;
            y1=y2;            
        }      
    }

这段代码有两个地方很有意思,一个是直线的绘制。我最初使用的是press和release,这样在鼠标拖动的过程中直线是不可见的,我们把其在鼠标拖动事件中实现,及在按下的时候保存坐标初值,然后不断获取鼠标位置,画新的线,用背景色覆盖原先的线,实现直线的实时显示。还有一个就是画曲线,及不断保存前后点的坐标,画很多短线,实现曲线的效果。

 private Shape[] arrayShape=new Shape[9000];
    public void set_shape(int x1,int y1,int x2,int y2,String name,Color color)
    {        
        Shape shape=new Shape(x1,y1,x2,y2,name,color);                         
        arrayShape[index++] = shape;       
    }

关于图像的复原,我们的思路是把每一步设置为对象保存在shape中,然后当最小化再打开,或者拖拽窗体时,再调用这个数组取出其中的内容,实现画面的恢复。

     public Shape(int x1,int y1,int x2,int y2,String name,Color color)
     {
         this.x1=x1;
         this.y1=y1;
         this.x2=x2;
         this.y2=y2;
         this.name=name;
         this.color=color;
     }
    
     public void drawShape(Graphics g)
     {
         switch(name)
         {
         case "直线":
             g.setColor(color);
             g.drawLine(x1, y1, x2, y2);
            // System.out.println("画");
             break;
         case "矩形":
             g.setColor(color);
             g.drawRect(x1, y1, x2, y2);
             break;
         case "椭圆":
             g.setColor(color);
             g.drawOval(x1, y1, x2, y2);
             break;        
         case "橡皮擦":
             g.setColor(color);
             g.clearRect(x1, y1, x2, y2);
             break;         
         }
        public void paint(Graphics g)
        {
            super.paint(g);           
            for(int i=0;i<arrayShape.length;i++)
            {
                Shape shape=arrayShape[i];
                if(shape!= null)
                    shape.drawShape(g);
                else break;
            }           
        }

这是涉及到参数保存和取出的方法。

以上代码便实现了我们的画板了,以画板为基础我们接下来还可以实现很多界面的设计,在画板的学习过程中充分使用了监听器这个工具,程序的可设计性大大提高了。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值