<JAVA - 大作业(1)文本编辑器 >

<JAVA - 大作业(1)文本编辑器 >

背景

  • JAVA上机大作业:qq / 代码评价系统

  • 第一次上机主题是练习JAVA自带的GUI图形化编程

  • 目的:实现一个跟window10记事本界面相同,功能相近的文本编辑器,时间有剩余的话再添加类似notepad++上的一些额外功能。

  • 记事本结构

    • 标题栏
      显示标题title

    • 菜单栏
      文件、编辑、格式、查看、帮助 - 菜单项

      • <文件>下拉栏有:新建、新窗口、打开、保存、退出

      • <编辑>下拉栏有:撤销、恢复、剪切、复制、粘贴、删除

      • <格式>下拉栏有:字体

      • <帮助>下拉栏有:关于

    • 文本区域

      • 底侧滚动条

设计思路

  • 菜单栏:

    • 主体就是一个JFrame,设置一些属性,然后设置菜单栏JMenuBar,add菜单项JMenu,菜单项的下拉栏add菜单子项JMenuItem
  • 文本区域:

    • 完成菜单栏之后就是一个占屏幕率超大的JTextArea,注意该文本区域需要将他设置为静态,并且写一个静态get函数得到它,因为有跨类的需要改该文本属性。由于有滚动条,后续还要加上显示行号的功能,所以用一个JScrollPane装载它。
  • 文件菜单子栏:

    • 新建:就新建一个窗口new TextEdition(),并且把当前窗口退出dispose(),退出前调用一次保存函数savefile()

    • 新窗口:

      • 新建一个窗口,并且当前窗口不退出,两窗口相互不影响,即默认退出操作使用DISPOSE_ON_CLOSE(当然可以自己写,退出时加上是否保存询问)
    • 打开:

      • 使用JFileChooser,界面为打开界面,打开跳出一个Dialog对话框,选择文件进行打开,InputStreamReader+BufferedReader
    • 保存:

      • 使用JFileChooser,界面为保存界面,跟打开操作一样,OutputStreamWriter+BufferedWriter
    • 退出:

      • 调用保存函数savefile(),并且dispose()当前窗口
  • 编辑菜单子栏:

    • 撤销:

      • 撤销与恢复都需要用到撤销管理类um = new UndoManager(),在监听器初始化时注册撤销可编辑监听器 - 文本区域getDocument().addUndoableEditListener,撤销操作就是 um.undo()
    • 恢复:

      • 恢复操作与上一致,监听器设置好以后,调用um.redo()即是恢复
    • 剪切:

      • 剪切与复制、粘贴是一起完成,需要用到系统剪切板clipboard = this.getToolkit().getSystemClipboard(),剪切就是复制+删除操作
    • 复制:

      • 先将文本区域的文本放到string里面,创建能传输指定 String 的 StringSelection,把系统剪切板的内容设置成StringSelection:clipboard.setContents(editText,null);
    • 粘贴:

      • 先将系统剪切板的内容放在一个Transferable里面,之后再将Transferable转成string,文本区域append即可
    • 删除

      • getSelectionStart,getSelectionEnd得到头尾,再replaceRange替换成空文本即可
  • 格式菜单子栏:

    • 字体
      • 新建一个窗体设置字体,用JComboBox实现下拉列表,一个JTextField放演示文本,再是确认取消按钮,三个JPanel分别装这三部分整理布局排版,确认按钮点击后就调用静态get函数得到父窗体的文本区域设置其属性。
  • 帮助菜单子栏:

    • 关于
      • 弹出一个新窗体,里面用label写文本显示

TextEdition - 主窗体类实现

主窗体属性

