Java小程序之高级画板图形保存篇


Java小程序之高级画板图形保存篇

前言:前面已经完成高级的UI篇,功能篇以及重绘篇,我们的画板已经具有良好的用户交互界面,很多的功能也已经实现,并且也解决了重绘的问题;如果某天你用我们的画板绘制了一个非常漂亮的图形,想要保存下来怎么办呢?

今天,就让我们来实现画板图形的保存吧!

那么我们该如何将绘制的图形进行保存呢?首先,需要明白的是,我们绘制的图形是一个个具体的对象,有直线对象,椭圆对象等,并且这些对象都保存在了容器里面,而容器也是一个大的对象;还记得我们前面对象写的实例吗?我们成功的将两个学生对象保存到了文件中,并且也成功的将学生对象的信息从文件中读了出来;相信聪明的你已经知道该怎么保存我们绘制了图形了吧!没错,就是要利用对象流进行图形的保存;

思路:
1、给我们的画板添加菜单条、菜单以及菜单项
2、给每个菜单项添加监听器
3、点击不同的菜单项实现相应的功能
3.1图形保存功能:利用对象输出流,将容器对象写入文件;
3.2打开图形功能:利用对象输入流,将容器对象读入,然后将容器里面的图形对象在画板上绘制出来
3.3新建文件功能:新建文件,将画板上绘制的内容清空(清空之前可以确认是否需要进行保存)


源代码:
在这个类中添加菜单条、菜单、菜单项以及监听器
package com.huaxin.zhou1;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;


public class DrawBorder extends JFrame{
	
	//声明颜色属性,并赋默认值
	public Color c=Color.RED;
	//按钮属性,便于其他类访问
	public JButton  bt ;
	
	public JPanel panelcenter;
	
	public Graphics2D g;
	
	//容器
	ArrayList<Shape> list = new ArrayList<Shape>();

	public void initFrame()throws Exception{
		
		//设置窗体相关属性
		this.setSize(600,500);
		this.setTitle("我的画板");
		this.setDefaultCloseOperation(3);
		this.setLocationRelativeTo(null);
		
		//把添加菜单作为一个方法封装起来
		addMenu();
		
		//窗体添加主面板
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		this.add(panel);
		
		
			panelcenter = new JPanel(){
			public void paint(Graphics g1) {
					g=(Graphics2D)g1;
				//画船体
				super.paint(g);
				for (int i = 0; i <list.size(); i++) {
					Shape shape =(Shape)list.get(i);
					shape.Draw(g);
				}
			}
		};
	    panelcenter.setBackground(Color.white);
		panel.add(panelcenter);
		
		//主面板添加左面板
		JPanel panelleft = new JPanel();
		panelleft.setPreferredSize(new Dimension(50,0));
		panelleft.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
		panelleft.setBackground(new Color(235,233,238));
		panel.add(panelleft,BorderLayout.WEST);
		
		//面板中添加按钮
		//按钮归类,统一管路
		ButtonGroup bg = new ButtonGroup();
		for(int i=0;i<16;i++){
			JRadioButton jrb = new JRadioButton();
			
			//给按钮添加图片
			ImageIcon img1  = new ImageIcon("images/draw"+i+".jpg");
			ImageIcon img2  = new ImageIcon("images/draw"+i+"-1.jpg");
			ImageIcon img3  = new ImageIcon("images/draw"+i+"-2.jpg");
			ImageIcon img4  = new ImageIcon("images/draw"+i+"-3.jpg");
			jrb.setIcon(img1);
			jrb.setRolloverIcon(img2);
			jrb.setPressedIcon(img3);
			jrb.setSelectedIcon(img4);
			jrb.setBorder(null);
			//设置默认选中的按钮
			if(i==10){
				jrb.setSelected(true);
			}
			jrb.setActionCommand("pic"+i);
			
			bg.add(jrb);
			panelleft.add(jrb);
		}
		
		//主面板添加下方面板
		JPanel paneldown =new JPanel();
		paneldown.setPreferredSize(new Dimension(0,60));
		paneldown.setLayout(null);
		paneldown.setBackground(Color.gray);
		panel.add(paneldown, BorderLayout.SOUTH);
		
		//下方面板添加子面板
		JPanel paneldownchild = new JPanel();
		paneldownchild.setBackground(Color.cyan);
		paneldownchild.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
		paneldownchild.setBounds(10,10,280,40);
		paneldown.add(paneldownchild);
		
		//按钮特效
		BevelBorder bb = new BevelBorder(0, Color.gray,Color.white);
		BevelBorder bb1 = new BevelBorder(1, Color.gray,Color.white);
		
		JPanel left = new JPanel();
		left.setBackground(Color.white);
		left.setLayout(null);
		left.setBorder(bb);
		left.setPreferredSize(new Dimension(40,40));
		
		//左面板中的两棵颜色按钮
	    bt = new JButton();
		bt.setBounds(5, 5, 20, 20);
		bt.setBorder(bb1);
		bt.setBackground(Color.black);
		bt.setSize(20,20);
		JButton bt1 = new JButton();
		bt1.setBorder(bb1);
		bt1.setBounds(15,15,20,20);
		left.add(bt);
		left.add(bt1);
		
        //右面板
		JPanel right = new JPanel();
		right.setBackground(Color.BLUE);
		right.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
		right.setPreferredSize(new Dimension(240,40));
		
		paneldownchild.add(left);
		paneldownchild.add(right);
		
		//给右面板的颜色按钮天添加监听器,注意传递this对象
		ButtonListener bl =new ButtonListener(this);
		//颜色数组,用来设置按钮的背景颜色
		Color []colors = {new Color(0,56,67),new Color(89,3,14),new Color(189,3,14)
				,new Color(89,93,14),new Color(89,113,14),new Color(89,73,14)
				,new Color(89,3,14),new Color(89,3,14),new Color(29,83,14)
				,new Color(89,3,184),new Color(189,233,14),new Color(89,253,14)
				,new Color(89,93,14),new Color(89,89,94),new Color(1,3,14)
				,new Color(9,83,94),new Color(89,178,147),new Color(9,33,164)
				,new Color(34,23,14),new Color(89,173,154),new Color(8,193,194)
				,new Color(9,253,76),new Color(89,240,104),new Color(199,73,4)};
		
		//循环添加24个颜色按钮
		for(int i=0;i<24;i++){
			JButton bt3 = new JButton();
		    Color c=new Color(i*10,30-i,i*7+50);
		    bt3.setBackground(colors[i]);
		    bt3.setPreferredSize(new Dimension(20,20));
		    bt3.setBorder(bb);
		    bt3.addActionListener(bl);
			right.add(bt3);
		}
		
		this.setVisible(true);
		
		//画笔必须在setVisible后才能拿
		g=(Graphics2D)panelcenter.getGraphics();
		
		//传递画笔,按钮组管理对象,以及this对象
		DrawListener dl =new DrawListener(g,bg,this,list);
		
		//添加普通鼠标监听器
		panelcenter.addMouseListener(dl);
		
		//添加鼠标拖动监听器
		panelcenter.addMouseMotionListener(dl);
			
	}
	
