Download Ejemplo
Document related concepts
no text concepts found
Transcript
Construcción de Interfaces a Usuario: Ejemplo Un Editor de Circuitos Construcción de Interfaces a Usuario - ©1999 Representacion de Vistas La metáfora de la transparencia puede ser representada a través de una clase abstracta Component... Component debe definir: Component • el área que ocupara el componente visual • la interface mínima para acceder ese área bounds Rectangle • la interface para mostrar gráficamente el componente getSize() -- returns bounds extent setSize(Dimension) getLocation() -- returns bounds origin paint() <<abstract>> Construcción de Interfaces a Usuario - ©1999 Presentación Un componente simple es responsable de producir la representación visual de si mismo Una vista específica se crea como subclase de Component, proveyendo la implementación para el método paint .... En Java se implementaría del modo siguiente public class MyView extends Component { ...... El argumento Graphics representa la interface genérica al sistema gráfico nativo... public void paint(Graphics g) { Dimension d = getSize(); g.drawRect(0,0, d.width - 1, d.height - 1); } Como se implementa la metáfora de las transparencias superpuestas? Construcción de Interfaces a Usuario - ©1999 Componentes Compuestos Una vista se puede componer de otras vistas, por lo tanto existe una abstracción que representa las vistas compuestas... Vista-Subvista Component Supervista components isHide components * Component add(Component) remove(Component) isHide padre paint Container Subvista for all c:components c.paint Composite Inv for all s:Supervista, ss:Subvista s.isHide => ss.isHide Un container propaga, en general, los mensajes a sus hijos visibles Construcción de Interfaces a Usuario - ©1999 Componentes Compuestos Los componentes compuestos deben verificar que sus hijos estén dentro de la región visible que determinan paint El método paint de un Container es un método template... paint paint Container::paint(Graphics g){ int i; Rectangle oldBox; oldBox:= g.clipRect; g.translate(this.bounds.origin); g.clipRect( this.bounds); paint paint for(i=0; i< components size; i++) if (this.bounds.intesects(c.bounds) c.paint(g); g.clipRect(oldBox) } Como se distribuyen espacialmente los componentes hijos? Construcción de Interfaces a Usuario - ©1999 Distribución de Espacio Una vista compuesta puede definir la distribución espacial de las vistas hijas... 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 ? } Construcción de Interfaces a Usuario - ©1999 Distribución de Espacio La distribución de espacio puede ser delegada en objetos que implementen estrategias de distribución específicas... Component-Layout Component strategy components add(Component c) strategy.add(c) setLayout(Layout) Layout strategy context Strategy 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 Construcción de Interfaces a Usuario - ©1999 Distribución de Espacio public class GridLayout implements LayoutManager { ... public void layoutContainer(Container parent) { Insets insets = parent.insets(); int ncomponents = parent.countComponents(); int nrows = rows; int ncols = cols; 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")); } 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); } } } } Construcción de Interfaces a Usuario - ©1999 Distribución de Espacio Los componentes pueden poseer difrentes requerimientos de espacio, asi, un protocolo mas complejo es necesario... Cada componente debe informar: • Tamaño preferido (preferredSize()) • 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; } Construcción de Interfaces a Usuario - ©1999 Sincronización de Presentaciones Una clase observable debe notificar a sus observadores que ha cambiado, un observador solo debe implementar el protocolo necesario para la notificación... Observable-Observer-Abstract El mecanismo de anuncio de cambios requiere un Observer soporte implementado para la administración de update(Observable,arg) la lista de observadores. Observable hasChanged <<Notify>> notifyObservers(arg) for all o in observers o.update observers * setChanged () <<Register>> inv self reflects sub.state Los observadores sólo deben definir el método que manejará la observación. addObserver(Observer) removeObserver(Observ er) Inv for all s:Observable , o: s.observers => o.isUpdated 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); } } Construcción de Interfaces a Usuario - ©1999 Sincronización de Presentaciones Una aplicación debe crear la relación entre observadores y sujetos, creando también las dependencias... class Circuit extends Observable { Vector chips, wires; ... public void addChip(Point center) { Chip c=new Chip(center); chips.addElement(c); setChanged(); notifyObservers(c); } } 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())); } } ..... 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); } } } Construcción de Interfaces a Usuario - ©1999 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 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); }} 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); } } Construcción de Interfaces a Usuario - ©1999 Tratamiento de Eventos Las vistas deben ser capaces de reaccionar a eventos de entrada y comunicarlos a la aplicación iconPallete simpleView Los componentes compuestos propagan el evento a sus hijos. Si estos no lo tratan, entonces debe intentar tratarlo el mismo... editorView evento Este mecanismo implica convenciones... Si un componente no esta interesado en tratar un evento entonces debe retornar false... window simpleView menu Construcción de Interfaces a Usuario - ©1999 Tratamiento de Eventos Cada vista podría determinar si trata un evento o no implementando un método determinado... 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; } Construcción de Interfaces a Usuario - ©1999 Flexibilizando el Manejo de Eventos El manejo de eventos embutidos en las vistas no permite variar dinámicamente las manipulaciones ni la reutilización de las mismas.... 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. Construcción de Interfaces a Usuario - ©1999 Tratamiento de Eventos 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); } } } Construcción de Interfaces a Usuario - ©1999 Usando toolkits nativos La interfaz debiera ser adaptable a diferentes looks and feels provistos por los manejadores de ventanas nativos... Component-Implementation Cada componente visual puede delegar la implementación Bridge Component ComponentPeer concreta en otra clase que peer context paint() handleEvent() implemente la interfaz con el paint() sistema nativo. handleEvent() Toolkit defaultToolkit Abstract Factory Una clase Toolkit puede creates implementar la interfaz createScrollbar() abstracta para crear createButton()... componentes específicos del sistema nativo Inv for all c:Component c.addNotify => Toolkit.defaultToolkit.createComponentPeer Construcción de Interfaces a Usuario - ©1999 Swing Nuevos Componentes Look & feel configurable Componentes Beans Construcción de Interfaces a Usuario - ©1999