ublic class TextEditor extends JFrame implements ActionListener{
	//	其实我们这里也可以用ItemListener用于捕获带有item的组件产生的事件,
	//	而ActionListener是所有监听器的父类,可以监听到所有的事件,由于担心还会有其他的事件需要监听,
	//	所以就直接用 ActionListener 了

	//菜单栏
	private JMenuBar menuBar;
	//菜单项
	private JMenu menu_File,menu_Edit,menu_Help,menu_Format;
	//file菜单项内容
	private JMenuItem item_new,item_newwindow,item_open,item_save,item_exit;
	//edit菜单项内容
	private JMenuItem item_undo,item_redo,item_cut,item_copy,item_stick,item_delete;
	//help菜单项内容
	private JMenuItem item_about;
	//format菜单项内容
	private JMenuItem item_word_format;
	
	//文本区域
	private static JTextArea edit_text_area;	//static因为要在其他窗口对其他字体格式进行改动
	//文本滚动条
	private JScrollPane scroll_bar;
	//撤销管理
	private UndoManager um;
	//系统剪切板
	private Clipboard clipboard;
	//文件选择器
	private JFileChooser fileChooser;

主窗体构造器

public TextEditor()
	{
		initMenuBar();
		initEditText();
		initListener();
		 
		this.setJMenuBar(menuBar);
		this.setSize(800,600);
		this.add(scroll_bar);
		this.setTitle("黄龙士文本编辑器");
		this.setVisible(true);
		this.setLocationRelativeTo(null);	//窗体位于正中间位置
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);	// EXIT_ON_CLOSE  https://www.cnblogs.com/lihaiming93/p/4752422.html 后续还要改,加上退出询问
	}

initMenuBar() - 初始化菜单栏

private void initMenuBar() {
		
		//初始化菜单栏
		menuBar = new JMenuBar();
		
		//file菜单项
		menu_File = new JMenu("文件(F)");
		menu_File.setMnemonic('f');		// 设置快捷键-alt+f:打开 - 也可以传入KeyEvent.VK_F
		item_new = new JMenuItem("新建");
		item_newwindow = new JMenuItem("新窗口");
		item_open = new JMenuItem("打开");
		item_save = new JMenuItem("保存");
		item_save.setAccelerator(KeyStroke.getKeyStroke('S',java.awt.Event.CTRL_MASK,false));//给item添加快捷键 CTRL_MASK = ctrl键
		item_exit = new JMenuItem("退出");
		menu_File.add(item_new);
		menu_File.add(item_newwindow);
		menu_File.add(item_open);
		menu_File.add(item_save);
		menu_File.add(item_exit);

		//edit菜单项
		menu_Edit = new JMenu("编辑(E)");
		menu_Edit.setMnemonic('e');
		item_undo = new JMenuItem("撤销");
		item_redo = new JMenuItem("恢复");
		item_cut = new JMenuItem("剪切");
		item_copy = new JMenuItem("复制");
		item_stick = new JMenuItem("粘贴");
		item_delete = new JMenuItem("删除");
		menu_Edit.add(item_undo);
		menu_Edit.add(item_redo);
		menu_Edit.add(item_cut);
		menu_Edit.add(item_copy);
		menu_Edit.add(item_stick);
		menu_Edit.add(item_delete);
		 
		//help菜单项
		menu_Help = new JMenu("帮助(H)");
		menu_Help.setMnemonic('h');
		item_about = new JMenuItem("关于");
		menu_Help.add(item_about);

		//format菜单项
		menu_Format = new JMenu("格式(O)");
		menu_Format.setMnemonic('o');
		item_word_format = new JMenuItem("字体(F)");			//CTRL_MASK = ctrl键
item_word_format.setAccelerator(KeyStroke.getKeyStroke('F',java.awt.Event.CTRL_MASK,false));//给item添加快捷键
		menu_Format.add(item_word_format);

		//加入菜单栏
		menuBar.add(menu_File);
		menuBar.add(menu_Edit);
		menuBar.add(menu_Format);
		menuBar.add(menu_Help);
	}

initEditText() - 初始化文本区域

private void initEditText() {
		
		edit_text_area = new JTextArea();
		scroll_bar = new JScrollPane(edit_text_area);
		//垂直滚动条总是出现 - 自动出现:JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED||VERTICAL_SCROLLBAR_ALWAYS
		scroll_bar.setRowHeaderView(new LineNumberHeaderView());		//显示行号
		// 具体显示行号类:https://blog.csdn.net/wangjianyu0115/article/details/50780269
        
    scroll_bar.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
  scroll_bar.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
		
		um = new UndoManager();
		clipboard = this.getToolkit().getSystemClipboard();
	}

saveFile - 保存文件

