עקרונות תכנות מונחה עצמים תרגול 8: MVC
Outline MVC Using the default models Example- File Browser
MVC (Model View Controller) “Model–view–controller (MVC) is a software architectural pattern for implementing user interfaces. It divides a given software application into three interconnected parts, so as to separate internal representations of information from the ways that information is presented to or accepted from the user.” -Wikipedia
MVC - Model A model stores data that is altered by the controller and displayed in the view. Whenever there is a change to the data it is updated by the controller
MVC - View A view requests information from the model that it uses to generate an output representation to the user
MVC - Controller A controller can send commands to the model to update the model's state (e.g., editing a document). It can also send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document)
Outline MVC Using the default models Example- File Browser
Using the Default Models Swing supplies default, mutable models for all widgets: List Tree (nodes) Table Spinner Slider …
getModel public boolean isPressed() { return getModel().isPressed(); } JButton +isPressed: boolean +getModel: ButtonModel ButtonModel +isPressed: boolean public boolean isPressed() { return getModel().isPressed(); }
Example 1 DefaultListModel
Default List Model Example Add and remove elements from a list using the default model: public class SimpleList extends JFrame { private JList _list; private DefaultListModel _model; ... }
Constructor with Default List Model public SimpleList() { super( "ListDemo" ); setModel(new DefaultListModel()); setList(new JList(_model)); getList().setPreferredSize(new Dimension(100, 300)); getContentPane().add(getList(), BorderLayout.CENTER); JToolBar tBar = new JToolBar(); JButton tAdd = new JButton("+"); // calls add() tBar.add(tAdd); JButton tRem = new JButton("-"); // calls rem() tBar.add(tRem); getContentPane().add(tBar, BorderLayout.NORTH); ... }
Adding and Removing with the Default Model private void add() { String tVal = JOptionPane.showInputDialog( this, "Add new value"); if (tVal != null) { getModel().add(getModel().getSize(), tVal); } private void rem() { int[] is = getList().getSelectedIndices(); Arrays.sort(is); for (int i = is.length - 1; i >= 0; i--) { getModel().remove(is[i]); }}
Outline MVC Using the default models Example- File Browser
A Simple File Broswer The file browser shows: Directories tree List of files in the selected directory Content of the selected file
Example 2 FileBrowser
Model View Controller File Browser Tree Model List Model Tree Cell Render List Cell Render The File Browser View Controller
File Browser Tree Model List Model Tree Cell Render List Cell Render The File Browser
A Model for the Tree View Tree model Get Root Get Children count per node Get Childe for index Check if a node is leaf Directories tree model Root = Top directory Children = sub-directories in the directory Leaf = A directory without sub-directory
The Directories Tree Model public class DirTreeModel implements TreeModel { private File _dir; public DirTreeModel(File dir) { setDir(dir); } private static FileFilter _FILTER = new FileFilter() { public boolean accept(File file) { return file.isDirectory(); }; private File[] list(File file) { return file.listFiles(_FILTER); Anonymous class
Tree Model Inferface Methods public Object getRoot() { return getDir(); } public Object getChild(Object parent, int index) { File[] tChildren = list((File)parent); return tChildren[index]; public int getChildCount(Object parent) { return list((File)parent).length;
Tree Model Interface Methods (cont’d) public boolean isLeaf(Object node) { return getChildCount(node) == 0; } public int getIndexOfChild(Object parent, Object child) { List<File> tFiles = Arrays.asList(list((File)parent)); return tFiles.indexOf((File)child);
File Browser Tree Model List Model Tree Cell Render List Cell Render The File Browser
TreeCellRenderer Defines the requirements for an object that displays a tree node. Implemented by DefaultTreeCellRenderer getTreeCellRendererComponent(..) gets the components properties and configures the renderer Add it to your VIEW
Rendering a Directories Tree Cell public class DirTreeCellRender extends DefaultTreeCellRender{ public Component GetTreeCellRenderComponent( JTree tree, Object Value, boolean cell, boolean expended, boolean leaf, int row, boolean hasFocus) { super.getCellRenderComponent(…); File tRoot = (File) getModel().getRoot(); File tFile = (File)value; setText(tRoot.equals(value)?tFile.getAbsolutePath(): tFile.getName()); setIcon( expanded? openIcon : closeIcon ); return this; }}
File Browser Tree Model List Model Tree Cell Render List Cell Render The File Browser
List Model Our List Model is mutable (changes when we change directory) This means we need to implement the following interface methods: addListDataListener(ListDataListener l) removeListDataListener(ListDataListener l) Otherwise, the list view won’t update when the model is changed
Files List Model public class FilesListModel implements ListModel { private File _dir; private List<File> _files; private Collection<ListDataListener> _listeners; private static FileFilter _FILTER = new FileFilter() { public boolean accept(File f) { return f.isFile(); } }; public FilesListModel() { setListeners(new LinkedList<ListDataListener>()); setFiles(Collections.EMPTY_LIST); 18 / 31
Files List Model Interface Methods public int getSize() { return getFiles().size(); } public Object getElementAt( int index) { return getFiles().get(index); public void addListDataListener(ListDataListener l) { getListeners().add(l); public void removeListDataListener(ListDataListener l) { getListeners().remove(l);
Setting the Directory public File getDir() { return _dir; } public void setDir(File dir) { _dir = dir; int tOldSize = getSize(); File[] tFiles = dir.listFiles(_FILTER); setFiles(Arrays.asList(tFiles)); int tMax = Math.max(tOldSize, getSize()); ListDataEvent tEvt = new ListDataEvent(this,LDE.CONTENTS_CHANGED, 0,tMax); for (ListDataListener tListener : getListeners()) { tListener.contentsChanged(tEvt); { 19 / 31
File Browser Tree Model List Model Tree Cell Render List Cell Render The File Browser
Rendering a Files List Cell public class FilesListCellRenderer extends DefaultListCellRenderer{ private static Color _ALT = new Color(220, 220, 220); public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus( { super.getListCellRendererComponent(...); File f = (File) value; setText(f.getName()); if (!isSelected && index % 2 == 0){ setBackground(_ALT); return this; }
File Browser Tree Model List Model Tree Cell Render List Cell Render The File Browser
Putting It Together public class FileBrowser extends JFrame implements TreeSelectionListener, ListSelectionListener { private JTree _tree; private JTextArea _content; private JList _files; ... }
File Broswer Constructor public FileBrowser(File file) { super(file.getAbsolutePath()); // Tree setTree(new JTree(new DirTreeModel(file))); getTree().setCellRenderer(new DirTreeCellRenderer()); getTree().addTreeSelectionListener(this); // File list setFiles(new JList(new FilesListModel())); getFiles().setCellRenderer(new FilesListCellRenderer()); getFiles().setSelectionMode(LSM.SINGLE_SELECTION); getFiles().addListSelectionListener(this); // Text area setContent(new JTextArea(10, 30)); ... } View View Model Model View with default model
Listening to Tree Events public void valueChanged(TreeSelectionEvent e) { File tDir = (File) e.getPath().getLastPathComponent(); ((FilesListModel)getFiles().getModel()).setDir(tDir); }
Listening to List Events public void valueChanged(ListSelectionEvent e) { // If the value is still adjusting we get two events, Un-select and select if (e.getValueIsAdjusting()) return; File tFile = (File) getFiles().getSelectedValue(); getContent().setText(""); try { BufferedReader tIn = new BufferedReader(new FileReader(tFile)); String tLine; while ((tLine = tIn.readLine()) != null) { getContent().append(tLine + "\n"); } tIn.close(); getContent().setCaretPosition(0); } … }