1 Construcción de Interfaces a Usuario - ©1999 Construcción de Interfaces a Usuario: Ejemplo Un Editor de Circuitos
2 Construcción de Interfaces a Usuario - ©1999 Component Rectangle bounds getSize() -- getSize() -- returns bounds extentsetSize(Dimension) getLocation() -- getLocation() -- returns bounds origin paint() > Component debe definir: el área que ocupara el componente visual el área que ocupara el componente visual la interface mínima para acceder ese área la interface mínima para acceder ese área la interface para mostrar gráficamente el componente la interface para mostrar gráficamente el componente Representacion de Vistas 4 La metáfora de la transparencia puede ser representada a través de una clase abstracta Component...
3 Construcción de Interfaces a Usuario - ©1999 public class MyView extends Component {...... public void paint(Graphics g) { Dimension d = getSize(); g.drawRect(0,0, d.width - 1, d.height - 1); } En Java se implementaría del modo siguiente Una vista específica se crea como subclase de Component, proveyendo la implementación para el método paint.... El argumento Graphics representa la interface genérica al sistema gráfico nativo... Como se implementa la metáfora de las transparencias superpuestas? Presentación 4 Un componente simple es responsable de producir la representación visual de si mismo
4 Construcción de Interfaces a Usuario - ©1999 Container Component components add(Component)remove(Component) * Component Vista-Subvista Supervista Subvista components Inv for all s:Supervista, ss:Subvista s.isHide => ss.isHide isHide paint for all c:components c.paint isHide Un container propaga, en general, los mensajes a sus hijos visibles Composite padre Componentes Compuestos 4 Una vista se puede componer de otras vistas, por lo tanto existe una abstracción que representa las vistas compuestas...
5 Construcción de Interfaces a Usuario - ©1999 Container::paint(Graphics g){ int i; Rectangle oldBox; oldBox:= g.clipRect; g.translate(this.bounds.origin); g.clipRect( this.bounds); for(i=0; i< components size; i++) if (this.bounds.intesects(c.bounds) c.paint(g); g.clipRect(oldBox)} El método paint de un Container es un método template... Como se distribuyen espacialmente los componentes hijos? paint paint paint paint paint Componentes Compuestos 4 Los componentes compuestos deben verificar que sus hijos estén dentro de la región visible que determinan
6 Construcción de Interfaces a Usuario - ©1999 Alternativa 1: Cada vista compuesta decide la ubicación de sus hijos de acuerdo a su política... import Math.* class PanelView extends Container{ Component north,south,west,east,center; setUp(Component c){ up:= c} setDown... reshape(int x,y; int width, height){ super.reshape(x,y,width,height); north.reshape(0,0,width, Math.trunc(height*.02)) south.reshape(0,height-Math.trunc(height*0.2)).....} Como se varía la distribución ? Distribución de Espacio 4 Una vista compuesta puede definir la distribución espacial de las vistas hijas...
7 Construcción de Interfaces a Usuario - ©1999 Component-Layout Component Layout strategy strategycomponents add(Component c) strategy.add(c)setLayout(Layout) Strategy context add(Component c) context.components.add(c) c.reshape(x,y,w,h) La estrategia permite Variar dinamicamente el layout de los componentes Reutilizar algoritmos comunes de layout por composición Distribución de Espacio 4 La distribución de espacio puede ser delegada en objetos que implementen estrategias de distribución específicas...
8 Construcción de Interfaces a Usuario - ©1999 import java.awt.*; public class GridWindow extends Frame { public GridWindow() { setLayout(new GridLayout(0,2)); add(new Button("Button 1")); add(new Button("2")); add(new Button("Button 3")); add(new Button("Long-Named Button 4")); add(new Button("Button 5")); } public class GridLayout implements LayoutManager {... public void layoutContainer(Container parent) { Insets insets = parent.insets(); int ncomponents = parent.countComponents(); int nrows = rows; int ncols = cols; if (ncomponents == 0) {return;} if (nrows > 0) { ncols = (ncomponents + nrows - 1) / nrows; } else { nrows = (ncomponents + ncols - 1) / ncols; } int w = parent.width - (insets.left + insets.right); int h = parent.height - (insets.top + insets.bottom); w = (w - (ncols - 1) * hgap) / ncols; h = (h - (nrows - 1) * vgap) / nrows; for (int c = 0, x = insets.left ; c < ncols ; c++, x += w + hgap) { for (int r = 0, y = insets.top ; r < nrows ; r++, y += h + vgap) { int i = r * ncols + c; if (i < ncomponents) { parent.getComponent(i).reshape(x, y, w, h); } } Distribución de Espacio
9 Construcción de Interfaces a Usuario - ©1999 Cada componente debe informar: Tamaño preferido (preferredSize()) Tamaño preferido (preferredSize()) Tamaño mínimo si es necesario (minumsSize()) Tamaño mínimo si es necesario (minumsSize()) Lo componentes compuestos deben tener en cuenta estas restricciones para atribuir el espacio ocupado por un componente, bien como calcular su propio tamaño, si no hay restricciones Container::preferredSize(){ int i; Dimension d; for(i=0; i< components size; i++) d := d + components[i].preferredSize; } Distribución de Espacio 4 Los componentes pueden poseer difrentes requerimientos de espacio, asi, un protocolo mas complejo es necesario...
10 Construcción de Interfaces a Usuario - ©1999 Observable hasChanged notifyObservers(arg) for all o in observers o.update setChanged setChanged ()addObserver(Observer) removeObserver(Observ er) Observer update(Observable,arg) inv self reflects sub.state * Observable-Observer-Abstract Inv for all s:Observable, o: s.observers => o.isUpdated observers El mecanismo de anuncio de cambios requiere un soporte implementado para la administración de la lista de observadores. Los observadores sólo deben definir el método que manejará la observación. Sincronización de Presentaciones 4 Una clase observable debe notificar a sus observadores que ha cambiado, un observador solo debe implementar el protocolo necesario para la notificación...
11 Construcción de Interfaces a Usuario - ©1999 Editor de Circuitos public class CircuitApp extends Applet { Circuit circuit=new Circuit(); public void init() { setLayout(new BorderLayout()); add("East",new PartListView(circuit)); add("Center", new LayoutView(circuit)); validate(); } public static void main(String args[]) { Frame f = new Frame("Editor de circuitos"); CircuitApp cir = new CircuitApp(); cir.init(); f.add("Center",cir); f.setSize(400, 200); }
12 Construcción de Interfaces a Usuario - ©1999 class Circuit extends Observable { Vector chips, wires;... public void addChip(Point center) { Chip c=new Chip(center); chips.addElement(c); setChanged(); notifyObservers(c); } } class ListView extends Panel implements Observer { Observable subject; public CircuitView(Circuit c) { setsubject(c); setLayout(new GridLayout(10,1)); for (Enumeration e = c.chips.elements() ; e.hasMoreElements() ;) { add(new ChipText(7,(Chip) e.nextElement())); } setSubject(Observable s){ if( subject != NULL) subject.removeObserver(this) subject:= s; subject.addObserver(this) } update(Observable o, Object arg){ if (arg.instanceOf(Chip) { add(new ChipText(7,arg); } } Sincronización de Presentaciones 4 Una aplicación debe crear la relación entre observadores y sujetos, creando también las dependencias... public class LayoutView extends Panel implements Observer{ Observable subject; public LayoutView(Circuit c) { setsubject(c); setLayout(null); setBounds(0,0,400,400); for (Enumeration e = circuit.chips.elements() ; e.hasMoreElements() ;) { add(new ChipV((Chip) e.nextElement())); } }.....
13 Construcción de Interfaces a Usuario - ©1999 public class ChipV extends Canvas implements Observer{ Observable subject;... public ChipV(Chip c) {.. } public void update(Observable o, Object arg) { repaint(); } public void paint(Graphics g) {..... g.drawString(subject.name,0,0); } Sincronización de Presentaciones class Chip extends Observable { Point center; String name;..... public void name(String s) { if (name.compareTo(s) != 0) { name=s; setChanged(); notifyObservers(this); } } public class ChipText extends TextField implements Observer { public ChipText(int f,Chip c) { super(f); setSubject(c); } public void update(Observable o, Object arg) { setText(subject.name); } }
14 Construcción de Interfaces a Usuario - ©1999 Los componentes compuestos propagan el evento a sus hijos. Si estos no lo tratan, entonces debe intentar tratarlo el mismo... Este mecanismo implica convenciones... Si un componente no esta interesado en tratar un evento entonces debe retornar false... window menu editorViewiconPalletesimpleView simpleView evento Tratamiento de Eventos 4 Las vistas deben ser capaces de reaccionar a eventos de entrada y comunicarlos a la aplicación
15 Construcción de Interfaces a Usuario - ©1999 public boolean processEvent(Event evt) { switch (evt.id) { case Event.MOUSE_ENTER: return mouseEnter(evt, evt.x, evt.y); case Event.MOUSE_EXIT: return mouseExit(evt, evt.x, evt.y); case Event.MOUSE_MOVE: return mouseMove(evt, evt.x, evt.y); case Event.MOUSE_DOWN: return mouseDown(evt, evt.x, evt.y); case Event.MOUSE_DRAG: return mouseDrag(evt, evt.x, evt.y); case Event.MOUSE_UP: return mouseUp(evt, evt.x, evt.y);.... public boolean mouseDown(Event evt, int x, int y){ return false; } public boolean mouseDrag(Event evt, int x, int y) { return false; } public boolean mouseUp(Event evt, int x, int y) { return false; } Tratamiento de Eventos 4 Cada vista podría determinar si trata un evento o no implementando un método determinado...
16 Construcción de Interfaces a Usuario - ©1999 Solución: Delegar el manejo de eventos en objetos separados Component-Listener Component Listener listeners * Los listeners se propagan el evento hasta que alguno lo trate, retornando true. Flexibilizando el Manejo de Eventos 4 El manejo de eventos embutidos en las vistas no permite variar dinámicamente las manipulaciones ni la reutilización de las mismas....
17 Construcción de Interfaces a Usuario - ©1999 public class CircuitApp extends Applet{.. public static void main(String args[]) { Frame f = new Frame("Editor de circuitos"); CircuitApp cir = new CircuitApp(); cir.init(); f.add("Center",cir); f.setSize(400, 200); f.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0);}}); f.show(); } } public class ChipV extends Canvas implements Observer{ public ChipV(Chip c) {.... addMouseMotionListener(new DragChip()); } class DragChip implements MouseMotionListener { public void mouseDragged(MouseEvent e) { setBounds(e.getX()-W/2,e.getY()-H/2,W,H); } } Tratamiento de Eventos
18 Construcción de Interfaces a Usuario - ©1999 Component-Implementation Component ComponentPeer peer paint()handleEvent() Bridge context paint() handleEvent() Toolkit createScrollbar() createButton()... defaultToolkit creates Abstract Factory Inv for all c:Component c.addNotify => Toolkit.defaultToolkit.createComponentPeer c.addNotify => Toolkit.defaultToolkit.createComponentPeer Cada componente visual puede delegar la implementación concreta en otra clase que implemente la interfaz con el sistema nativo. Una clase Toolkit puede implementar la interfaz abstracta para crear componentes específicos del sistema nativo Usando toolkits nativos 4 La interfaz debiera ser adaptable a diferentes looks and feels provistos por los manejadores de ventanas nativos...
19 Construcción de Interfaces a Usuario - ©1999 Swing 4 Nuevos Componentes 4 Look & feel configurable 4 Componentes Beans