private void saveFile() {
		// TODO Auto-generated method stub

		File file =null;
		int result;		//int型值,showSaveDialog返回值用来判断用户是否选择了文件或路径。
		fileChooser = new JFileChooser("C:\\Users\\62473\\Desktop\\");	//打开用户默认目录
		result = fileChooser.showSaveDialog(rootPane); // 设置Dialog的根View根布局	显示对话框
		
		if(result == JFileChooser.APPROVE_OPTION) {
			file = fileChooser.getSelectedFile(); // 若点击了确定按钮,给file填文件路径
		}
		else return;	//按取消键后不会运行下面代码
		
		try{
			OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file),"UTF-8"); // 对字符进行编码转换 输出流
			BufferedWriter writer = new BufferedWriter(write);
			for (String value : edit_text_area.getText().split("\n")) {		// 实现在JTextArea多行如何转换到实际文本多行
				writer.write(value);
				writer.newLine();//换行
            }
			writer.close();
		}
		catch(IOException e) {
			e.printStackTrace();
		}
	}

openfile() - 打开文件

private void openFile() {
	
		File file =null;
		int result;
		fileChooser =new JFileChooser("C:\\Users\\62473\\Desktop\\");
		result = fileChooser.showOpenDialog(rootPane);
		
		if(result == JFileChooser.APPROVE_OPTION) {
			file = fileChooser.getSelectedFile(); // 若点击了确定按钮,给file填文件路径
		}
		else return;	//按取消键后不会运行下面代码
	
		if(file.isFile() && file.exists()) {
			try {
				edit_text_area.setText("");		//清空目前文本内容
				InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file),"UTF-8");
				BufferedReader reader = new BufferedReader(inputStreamReader);
				String readLine;
				while ((readLine = reader.readLine()) != null) {
					edit_text_area.append(readLine+'\n');
				}
				reader.close();
			} 
			catch (IOException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	
	}

actionPerformed(ActionEvent e) - 监听事件响应

public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		//https://zhidao.baidu.com/question/2204274734088183468.html
		
		if(e.getSource()==item_about)
		{
			new helpWindow();
		}
		else if (e.getSource()==item_word_format) {
			new aboutFormat();
		}
		else if (e.getSource()==item_new) {
			this.saveFile();		//新建文件前保存原文件
			new TextEditor();
			this.dispose();		
		}
		else if (e.getSource()==item_newwindow) {
			new TextEditor();		//添加新窗口,父窗口不会退出
		}
		else if (e.getSource()==item_exit){
			this.saveFile();
			this.dispose();			//退出询问(目前有无更改都会弹出保存窗口)
		}
		else if (e.getSource()==item_save) {
			this.saveFile();
		}
		else if (e.getSource()==item_open) {
			this.openFile();
		}
		else if (e.getSource()==item_undo&&um.canUndo()) {		//撤销可以撤销是撤销上一步文本操作
			um.undo();
		}
		else if (e.getSource()==item_redo&&um.canRedo()) {		//恢复就是恢复上一步文本操作(需要被撤销)
			um.redo();
		}
		else if (e.getSource()==item_copy) {
			String temptext = edit_text_area.getSelectedText();		//得到选取文本
			StringSelection editText = new StringSelection(temptext);	//创建能传输指定 String 的 Transferable
			clipboard.setContents(editText,null);		// 将剪贴板的当前内容设置到指定的 transferable 对象,
														// 并将指定的剪贴板所有者作为新内容的所有者注册。
		}
		else if (e.getSource()==item_cut) {
			String temptext = edit_text_area.getSelectedText();
			StringSelection editText = new StringSelection(temptext);
			clipboard.setContents(editText,null);
			int start= edit_text_area.getSelectionStart();		//复制+删除
			int end  = edit_text_area.getSelectionEnd(); 
			edit_text_area.replaceRange("",start,end);
		}
		else if (e.getSource()==item_stick) {
			Transferable contents = clipboard.getContents(this);
			DataFlavor  flavor= DataFlavor.stringFlavor;
		    if( contents.isDataFlavorSupported(flavor))
		    {
			    try
			    {  
			    	String str;
			    	str = (String)contents.getTransferData(flavor);
			    	edit_text_area.append(str);
			    }
			    catch(Exception ex)
			    {
			    	ex.printStackTrace();
			    }
		    }
		}
		else if (e.getSource()==item_delete) {
			int start= edit_text_area.getSelectionStart();		//删除
			int end  = edit_text_area.getSelectionEnd(); 
			edit_text_area.replaceRange("",start,end);
		}
	}

initListener() - 初始化监听器

