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