画图板再改造之重绘和界面美观化


 在前面的一篇博客中发表了一篇关于简单画图板的实现的博客,那个画图板的功能不多,而且界面不美观,
最重要的缺陷在于当我们最小化画图板后再最大化的时候,之前我们画的所有的东西都不见了,主要原因是当  我们再次最大化画板的时候系统会自动调用画板所继承的父类中的paint()方法,从而将整个画板都重新初始化的绘制了一下,所以我们原来的东西就不见了,所以我们如果想实现不管怎么操作画板(移动,最小化,最大化)绘制的图片还存在画板上的话,首先是定义一个队列来保存画过的图形,然后就是要重写画图面板的paint()方法(这里要注意的是:一定要重写我们绘画的区域的paint()方法,比如是JFrame的的重写JFrame的paint()方法,如果是JPanel的就重写JPanel的paint() 方法,要是paint()方法的位置写错了的话,这次还有改进的是界面的美观化,仿造了Windows系统下的画图板的界面,虽然还离得比价远,但是比原先的要美观了许多,以下将具体分析代码和所实现的功能,以及自己在实现过程中曾经遇到的问题

1:画图板界面的设计,主要分为两个区域(north_Panel),一个是功能选择区(包括颜色,形状的选择,橡皮擦,保存图片,打开图片(这两个功能尚未实现,会在后面继续完善),清空画图区所画的图片,还有显示所选颜色的显示,另外一个区域就是画图区(south_Panel),总体的显示如下图:

<!--StartFragment -->


 

先说颜色选择区域,使用的全是按钮组件,在单选颜色区给每个按钮设置背景颜色,并且使用setBounds()方法(必须先将north_Panel的布局不要成默认的,操作如下north_Panel.setLayout(null)将每个按钮安放到指定的位置,具体代码如下(由于其颜色按钮的代码都一样,只取一部分代码):

  JButton color1 = new JButton();
  JButton color2 = new JButton();
  JButton color3 = new JButton();

  .......

  color1.setBounds(700, 40, 25, 25);
  color1.setBackground(Color.blue);
  color2.setBounds(727, 40, 25, 25);
  color2.setBackground(Color.black);
  color3.setBounds(752, 40, 25, 25);
  color3.setBackground(Color.gray);

  .......

  其他的按钮都是使用相同的方法,只是不是给每个按钮设置背景颜色,而是给每个按钮设置一张背景图片,本人画图板的按钮的图片都是截图,然后要保存在所建的工程下面如图:



 插入到具体按钮下的代码如下(只取一部分):

  JButton type1 = new JButton();
  type1.setBounds(320, 20, 39, 37);
  JButton type2 = new JButton();
  type2.setBounds(360, 20, 39, 37);

  ......

  ImageIcon Type_icon1 = new ImageIcon("type1.jpg");
  ImageIcon Type_icon2 = new ImageIcon("type2.jpg");

 ........

  type1.setIcon(Type_icon1);
  type2.setIcon(Type_icon2);
 此处需要注意的时候就是ImageIcon()的参数是一个字符串,必须保持与保存在工程文件中的图片的名字一样

 画图板的north_Panel的最左边是一个自己定义的面板colorUI,用来实现显示所选的颜色,初始默认为黑色,代码如下:

  JPanel colorUI = new JPanel();
  colorUI.setBounds(920, 30, 50, 50); // 显示所选的颜色的面板,初始化为black,
  colorUI.setBackground(Color.black);
  north_Panel.add(colorUI);

 功能区域下面是画图区域,定义背景颜色为白色,代码如下:

  south_Panel.setBackground(Color.white);

 

  整个窗体使用的是BorderLayout布局管理,起初设置north_Panel的大小,然后将south_Panel布局在中间(就默认充满剩余的区域了)代码如下:

  north_Panel.setPreferredSize(new Dimension(0, 150)); //窗体外的组件的大小设置只能用setPreferredSize

                                                                                           //(0,150)表示只要设置宽度就行了,长度默认

  this.add(north_Panel, BorderLayout.NORTH);
  this.add(south_Panel, BorderLayout.CENTER);

 

 下面介绍每个按钮的功能背后是如何实现的:

  1 .给每个按钮设置一个命令,就相当于点击后产生的命令,示例代码如下:

  

   color1.setActionCommand("blue");
   color2.setActionCommand("black");

  ........

  2.创建一个动作监听器 ,用来监听每个动作,代码如下:
    public class DrawPanelActionLis implements ActionListener { 

          public void actionPerformed(ActionEvent e);

   }

 

  3.给每个按钮加上 动作监听器,这就要在主类中生成一个动作监听器对象,然后给每个按钮加上代码如下:

   DrawPanelActionLis dPAc = new DrawPanelActionLis();

   color1.addActionListener(dPAc);
   color2.addActionListener(dPAc);

   .........

 

4.在动作监听器内实现ActionListener接口的方法,通过e.getActionCommand()方法获取各个按钮的命令,通过判断来实现点击按钮后所实现的操作,示例代码:

       public void actionPerformed(ActionEvent e) {    

                colorCommand = e.getActionCommand();      //获取命令
       if(colorCommand.equals("blue")) {                            //进行判断
      color = Color.blue;                                                      //进行所选颜色的赋值
      jp1.setBackground(color);                                          //将选中的颜色设置为“所选颜色”面板的背景颜色
     }else if(colorCommand.equals("black")) {
      color = Color.black;
      jp1.setBackground(color);
     }else if(colorCommand.equals("gray")) {
      color = Color.gray;
      jp1.setBackground(color);
     }else if(colorCommand.equals("red"))

      .........

      //点击更多颜色时会弹出调色板,可以选择颜色,调色板的使用方法代码如下:

      else if(colorCommand.equals("moreColor")) {  
      color = JColorChooser.showDialog(null, "请选择你所需的颜色",Color.black);//这是调色板的的一些信息
      jp1.setBackground(color);                //也会将选择的颜色显示到“所选颜色”面板上
     }

    效果如下:

    

 

 

      //点击相应按钮能实现相应的操作的方法都是写在此方法内,在些按钮面有个“清空图片”的操作,本人

      //使用的是用画布背景色画一个充满画布的满圆来覆盖原来的图片,代码如下:

      clearAllCommand = e.getActionCommand();
          if(clearAllCommand.equals("clear")) {
                                                                            //清空图片 ,
          g.setColor(Color.white);
          g.fill3DRect(0, 0, 1000,750,true);

    }

 

       这里就存在问题了,在我们给“所选颜色”面板(jp1)设置背景颜色和画满圆时,在DrawPanelActionLis里面没有画布对象g(g是在前面向系统获取画画权限所生成的(Graphics g = south_Panel.getGraphics(); // 这样才可以在south_Panel上进行绘图),所以就要进行传参的操作代码如下:

   JPanel colorUI = new JPanel();

   Graphics g = south_Panel.getGraphics();
   DrawPanelActionLis dPAc = new DrawPanelActionLis(colorUI, g);

 

然后在DrawPanelActionLis中就要重载 构造方法:

 

 private JPanel jp1;
 private Graphics g;
 public DrawPanelActionLis(JPanel jp1,Graphics g) {
  this.jp1 = jp1;
  this.g = g;
 }

 

接下来就是获得画图点的坐标了  我定义的是类MymouseAdlistener继承抽象类 MouseAdapter 取出MouseAdapter中 的方法

      public void mousePressed(MouseEvent e) {};
      public void mouseReleased(MouseEvent e) {};

来获取点的坐标,并通过DrawPanelActionLis的对象来获取所选的颜色和形状(这就首先需要在DrawPanelActionLis中定义两个返回所选的颜色和图形形状的方法):

//定义返回所选颜色的方法

 private Color color;
 public Color getColor() {
  return color;
 }
 
 //定义返回所选的图形
 private int shapeStyle;  //将对应的图形用数字表示
 public int getShape() {
  return shapeStyle;
}

 

 

接下来需要创建一个MymouseAdlistener的对象然后将south_Panel添加到MouseAdapter(其中包含三个监听器,自由选择)中的MouseListener监听器下面,代码:  MymouseAdlistener myadlistener = new MymouseAdlistener(g, dPAc);      south_Panel.addMouseListener(myadlistener);  然后就是绘图了 ,首先我先定义了一个Myshape类 ,里面有一个draw()方法,判断type的值来绘制不同的图形(前面说明已将各种图形用数字代替了),在MymouseAdlistener中通过DrawPanelActionLis的对象调用getShape() 和 getColor() 方法返回颜色和图形形状选择,再在MymouseAdlistener中创建Myshape对象调用Myshape中的draw()方法,具体代码如下:
import java.awt.Color;
import java.awt.Graphics;

/**
 * 定义图形类
 * @author sony
 *
 */
public class Myshape {
 

    private int type;
    private int x1,x2,y1,y2;
    private Graphics g;

     private Color color;
    public Graphics getG() {
          return g;
 }

 public void setG(Graphics g) {
        this.g = g;
 }
    public int getType() {
       return type;
 }

 public void setType(int type) {
       this.type = type;
 }
    public Color getColor() {
          return color;
 }

 public void setColor(Color color) {
         this.color = color;
 }
 public int getX1() {
  return x1;
 }

 public void setX1(int x1) {
  this.x1 = x1;
 }

 public void setX2(int x2) {
  this.x2 = x2;
 }

 public int getX2() {
  return x2;
 }
 public void setY1(int y1) {
  this.y1 = y1;
 }
 public void setY2(int y2) {
  this.y2 = y2;
 }
 public int getY1() {
  return y1;
 }
 public int getY2() {
  return y2;
 }
 public void draw()  {                         //通过type的数值来进行判断画什么形状
  if(type == 1) {
   //画right 箭头

   g.setColor(color);                            // 通过Myshape的对象shape来返回所选的图形的颜色
   ......
  }
if(type == 2) {
   //画左箭头

  g.setColor(color);                            

  ......
  }

  ......

 在上面我将橡皮擦的功能设置在type=10时画的图形  我的橡皮擦比较简单就是用g.drawfillOval的方法,将填充色改成画板的背景色代码如下:

if(type == 10) {
   g.setColor(Color.white);          //画板的背景色为白色,所以用白色来画满圆来起到橡皮擦的作用
   g.fillOval(Math.min(x1, x2), Math.min(y1, y2),Math.abs(x1-x2), Math.abs(y1-y2));
  }

 

 

我们知道坐标是在MymouseAdlistener中通过

public void mousePressed(MouseEvent e) {
   //得到起始点坐标
  x1 = e.getX();
  y1 = e.getY();  
  }
 
  public void mouseReleased(MouseEvent e) {
   //得到终点坐标
   x2 = e.getX();
   y2 = e.getY();

}

得到的,要传到Myshape类中实现画图就要用在MymouseAdlistener中定义的Myshape类的shape对象将得到的坐标送到Myshape中的x1,x2,y1,y2,中去,还要将画布对象也传过去,代码如下:

Myshape shape = new Myshape();
   shape.setG(g);                       //把Graphics g = south_Panel.getGraphics();得到的g传到                            
   shape.setX1(x1);                    //MymouseAdlistener  中然后通过shape.setG(g)传到Myshape中
   shape.setY1(y1);
   shape.setX2(x2);                  //将坐标传到Myshape 中
   shape.setY2(y2);
   shape.setColor(d.getColor());      //将颜色选择和图形选择传到Myshape中
   shape.setType(d.getShape());
   shape.draw();                            //调用Myshape中的draw()方法   

 

现在最重要的事就出现了  我们想要实现的是重绘,所以我们所要做的就是把我们已经画的图保存下来,所以我们要在MymouseAdlistener中定义一个Myshape类型的队列用来保存我们所画过的图的信息(包括颜色,图形类型)利用List的add()方法将每个shape对象添加到队列中去所以总的实现画了之后并保存下来的代码为:

 List<Myshape> list = new ArrayList<Myshape>();

public MymouseAdlistener(Graphics g,DrawPanelActionLis d) {     //将要用到的对象传过来
   this.g = g;
   this.d = d;
  }
  public void mousePressed(MouseEvent e) {
   //得到起始点坐标
  x1 = e.getX();
  y1 = e.getY();  
  }
  public void mouseReleased(MouseEvent e) {
   //得到终点坐标
   x2 = e.getX();
   y2 = e.getY();
  
   //把图形的信息封装到一个对象中
   Myshape shape = new Myshape();
   shape.setG(g);
   shape.setX1(x1);
   shape.setY1(y1);
   shape.setX2(x2);
   shape.setY2(y2);
   shape.setColor(d.getColor());
   shape.setType(d.getShape());
   shape.draw();
   list.add(shape);          //队列list对象调用add()方法将当前的shape添加到队列中
  }

  放进去后要实现重绘的时候就要从队列中依次把每个元素取出来实现重绘所以还要定义一个返回 队列中元素的方法:

public Myshape getShape(int i) {          //队列中的每一个元素都是Mysahpe 类型的
  return list.get(i);
 }

 

好!这样想实现 重绘就差一步了,我们要就只要像开头说的那样,重写我们画布区域south_Panel中的paint()方法了,这都是固定的格式,代码如下:

 

JPanel sorth_Panel = new JPanel() {

           public void paint(Graphics g) {                    //这是每个组件的都有的一个固有的方法
               super.paint(g);                                         //首先要重写父类的paint()方法

        for (int i = 0; i < myadlistener.list.size(); i++) {        //使用循环实现每个队列中的图像的再绘制
                  Myshape shape = myadlistener.getShape(i);    //获得队列中的元素

                  if (shape.getType() == 5) {                                  //通过判断实现再绘制
                  g.setColor(shape.getColor());                             //获得队列中的相对应元素的颜色
                  g.drawLine(shape.getX1(), shape.getY1(), shape.getX2(),  //获取坐标,实现重绘
                  shape.getY2());

                 } else  if (........) {
                ......                                                                            //相同的方法

                }

                .......

           }

      }

}

//  myadlistener      说明:    应该定义为类的一个变量,这样才能在paint()f方法中调用    即:   private MymouseAdlistener   myadlistener  所以将上面south_Panel 加MouseListener处的代码改成如下:

          private MymouseAdlistener  myadlistener ;    //这个变量可以在类里面用

          myadlistener = new MymouseAdlistener(g, dPAc);

这样myadlistener就可以在paint() 方法中调用了

 

最后总结一下完成这个画图板后的一些收获,虽然画图板是很基本的一个小项目,但是这里面所包含的一些知识点 确是很重要的,其中各个类之间的传参就很值得我们去学习,就这么几个类就把我传晕了,基本功还不是很扎实,在传参这部分还有待加强,还有就是布局的收获也挺大的,一开始就走错路了,搞得后面就只能一直使用 setBounds()方法来摆放各种组件,都是在错误中成长的嘛!这是学java一个多月以来算是做的最久的一个小项目吧,写博客也没什么经验,里面肯定还有很多错误的地方,慢慢改进吧。还有保存和打开的功能也还没能实现,还需改进。下面是代码 运行的效果:



 

 

点击橡皮擦后擦点一部分后的效果:



 

 

点击最小画和最大化后的效果如下:




 
 点击清空后的效果如下:





 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值