public void initListener()
	{
		// 菜单iter监听
		item_new.addActionListener(this);
		item_newwindow.addActionListener(this);
		item_open.addActionListener(this);
		item_save.addActionListener(this);
		item_exit.addActionListener(this);
		item_undo.addActionListener(this);
		item_redo.addActionListener(this);
		item_cut.addActionListener(this);
		item_copy.addActionListener(this);
		item_stick.addActionListener(this);
		item_delete.addActionListener(this);
		item_word_format.addActionListener(this);
		item_about.addActionListener(this);
		
		//注册撤销/恢复可编辑监听器
		edit_text_area.getDocument().addUndoableEditListener(new UndoableEditListener(){		
            public void undoableEditHappened(UndoableEditEvent e) {
                um.addEdit(e.getEdit());
            }
        });
		
	}

getEdit_text_area() - 静态get函数得到文本区域

public static JTextArea getEdit_text_area() {
	//返回文本编辑区给其他窗口
		return edit_text_area;
	}

main() - main函数

public static void main(String[] args) {
		
		TextEditor test = new TextEditor();
	
	}
	
}

aboutFormat - 字体窗口实现

字体窗口属性

public class aboutFormat extends JFrame implements ItemListener,ActionListener {

	private JComboBox choose_word_style;	//下拉列表
	private JComboBox choose_word_big;
	private JComboBox choose_word_pattern;
	private JComboBox choose_word_color;
	
	private String[] styles = {"宋体","黑体","楷体","微软雅黑","隶书"};
	private String[] colors = {"黑色","蓝色","绿色","红色","白色","黄色"};
	private String[] word_big = {"2","4","8","16","24","32","64","72"};
	private String[] pattern = {"常规","倾斜","粗体"};
	
	private JPanel paneNorth;//用于装四个ComboBox
	private JPanel paneCenter;//用来装演示区
	private JPanel paneSouth;//用来装按钮
	
	private JTextField showText;//演示文本
	
	private JButton btn_ok;		
	private JButton btn_cancel;
	
	private Font selectedFont = TextEditor.getEdit_text_area().getFont();	//用来封装改变的属性
	private String selectedStyle = "宋体";		//默认字体属性
	private int selectedBig = 32;
	private int selectedPattern = Font.PLAIN;
	private Color selectedColor = TextEditor.getEdit_text_area().getForeground();	//颜色要单独加上改

字体窗口构造器

public aboutFormat() {
		  initBox();
		  initText();
		  initButton();
		  initLocation();
		  initListener();
		  addBtnListener();
		  
		  this.setSize(550,240);
		  this.setTitle("文字格式");
		  this.setVisible(true);
		  this.setLocationRelativeTo(null);
		  this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	  }

addBtnListener() - 注册监听

private void addBtnListener() {
		// TODO Auto-generated method stub		//注册按钮监听
		btn_cancel.addActionListener(this);
		btn_ok.addActionListener(this);
	}

initListener() - 初始化监听器

private void initListener() {		//注册下拉框监听
		// TODO Auto-generated method stub
		choose_word_style.addItemListener(this);
		choose_word_big.addItemListener(this);
		choose_word_pattern.addItemListener(this);
		choose_word_color.addItemListener(this);
	}

initButton() - 初始化按钮

private void initButton() {
		// TODO Auto-generated method stub
		btn_ok = new JButton("OK");
		btn_cancel = new JButton("CANCEL");
	}

initText() - 初始化演示文本

private void initText() {
		// TODO Auto-generated method stub
		showText = new JTextField("字体展示");
		showText.setHorizontalAlignment(JTextField.CENTER);	//文本居中
		showText.setFont(selectedFont);
		showText.setEditable(false);	//不可编辑
		showText.setSize(200,200);
//		showText.setForeground(Color.green);//字体颜色
	}

initLocation() - 初始化布局

/**
	   * 初始化布局
	   * 将每个控件按照一定得布局排在this窗口中
	   */
	  public void initLocation() {
		  paneNorth = new JPanel();
		  paneNorth.add(new JLabel("字体:"));	//添加下拉区
		  paneNorth.add(choose_word_style);
		  paneNorth.add(new JLabel("字号:"));
		  paneNorth.add(choose_word_big);
		  paneNorth.add(new JLabel("字形:"));
		  paneNorth.add(choose_word_pattern);
		  paneNorth.add(new JLabel("颜色:"));
		  paneNorth.add(choose_word_color);
		  paneNorth.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));	//让add的组件不置顶
		  this.add(paneNorth,BorderLayout.NORTH);
		  
		  paneCenter = new JPanel();	//添加展示区
		  paneCenter.add(showText);
		  
		  this.add(paneCenter, BorderLayout.CENTER);	//添加按钮去
		  paneSouth = new JPanel();
		  paneSouth.add(btn_ok);
		  paneSouth.add(btn_cancel);
		  this.add(paneSouth, BorderLayout.SOUTH);
		  
	  }