	public void addMenu()throws Exception{
		//菜单条对象创建
		JMenuBar bar = new JMenuBar();
		//菜单创建
		JMenu menu=new JMenu("文件");
		//菜单项监听器创建
		ItemListener il=new ItemListener(this);
		//创建三个菜单项
		JMenuItem item0= new JMenuItem("新建");
		JMenuItem item= new JMenuItem("打开");
		JMenuItem item1= new JMenuItem("保存");
		//给每个菜单项添加监听器
		item0.addActionListener(il);
		item.addActionListener(il);
		item1.addActionListener(il);
		//将菜单条添加到窗体上
		this.setJMenuBar(bar);
		//将菜单添加到菜单条上
		bar.add(menu);
		//将菜单项添加到菜单上
		menu.add(item0);
		menu.add(item);
		menu.add(item1);
	}
	
}

菜单项监听器类:里面实现了不同菜单项点击时的逻辑

package com.huaxin.zhou1;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

import com.huaxin.zhou.Line;

public class ItemListener implements ActionListener{
	
	public DrawBorder paint;
	
	//构造方法
	public ItemListener(DrawBorder paint){
		this.paint=paint;
		
	}
	//监听器的具体实现逻辑
	public void actionPerformed(ActionEvent e) {
		
		//判断是哪个菜单项被按下
		String command =e.getActionCommand();
		
		/*
		 * 新建逻辑实现:
		 * 当新建的时候,只需要把容器里面所有的对象清空,然后将中间面板重绘就可以了
		 */
		if("新建".equals(command)){
			
			int value=JOptionPane.showConfirmDialog(null, "是否需要保存当前文件?", "提示信息", 0);
			if(value==0){
				
				saveFile();
			}
			if(value==1){
				paint.list.removeAll(paint.list);
				paint.panelcenter.repaint();
			}
		}
		/*
		 * 打开逻辑实现:
		 * 当点击打开菜单项时,首先应该清空容器里面的东西,然后面板重绘
		 * 然后再把打开的文件利用对象输入流读入
		 * 将读入的信息取出来,转换成相应的图形对象
		 * 将取出来的图形对象添加到容器里面
		 * 调用中间画板,将取出来的图形进行绘制
		 */
		else if("打开".equals(command)){
			
			int value=JOptionPane.showConfirmDialog(null, "是否需要保存当前文件?", "提示信息", 0);
			if(value==0){
				
				saveFile();
			}
			if(value==1){
				//清空容器里面的东西
				paint.list.removeAll(paint.list);
				paint.panelcenter.repaint();
				
				try {
					//弹出选择对话框,选择需要读入的文件
					JFileChooser chooser = new JFileChooser();
					chooser.showOpenDialog(null);
					File file =chooser.getSelectedFile();
					//如果为选中文件
					if(file==null){
						JOptionPane.showMessageDialog(null, "没有选择文件");
					}
					else {
						//选中了相应的文件,则柑橘选中的文件创建对象输入流
						FileInputStream fis = new FileInputStream(file);
						ObjectInputStream ois = new ObjectInputStream(fis);
						//将读出来的对象转换成父类对象的容器进行接收
						ArrayList<Shape> list =(ArrayList<Shape>)ois.readObject();
						//遍历容器里面的具体对象,将取出来的对象保存到容器里面
						for (int i = 0; i <list.size(); i++) {
							Shape shape=(Shape)list.get(i);
							paint.list.add(shape);
							//调用中心画板的repaint()方法,将容器里面的图形绘制出来
							paint.panelcenter.repaint();
						}
						ois.close();
					}
					
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
			
			
			
		}else if("保存".equals(command)){
			
			
			saveFile();
			
			
		}
	
	}
	
	public void saveFile(){
		//选择要保存的位置以及文件名字和信息
		JFileChooser chooser = new JFileChooser();
		chooser.showSaveDialog(null);
		File file =chooser.getSelectedFile();
		
		if(file==null){
			JOptionPane.showMessageDialog(null, "没有选择文件");
		}else {

			try {
				//根据要保存的文件创建对象输出流
				FileOutputStream fis = new FileOutputStream(file);
				ObjectOutputStream oos = new ObjectOutputStream(fis);
				//将容器里面所绘制的图形利用对象流全部写入选中的文件中
				oos.writeObject(paint.list);
			    JOptionPane.showMessageDialog(null, "保存成功!");
				oos.close();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

}



其他没有改变代码的类如下:

颜色按钮监听类:

package com.huaxin.zhou1;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;

//按钮监听类,实现ActionListenerde接口
public class ButtonListener implements ActionListener{
	
	
	public DrawBorder db;

	//构造函数
	public ButtonListener(DrawBorder db1) {
		db=db1;
	}

	//监听具体实现
	public void actionPerformed(ActionEvent e) {
		
		//拿到被选中按钮的对象
		JButton bt =(JButton)e.getSource();
		//拿到被选中按钮的背景颜色
		Color c= bt.getBackground();
		//把背景颜色复制给DrawBorder中的颜色属性
		db.c=c;
		//把左面板中的按钮颜色设置成选中按钮的背景颜色
		db.bt.setBackground(c);
		
	}

}


中心画板监听类:

package com.huaxin.zhou1;

import java.awt.AWTException;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;


public class DrawListener implements MouseListener,MouseMotionListener{
	
	public Graphics2D g;
	public int x1,y1,x2,y2,ox,oy,x3,y3;
	public ButtonGroup bg;
	public String command;
	public Color color;
	public DrawBorder db;
	public ArrayList list;
	public boolean flag=true;
	
	public static final  Stroke s1 = new BasicStroke(1);
	public static final  Stroke s2 = new BasicStroke(10);
	public static final  Stroke s3 = new BasicStroke(15);
	
	public Random r =new Random();
	//构造函数1
	public DrawListener(Graphics g1){
		g=(Graphics2D)g1;
	}
	
	//构造函数2
	public DrawListener(Graphics g2, ButtonGroup bg2) {
		g=(Graphics2D)g2;
		bg=bg2;
	}

    //构造函数3
	public DrawListener(Graphics g2, ButtonGroup bg2, DrawBorder db1,ArrayList list) {
		g=(Graphics2D)g2;
		bg=bg2;
		db=db1;
		this.list=list;
	}

    //鼠标按下事件监听
	public void mousePressed(MouseEvent e) {
		//获取鼠标按下点的坐标
		x1=e.getX();
		y1=e.getY();
		
	
		
		//判断选择的是左面板中的那个按钮被选中(前面已经设置每个按钮的名称了)  
        ButtonModel bm=bg.getSelection();//拿到按钮组中被选中的按钮  
        command=bm.getActionCommand();//拿到选中按钮的名字  
       
	}

	public void mouseReleased(MouseEvent e) {  
        //获取鼠标释放的坐标  
        x2=e.getX();  
        y2=e.getY();  
        
        
        //如果选中的是绘制直线的按钮,那么根据鼠标按下点的坐标和释放点的左边绘制直线(两点确定一条直线)  
        if("pic10".equals(command))  
        {  
        	Shape line = new Line(x1, y1, x2, y2,g.getColor(),1);
        	line.Draw(g);
        	list.add(line);
        }//同理选中的是矩形按钮,那么绘制矩形(这里有绘制矩形的纠正,不纠正的话从右下角往左上角方向绘制矩形会出现问题,参看后面难点解析)  
        else if("pic12".equals(command)){  
        	Shape rect = new Rect(Math.min(x2, x1),Math.min(y2, y1), Math.abs(x2-x1),Math.abs(y1-y2),g.getColor(),1);
        	rect.Draw(g);
        	list.add(rect);
        }//绘制椭圆  
        else if("pic14".equals(command)){  
        	Shape oval = new Oval(Math.min(x2, x1),Math.min(y2, y1), Math.abs(x2-x1),Math.abs(y1-y2),g.getColor(),1);
        	oval.Draw(g);
        	list.add(oval);
        } else if("pic15".equals(command)){
        	Shape roundrect = new RoundRect(Math.min(x2, x1),Math.min(y2, y1), Math.abs(x2-x1),Math.abs(y1-y2),40,40,g.getColor(),1);
        	roundrect.Draw(g);
        	list.add(roundrect);
        }//绘制曲线
        else if("pic13".equals(command)){
        	
        	//第一次画直线,设置标志
        	if(flag){
        		Shape line = new Line(x1, y1, x2, y2,g.getColor(),1);
            	line.Draw(g);
            	list.add(line);
                flag=false;
              //记录这次鼠标释放的坐标,作为下次绘制直线的起点
                x3=x2;
                y3=y2;
                //记录第一点击的坐标,绘制封闭的曲线
                ox=x1;
                oy=y1;
        	}
        	else{
        		Shape line = new Line(x3, y3, x2, y2,g.getColor(),1);
             	line.Draw(g);
             	list.add(line);
        		  //记录上次鼠标释放的坐标
                 x3=x2;
                 y3=y2; 
        	}
        }
        //取色功能
        else if("pic4".equals(command)){
        	
        	//拿到相对面板的那个坐标
    		int x=e.getXOnScreen();
    		int y=e.getYOnScreen();
    		
    		try {
    			
				Robot robot = new Robot();//Robot类的使用
				
				//拿到坐标点的那个矩形
			    Rectangle rect = new Rectangle(x,y,1,1);
			    //生成该矩形的缓冲图片
				BufferedImage bi =robot.createScreenCapture(rect);
				//得到图片的背景颜色
				int  c =bi.getRGB(0, 0);
				//将该颜色进行封装
				Color color = new Color(c);
				//将取色笔取来的图片设置成画笔的颜色
				db.c=color;
			} catch (AWTException e1) {
				e1.printStackTrace();
			}
    	}
          
    }  

	public void mouseClicked(MouseEvent e) {
		//多边形图形双击封闭
		int count =e.getClickCount();
		if(count==2 && "pic13".equals(command)){
			Shape line = new Line(ox, oy, x2, y2,g.getColor(),1);
         	line.Draw(g);
         	list.add(line);
		
			flag=true;
		}
	}

	public void mouseEntered(MouseEvent e) {
		    color=db.c;//设置画笔颜色  
			g.setColor(color);
			g.setStroke(s1);
	}

	public void mouseExited(MouseEvent e) {
		
	}

	public void mouseDragged(MouseEvent e) {
		
		int x=e.getX();
		int y=e.getY();
		
		//画笔功能
		if("pic6".equals(command)){
			
			Shape line = new Line(x1, y1, x, y,g.getColor(),1);
         	line.Draw(g);
         	list.add(line);
			x1=x;
			y1=y;
		}
		//橡皮擦功能
		else if("pic2".equals(command)){
			db.c=Color.white;
			g.setColor(db.c);
			g.setStroke(s3);
			
			Shape line = new Line(x1, y1, x, y,g.getColor(),15);
         	line.Draw(g);
         	list.add(line);
			
			x1=x;
			y1=y;
		}
		//刷子功能
		else if("pic7".equals(command)){
			g.setStroke(s2);//设置画笔 粗细
			
			Shape line = new Line(x1, y1, x, y,g.getColor(),10);
         	line.Draw(g);
         	list.add(line);
         	
			x1=x;
			y1=y;
		}
		//喷桶功能
		else if("pic8".equals(command)){
			//随机产生30个-15到15之间的整数
			for (int i = 0; i < 30; i++) {
				int xp=r.nextInt(31)-15;
				int yp=r.nextInt(31)-15;
				//在x,y附件绘制原点
				
				Shape line = new Line(x+xp, y+yp, x+xp, y+yp,g.getColor(),1);
	         	line.Draw(g);
	         	list.add(line);
			}
			
		}
		
		
	}

	public void mouseMoved(MouseEvent e) {
		
	}

	

}


图形父类:Shape类

package com.huaxin.zhou1;

import java.awt.Color;
import java.awt.Graphics2D;
import java.io.Serializable;

//抽象父类
public abstract class Shape implements Serializable{
	
	public  int x1,y1,x2,y2;//绘制图形的坐标
	public Color color;//画笔颜色
	public int width;//画笔粗细
	//抽象的Draw方法
	public abstract void Draw(Graphics2D g);

}


各类图形子类:

Line类:

package com.huaxin.zhou1;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.Serializable;

public class Line extends Shape implements Serializable{
	
	//子类构造函数
	public Line(int x1,int y1,int x2,int y2,Color color,int width){
		this.x1=x1;
		this.y1=y1;
		this.x2=x2;
		this.y2=y2;
		this.color=color;
		this.width=width;
	}
  //重写父类的Draw方法,实现直线的绘制
	public void Draw(Graphics2D g) {
		g.setColor(this.color);
		g.setStroke(new BasicStroke(width));
		g.drawLine(x1, y1, x2, y2);
	}

}

Oval类:

package com.huaxin.zhou1;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.Serializable;

public class Oval extends Shape implements Serializable{
	
	public Oval(){
		
	}
	
	public Oval(int x1,int y1,int x2,int y2,Color color ,int width){
		this.x1=x1;
		this.y1=y1;
		this.x2=x2;
		this.y2=y2;
		this.color=color;
		this.width=width;
	}


	public void Draw(Graphics2D g) {
		g.setColor(this.color);
		g.setStroke(new BasicStroke(width));
		g.drawOval(x1, y1, x2, y2);
	}

	
}

Rect类

package com.huaxin.zhou1;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.Serializable;

public class Rect extends Shape implements Serializable{
	
	public Rect(){
		
	}
	//矩形的构造方法
	public Rect(int x1,int y1,int x2,int y2,Color color,int width){
		this.x1=x1;
		this.y1=y1;
		this.x2=x2;
		this.y2=y2;
		this.color=color;
		this.width=width;
	}

    //重写父类的Draw方法,实现矩形的绘制
	public void Draw(Graphics2D g) {
		g.setColor(this.color);
		g.setStroke(new BasicStroke(width));
		g.drawRect(x1, y1, x2, y2);
	}

}


RoundRect类:

package com.huaxin.zhou1;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.Serializable;

public class RoundRect extends Shape implements Serializable{
	
	//子类新增属性,圆角矩形的角的弯曲程度
	public int arcWidth,arcHeight;
	

	public RoundRect(){
		
	}
	
	public RoundRect(int x1,int y1,int x2,int y2,int i,int j,Color color ,int width){
		this.x1=x1;
		this.y1=y1;
		this.x2=x2;
		this.y2=y2;
		this.arcWidth=i;
		this.arcHeight=j;
		this.color=color;
		this.width=width;
	}
    
	public void Draw(Graphics2D g) {
		g.setColor(this.color);
		g.setStroke(new BasicStroke(width));
		g.drawRoundRect(x1, y1, x2, y2, this.arcWidth, this.arcHeight);
	}

}

主函数:这里我没有使用系统外观,因为在我电脑上使用后颜色按钮不明显,你们可以把注释去掉看看

package com.huaxin.zhou1;

import javax.swing.UIManager;

public class Test {
   
	//测试函数
	public static void main(String[] args) throws Exception{
//		UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		DrawBorder db = new DrawBorder(); 
		db.initFrame();
	}
}

运行结果:

1、绘制图形



2、保存图形




3.点击打开菜单项、选择否



4、选择我们保存在桌面上的文件



5、文件打开



总结:
1、通过流的学习,学到了很多前面没学过的东西以及以前学过但是没有掌握的知识;
2、对流的知识有了更加深刻的理解;
3、自己感觉对流的知识的掌握熟练了很多;
4、在小项目中体验到了流的用处;
5、共勉!努力!

  • 18
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值