728x90
안녕하세요 IT여행자입니다.
이번 여행지는 swing으로 만들어본 멀티 메모장 두 번째 부분입니다.
첫 번째 영상에서도 언급한 것처럼 자바 GUI를 사용한 프로그램이라는 인터페이스가 많이 사용되는 인터페이스가 아니지만, 진입 장벽이 높지 않으며, 웹 인터페이스처럼 다양한 언어를 사용하지 않아도 만들어 볼 수 있는 프로그램이기에 두 편에 걸쳐 만들어 봤습니다.
개발에 필요한 보다 자세한 내용들은 영상을 참조하시면서 하나씩 개발해 보시는 걸 추천드립니다.
1편에서는 레이아웃에 관한 내용을 다루었고, 2편에서는 BACK-END 부분을 다루었습니다.
주요 내용은 아래와 같습니다.
- MemoVo.java : 하나의 메모 내용을 저장하는 Value Object
- config.xml : mybatis를 사용하기 위한 환결 설정
- memo.xml : 메모와 관련된 CRUD 매퍼 파일
- Factory.java : mybatis의 환결설정 파일과 매퍼를 읽어 들여 실제로 Sqlite 데이터베이스를 사용할 수 있게 해주는 클래스
- Content.java : 메모 목차를 지정하는 인터널 프레임
- Editor.java : 메모장을 작성할 때 사용되는 인터널 프레임
- Main.java : 메모장의 메인 프레임
영상에서 작성한 코드의 완성본을 하단에 적습니다.
MemoVo.java
하나의 메모 내용을 저장하는 클래스입니다.
package memo;
public class MemoVo {
int id;
String nal;
String subject;
String doc;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNal() {
return nal;
}
public void setNal(String nal) {
this.nal = nal;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getDoc() {
return doc;
}
public void setDoc(String doc) {
this.doc = doc;
}
}
config.xml
sqlite 데이터 베이스를 연결할 때 사용되는 정보들
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="UNPOOLED">
<property name="driver" value="org.sqlite.JDBC"/>
<property name="url" value="jdbc:sqlite:db/memo.db"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="memo/myba/memo.xml"/>
</mappers>
</configuration>
memo.xml
메모장에 관한 CRUD 매퍼 파일
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="memo">
<insert id='insert' parameterType="memo.MemoVo">
insert into memo(nal, subject, doc)
values(#{nal}, #{subject}, #{doc})
</insert>
<update id='update' parameterType="memo.MemoVo">
update memo set subject = #{subject}, doc = #{doc}
where id = #{id}
</update>
<delete id='delete' parameterType="Integer">
delete from memo where id = ${_parameter}
</delete>
<select id='content' resultType="memo.MemoVo">
select id, nal, subject from memo order by nal desc
</select>
<select id='last_row' resultType="Integer">
select last_insert_rowid()
</select>
<select id='select_one' parameterType="Integer" resultType="memo.MemoVo">
select id, nal, subject, doc from memo where id = #{_parameter}
</select>
</mapper>
Factory.java
Sqlite 데이터 베이스 연결 정보 객체
package memo.myba;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Factory {
private static SqlSessionFactory factory;
static {
try {
Reader reader = Resources.getResourceAsReader("memo/myba/config.xml");
factory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getFactory() {
return factory;
}
}
Content.java
메모장들의 제목 표시 인터널 프레임
package memo;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JInternalFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import org.apache.ibatis.session.SqlSession;
import memo.myba.Factory;
public class Content extends JInternalFrame {
Main main; // 메모목차클래스에서 Main객체의 멤버들을 접근하기 위한 요소
List<Integer> idList; // 메모의 id
protected DefaultListModel<String> model; // 메모제목들
SqlSession session; // mybatis를 사용하기 위한 객체
private JScrollPane scrollPane;
private JList subjectList;
/**
* Create the frame.
*/
public Content(Main main) {
super("메모목차", false, true, false, false);
this.main = main;
addInternalFrameListener(new InternalFrameAdapter() {
@Override
public void internalFrameClosed(InternalFrameEvent e) {
main.getDesktopPane().remove(Content.this);
main.content= null;
}
});
setBounds(100, 100, 291, 433);
getContentPane().add(getScrollPane(), BorderLayout.CENTER);
idList = new ArrayList<Integer>();
makeAllSubject();
setVisible(true);
}
public void makeAllSubject() {
if (session == null) {
session = Factory.getFactory().openSession();
}
List<MemoVo> list = session.selectList("memo.content");
for (MemoVo m : list) {
String temp = String.format("%-12s %s", m.getNal(), m.getSubject());
model.addElement(temp);
idList.add(m.getId());
}
session.close();
session = null;
}
public void load(int[] indexs) {
for (int i : indexs) {
int id = idList.get(i);
Editor ed = null;
// 기존창이 있는지 체크
Component[] comps = main.getDesktopPane().getComponents();
boolean exist = false;
for (Component comp : comps) {
if (comp instanceof Editor) {
ed = (Editor) comp;
if (ed.getId() == id) {
exist = true;
break;
}
}
}
if (exist) {
ed.toFront();
} else {
//에디터창의 기본 위치를 지정하기 위해
int x = (i * 22);
int y = (i * 22);
Point point = new Point(x, y);
getData(idList.get(i), point);
}
}
}
public void getData(int id, Point point) {
if (session == null) {
session = Factory.getFactory().openSession();
}
MemoVo vo = session.selectOne("memo.select_one", id);
Editor ed = new Editor(main);
ed.setData(vo);
ed.setLocation(point);
main.getDesktopPane().add(ed);
ed.toFront();
session.close();
session = null;
}
public void delete(int index) {
if (session == null) {
session = Factory.getFactory().openSession();
}
int yn = JOptionPane.showConfirmDialog(Content.this,
"정말 삭제하시겠습니까?", "데이터 삭제", JOptionPane.YES_NO_OPTION);
if (yn == JOptionPane.OK_OPTION) {
int id = idList.get(index);
int cnt = session.delete("memo.delete", id);
if (cnt > 0) {
session.commit();
model.remove(index);
idList.remove(index);
Component[] comps = main.getDesktopPane().getComponents();
for (Component comp : comps) {
if (comp instanceof Editor) {
Editor ed = (Editor) comp;
if (ed.getId() == id) {
main.getDesktopPane().remove(ed);
main.getDesktopPane().updateUI();
}
}
}
}
session.close();
session = null;
}
}
protected JScrollPane getScrollPane() {
if (scrollPane == null) {
scrollPane = new JScrollPane();
scrollPane.setViewportView(getSubjectList());
}
return scrollPane;
}
protected JList getSubjectList() {
if (subjectList == null) {
model = new DefaultListModel<String>();
subjectList = new JList(model);
subjectList.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
int[] indexs = subjectList.getSelectedIndices();
load(indexs);
}
}
});
subjectList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.isMetaDown()) { // 마우스 우클릭하여 삭제
int index = subjectList.locationToIndex(e.getPoint());
if (index == -1) return;
subjectList.setSelectedIndex(index);
delete(index);
} else if (e.getClickCount() == 2) {
int[] indexs = subjectList.getSelectedIndices();
load(indexs);
} else {
int index = subjectList.getSelectedIndex();
if (index == -1) return;
int id = idList.get(index);
Component[] comps = main.getDesktopPane().getComponents();
for (Component comp : comps) {
if (comp instanceof Editor) {
Editor ed = (Editor) comp;
if (ed.getId() == id) {
ed.toFront();
break;
}
}
}
}
}
});
}
return subjectList;
}
}
Editor.java
메모 작성과 관련된 인터널 프레임
package memo;
import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.swing.JInternalFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import org.apache.ibatis.session.SqlSession;
import memo.myba.Factory;
public class Editor extends JInternalFrame implements DocumentListener{
int id=0;// 에디터 자신의 아이디
boolean editFlag=false; // 현재 편집 상태
boolean load = false; //처음 로딩된 경우
SqlSession session;
Main main;
private JScrollPane scrollPane;
private JTextArea textArea;
/**
* Create the frame.
*/
public Editor(Main m) {
super("새 메모", true, true, true, true);
this.main = m;
setVisible(true);
addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
activate();
}
});
addInternalFrameListener(new InternalFrameAdapter() {
@Override
public void internalFrameActivated(InternalFrameEvent e) {
activate();
}
});
setBounds(100, 100, 450, 300);
getContentPane().add(getScrollPane(), BorderLayout.CENTER);
}
public void activate() {
//열러진 메모장의 id목록을 가져옴.
if(main.getContent() == null) return;
List<Integer> idList = main.getContent().idList;
//메모목차(Content)에 들어있는 JLIST 항목들
JList jlist = main.getContent().getSubjectList();
for(int i=0; i<idList.size() ; i++) {
int id = idList.get(i);
if(id == this.id) {
jlist.setSelectedIndex(i);
}
}
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setData(MemoVo vo) {
this.setId(vo.getId());
this.setTitle(vo.getSubject());
this.getTextArea().setText(vo.getDoc());
}
public void save() {
if(session == null) {
session = Factory.getFactory().openSession();
}
String doc = getTextArea().getText();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
MemoVo vo = new MemoVo();
//엔터없이 한줄인 경우
int length = doc.indexOf("\n");
if(length == -1) {
length = doc.length();
}
// 제목으로 사용할 문자열을 15자 아내로
length = (length<15)? length : 15;
// 테이블에 저장할 데이터를 vo 객체에 담음.
String subject = doc.substring(0,length);
vo.setNal(sdf.format(new Date()));
vo.setId(id);
vo.setSubject(subject);
vo.setDoc(doc);
try {
int cnt = 0;
// 새글인 경우 insert
if(this.id == 0) {
cnt = session.insert("memo.insert", vo);
// 저장에 성공했다면 저장된 마지막 행번호를 가져와
// 에디터창의 id로 사용하고
// 메모 목차 설정, 에디터창 타이틀 설정
if(cnt>0) {
this.id = session.selectOne("memo.last_row");
String temp = String.format("%-12s %s",vo.getNal(), subject);
if(main.getContent() != null) {
main.getContent().model.addElement(temp);
main.getContent().idList.add(this.id);
}
setTitle(subject);
}
}else { // 수정인 경우 update
cnt = session.update("memo.update", vo);
setTitle(subject);
String temp = String.format("%-12s %s",vo.getNal(), subject);
int index = main.getContent().idList.indexOf(this.id);
main.getContent().model.set(index, temp);
}
if(cnt>0) {
session.commit();
editFlag=false;
}else {
session.rollback();
throw new Exception("저장되지 않았습니다.");
}
}catch(Exception ex) {
ex.printStackTrace();
}
session.close();
session = null;
}
public void changed() {
if( !editFlag && load) {
setTitle(getTitle() + " *");
editFlag = true;
}
load = true; // 이미로딩되었다.
}
protected JScrollPane getScrollPane() {
if (scrollPane == null) {
scrollPane = new JScrollPane();
scrollPane.setViewportView(getTextArea());
}
return scrollPane;
}
protected JTextArea getTextArea() {
if (textArea == null) {
textArea = new JTextArea();
textArea.getDocument().addDocumentListener(this);
textArea.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
activate();
}
});
textArea.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
// Ctrl +s 키를 누를 때
if(e.isControlDown() && e.getKeyCode()== KeyEvent.VK_S) {
save();
}
}
});
}
return textArea;
}
@Override
public void insertUpdate(DocumentEvent e) {
// TODO Auto-generated method stub
changed();
}
@Override
public void removeUpdate(DocumentEvent e) {
// TODO Auto-generated method stub
changed();
}
@Override
public void changedUpdate(DocumentEvent e) {
// TODO Auto-generated method stub
changed();
}
}
Main.java
메모장의 메인 프레임
package memo;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class Main extends JFrame {
protected Content content;
private JPanel contentPane;
private JMenuBar menuBar;
private JMenu mnNewMenu;
private JMenuItem mntmNewMenuItem;
private JMenuItem mntmNewMenuItem_1;
private JMenuItem mntmNewMenuItem_2;
private JMenuItem mntmNewMenuItem_3;
private JMenuItem mntmNewMenuItem_4;
private JDesktopPane desktopPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main frame = new Main();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public void exit() {
int yn = JOptionPane.showConfirmDialog(Main.this,
"프로그매을 종료하시겠습니까?", "프로그램 종료",
JOptionPane.YES_NO_OPTION);
if(yn == JOptionPane.OK_OPTION) {
setDefaultCloseOperation(EXIT_ON_CLOSE);
System.exit(0);
}else {
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
}
public Main() {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
exit();
}
});
setTitle("메모장");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 761, 460);
setJMenuBar(getMenuBar_1());
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(getDesktopPane(), BorderLayout.CENTER);
}
public Content getContent() {
return content;
}
protected JMenuBar getMenuBar_1() {
if (menuBar == null) {
menuBar = new JMenuBar();
menuBar.add(getMnNewMenu());
}
return menuBar;
}
protected JMenu getMnNewMenu() {
if (mnNewMenu == null) {
mnNewMenu = new JMenu("관리");
mnNewMenu.add(getMntmNewMenuItem());
mnNewMenu.add(getMntmNewMenuItem_1());
mnNewMenu.add(getMntmNewMenuItem_2());
mnNewMenu.add(getMntmNewMenuItem_3());
mnNewMenu.add(getMntmNewMenuItem_4());
}
return mnNewMenu;
}
protected JMenuItem getMntmNewMenuItem() {
if (mntmNewMenuItem == null) {
mntmNewMenuItem = new JMenuItem("목차보기");
mntmNewMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(content == null) {
content = new Content(Main.this);
contentPane.add(content,BorderLayout.WEST);
}
}
});
}
return mntmNewMenuItem;
}
protected JMenuItem getMntmNewMenuItem_1() {
if (mntmNewMenuItem_1 == null) {
mntmNewMenuItem_1 = new JMenuItem("새로만들기");
mntmNewMenuItem_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Editor ed = new Editor(Main.this);
desktopPane.add(ed);
ed.toFront();
}
});
}
return mntmNewMenuItem_1;
}
protected JMenuItem getMntmNewMenuItem_2() {
if (mntmNewMenuItem_2 == null) {
mntmNewMenuItem_2 = new JMenuItem("저장");
mntmNewMenuItem_2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Editor ed = (Editor) desktopPane.getSelectedFrame();
ed.save();
}
});
}
return mntmNewMenuItem_2;
}
protected JMenuItem getMntmNewMenuItem_3() {
if (mntmNewMenuItem_3 == null) {
mntmNewMenuItem_3 = new JMenuItem("모두저장");
mntmNewMenuItem_3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Component[] comps = desktopPane.getComponents();
for(Component comp : comps) {
if(comp instanceof Editor) {
Editor ed = (Editor)comp;
ed.save();
}
}
}
});
}
return mntmNewMenuItem_3;
}
protected JMenuItem getMntmNewMenuItem_4() {
if (mntmNewMenuItem_4 == null) {
mntmNewMenuItem_4 = new JMenuItem("종료");
mntmNewMenuItem_4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
exit();
}
});
}
return mntmNewMenuItem_4;
}
protected JDesktopPane getDesktopPane() {
if (desktopPane == null) {
desktopPane = new JDesktopPane();
}
return desktopPane;
}
}
'BOOKS > 수월한 Java' 카테고리의 다른 글
추상 클래스 (0) | 2024.04.07 |
---|---|
인터페이스 (0) | 2024.04.07 |
swing으로 만들어본 멀티 메모장 #1/2 (0) | 2023.03.01 |
List에 담긴 정보를 배열로 변환하기 (0) | 2022.11.27 |
이클립스에서 Data Management로 MySQL 연결하기 (0) | 2021.10.30 |