initBox() - 初始化下拉栏

/**
  	 * 初始化几个comboBox 
  	 * 把相应的选项加入
  	 */
  public void initBox() {
	  choose_word_style = new JComboBox(styles);	//数组传入就不用addItem
	  choose_word_big = new JComboBox(word_big);
	  choose_word_pattern = new JComboBox(pattern);
	  choose_word_color = new JComboBox(colors);
  }

actionPerformed(ActionEvent e) - 监听事件响应

@Override
	public void actionPerformed(ActionEvent e) {	//对按钮的监听
		// TODO Auto-generated method stub
		if (e.getSource() == btn_cancel) {
			  this.dispose();//销毁当前窗口
		}else if (e.getSource() == btn_ok) { // 调用父窗体的实例,拿到text_area并对其setFont
	    
			TextEditor.getEdit_text_area().setFont(selectedFont);	//改字体
			TextEditor.getEdit_text_area().setForeground(selectedColor);	//改字体颜色
			this.dispose();
		}
	}

itemStateChanged(ItemEvent e) - 下拉栏的监听

@Override
	public void itemStateChanged(ItemEvent e) {	//对下拉框的监听
		// TODO Auto-generated method stub
		if (e.getItem() == "宋体") {
			  selectedStyle = "宋体";
			  renewFont();
		  }else if (e.getItem() == "黑体") {
			  selectedStyle = "黑体";
			  renewFont();
		  }else if (e.getItem() == "楷体") {
			  selectedStyle = "楷体";
			  renewFont();
		  }else if (e.getItem() == "微软雅黑") {
			  selectedStyle = "微软雅黑";
			  renewFont();
		  }else if (e.getItem() == "隶书") {
			  selectedStyle = "隶书";
			  renewFont();
		  }else if (e.getItem() == "常规") {
			  selectedPattern = Font.PLAIN;
			  renewFont();
		  }else if (e.getItem() == "倾斜") {
			  selectedPattern = Font.ITALIC;
			  renewFont();
		  }else if (e.getItem() == "粗体") {
			  selectedPattern = Font.BOLD;
			  renewFont();
		  }else if (e.getItem() == "2") {
			  selectedBig = 2;
			  renewFont();
		  }else if (e.getItem() == "4") {
			  selectedBig = 4;
			  renewFont();
		  }else if (e.getItem() == "8") {
			  selectedBig = 8;
			  renewFont();
		  }else if (e.getItem() == "16") {
			  selectedBig = 16;
			  renewFont();
		  }else if (e.getItem() == "24") {
			  selectedBig = 24;
			  renewFont();
		  }else if (e.getItem() == "32") {
			  selectedBig = 32;
			  renewFont();
		  }else if (e.getItem() == "64") {
			  selectedBig = 64;
			  renewFont();
		  }else if (e.getItem() == "72") {
			  selectedBig = 72;
			  renewFont();
		  }else if (e.getItem() == "红色") {
			  selectedColor = Color.red;
			  renewFont();
		  }else if (e.getItem() == "黑色") {
			  selectedColor = Color.black;
			  renewFont();
		  }else if (e.getItem() == "蓝色") {
			  selectedColor = Color.blue;
			  renewFont();
		  }else if (e.getItem() == "黄色") {
			  selectedColor = Color.yellow;
			  renewFont();
		  }else if (e.getItem() == "绿色") {
			  selectedColor = Color.green;
			  renewFont();
		  }else if (e.getItem() == "白色") {
			  selectedColor = Color.WHITE;
			  renewFont();
		  }
	}

renewFont() - 更改默认字体

private void renewFont() {
		// TODO Auto-generated method stub
		selectedFont = new Font(selectedStyle,selectedPattern,selectedBig);
		showText.setFont(selectedFont);
		showText.setForeground(selectedColor);
	}

}

helpWindow - 帮助窗口实现

帮助窗口属性

public class helpWindow extends JFrame {

	private JButton btn_ok;
	private JLabel about_label;
 
	private JPanel panel ;
	private BoxLayout boxlayout;	//指定在容器中是否对控件进行水平或者垂直放置

帮助窗口构造器

	public helpWindow() {
		panel = new JPanel();	//轻量级面板容器
		boxlayout = new BoxLayout(panel,BoxLayout.Y_AXIS);	//Y_AXIS - 控件垂直放置
		panel.setLayout(boxlayout);
		
		btn_ok = new JButton("OK");
		btn_ok.setAlignmentX(CENTER_ALIGNMENT);	//alignment对齐
//		about_label = new JLabel("https://www.cnblogs.com/ymjun/");
		about_label = new JLabel("<html><br /><h3 style='text-align:center;color:brown;'>黄龙士文本编辑器</h3>"	//JLabel里面可以嵌入html
				+ "blog:https://www.cnblogs.com/ymjun/<br />"
				+ "github:https://github.com/iym070010<br />"
				+ "email:iym070010@163.com<br />"
				+ "使用方式跟.txt文本编辑器一致<br />"
				+ "<br /><br />"
				+ "</html>",JLabel.CENTER);
		about_label.setAlignmentX(CENTER_ALIGNMENT);
 
		panel.add(about_label);
		panel.add(btn_ok);
		
		this.add(panel);
		this.setSize(400,300);
		this.setTitle("关于");
		this.setVisible(true);
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		
		btn_ok.addActionListener(e->{	//窗口关闭
			this.dispose();
		});
	}
}

LineNumberHeaderView - TextArea行数显示插件

//package com.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
//TEXTAREA 行号显示插件
public class LineNumberHeaderView extends javax.swing.JComponent {		//貌似就是Notepad++行号显示的实现类
 
    /**
	 * JAVA TextArea行数显示插件
	 */
	private static final long serialVersionUID = 1L;
	private final  Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 11);
    public final Color DEFAULT_BACKGROUD = new Color(228, 228, 228);
    public final Color DEFAULT_FOREGROUD = Color.BLACK;
    public final int nHEIGHT = Integer.MAX_VALUE - 1000000;
    public final int MARGIN = 5;
    private int lineHeight;
    private int fontLineHeight;
    private int currentRowWidth;
    private FontMetrics fontMetrics;
 
    public LineNumberHeaderView() {
        setFont(DEFAULT_FONT);
        setForeground(DEFAULT_FOREGROUD);
        setBackground(DEFAULT_BACKGROUD);
        setPreferredSize(9999);
    }
 
    public void setPreferredSize(int row) {
        int width = fontMetrics.stringWidth(String.valueOf(row));
        if (currentRowWidth < width) {
            currentRowWidth = width;
            setPreferredSize(new Dimension(2 * MARGIN + width + 1, nHEIGHT));
        }
    }
 
    @Override
    public void setFont(Font font) {
        super.setFont(font);
        fontMetrics = getFontMetrics(getFont());
        fontLineHeight = fontMetrics.getHeight();
    }
 
    public int getLineHeight() {
        if (lineHeight == 0) {
            return fontLineHeight;
        }
        return lineHeight;
    }
 
    public void setLineHeight(int lineHeight) {
        if (lineHeight > 0) {
            this.lineHeight = lineHeight;
        }
    }
 
    public int getStartOffset() {
        return 4;
    }
 
    @Override
    protected void paintComponent(Graphics g) {
        int nlineHeight = getLineHeight();
        int startOffset = getStartOffset();
        Rectangle drawHere = g.getClipBounds();
        g.setColor(getBackground());
        g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);
        g.setColor(getForeground());
        int startLineNum = (drawHere.y / nlineHeight) + 1;
        int endLineNum = startLineNum + (drawHere.height / nlineHeight);
        int start = (drawHere.y / nlineHeight) * nlineHeight + nlineHeight - startOffset;
        for (int i = startLineNum; i <= endLineNum; ++i) {
            String lineNum = String.valueOf(i);
            int width = fontMetrics.stringWidth(lineNum);
            g.drawString(lineNum + " ", MARGIN + currentRowWidth - width - 1, start);
            start += nlineHeight;
        }
        setPreferredSize(endLineNum);
    }
}
posted @ 2019-11-13 09:30  黄龙士  阅读(728)  评论(0编辑  收藏  举报