GXT RIA App 만들기2011. 1. 18. 12:28

이전 강좌에서 우리는 AppView.java에 프로그램의 뼈대를 구성하였다. 이어 이번에는 좌측화면과, 프로그램영역을 제어할 Controller를 생성하고 이Controller에 의해 제어될 View를 구성해 보자.
1. 좌측 메뉴을 제어할 Controller와 AppView내의 viewport의 좌측에 해당되는 부분을 재정의할 View클래스를 생성한다.

☞ NavigationController.java
package com.gxt.riaapp.client.mvc;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.gxt.riaapp.client.AppEvents;
/** * 좌측트리메뉴를 제어할 컬트롤 클래스 
* @author KYS
* 
*/
public class NavigationController extends Controller{	
	private NavigationView view;	// AppView.java에서 정의한 viewport의 WestPanel을 구성함.	
	public NavigationController() {		
		registerEventTypes(AppEvents.Init);	// 사용할 이벤트 등록	
	}	
	public void initialize() {	    
		view = new NavigationView(this);	 
 	}	
	@Override	public void handleEvent(AppEvent event) {		
		forwardToView(view, event);	// 이벤트가 발생할 경우 view로 이동.	
	}
}
☞ NavigationView.java
package com.gxt.riaapp.client.mvc;
import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.View;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.TabItem;
import com.extjs.gxt.ui.client.widget.TabPanel;
import com.extjs.gxt.ui.client.widget.TabPanel.TabPosition;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
/** * AppView클래스의 WEST_PANEL을 구성한다. 
*  
* @author KYS 
*  
*/
public class NavigationView extends View {	
	private ContentPanel westPanel;	
	private TabPanel tabPanel;	
	private TabItem listItem, treeItem;	

	@Override	protected void initialize() {		
		// TODO Auto-generated method stub		
		super.initialize();		
		// AppView의 좌측 패널로 정의된 ContentPanel을 얻어온다. 이렇게 얻어오기 위해선		
		// AppView에 Registry.register(WEST_PANEL, westPanel); 와 같이 기술되어 있어야한다.		
		westPanel = (ContentPanel) Registry.get(AppView.WEST_PANEL);		
		westPanel.setHeading("Navigation");		
		westPanel.setLayout(new FitLayout());		
		westPanel.add(createTabPanel());		
		westPanel.syncSize();	
	}	
	public NavigationView(Controller controller) {		
		super(controller);	
	}	
	@Override	protected void handleEvent(AppEvent event) {	
	}	
	private TabPanel createTabPanel() {		
		tabPanel = new TabPanel();		
		tabPanel.setMinTabWidth(70);		
		tabPanel.setBorderStyle(false);		
		tabPanel.setBodyBorder(false);		
		tabPanel.setTabPosition(TabPosition.BOTTOM);	
	
		treeItem = new TabItem();		
		treeItem.setText("Tree");		
		tabPanel.add(treeItem);	
	
		listItem = new TabItem();		
		listItem.setText("List");		
		tabPanel.add(listItem);		
		return tabPanel;	
	}
}
☞ 위에서 주목해야 할 부분은 "westPanel = (ContentPanel) Registry.get(AppView.WEST_PANEL);"으로 AppView.java에 아래와 같이 추가해야한다. AppView에서 각 영역을 생성했고 이 생성된 영역을 Registry에 등록시키므로 해서 어디서든 Registry.get()으로 이용할 수 있다. 세션처럼 말이다.
/** 
* 좌측 메뉴영역 
*/
private void createWest() {    
	...	
	viewport.add(westPanel, data);	
	Registry.register(WEST_PANEL, westPanel);  // Rigistry에 등록
}
/** 
* 중간 프로그램 영역 
*/
private void createCenter() {    
	....		
	viewport.add(centerPanel, data);	
	Registry.register(CENTER_PANEL, centerPanel); // Rigistry에 등록
}
/** * 상단 영역 구성하기 */
private void createNorth() {    
	....	
	viewport.add(northPanel, data);	
	Registry.register(NORTH_PANEL, northPanel);  // Registry에 등록
}
☞ NavigationController를 RiaApp내의 Dispatcher에 추가하자.
	
public void onModuleLoad() {		
	dispatcher = Dispatcher.get();	    
	dispatcher.addController(new AppController());	    
	dispatcher.addController(new NavigationController());  // 좌측	    
	dispatcher.dispatch(AppEvents.Init);	
}

 

☞ 여기까지 코딩이 끝났다면 실행시켜 결과를 확인을 해보자. 좌측 메뉴 패널이 변경된 것을 확인 할 수 있다.


☞ 이제 남은 것은 툴바를 생성하고 툴바내에 필터를 추가할 것이고, 2개의 TabItem, Tree, List TabItem을 구현해야한다. 그러기 전에 2개의 TabItem에 들어갈 객체를 정의해야 한다. 이 객체는 BaseTreeModel을 상속 받고 getter와 setter메소드들로 채워져 있다. 아래는 Entry.java의 전문이다. com.gxt.riaapp.client.mvc 아래 model 패키지를 생성하고 Entry.java 추가하자.
package com.gxt.riaapp.client.mvc.model;
import com.extjs.gxt.ui.client.Style.HideMode;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
/** 
* 좌측 메뉴의 TreeItem과 ListItem에 채워질 entity class 
*  
* @author winkos1 
*  
*/
public class Entry extends BaseTreeModel {	
	private String name;	
	private String id;	
	private boolean fill;	
	private boolean closable = true;	
	private HideMode hideMode = HideMode.DISPLAY;	
	protected Entry() {	
	}		
	/**	 
	* Constructor	 
	* 	 
	* @param name	 
	* @param example	 
	* @param image	 
	*/	
	public Entry(String name, LayoutContainer example, String image) {		
		this.name = name;		
		set("name", name);		
		set("image", image);		
		set("example", example);	
	}	

	public Entry(String name, LayoutContainer example) {		
		this.name = name;		
		set("name", name);		
		set("example", example);	
	}	

	public Entry(String name, String id, LayoutContainer example) {		
		this.name = name;		
		set("name", name);		
		set("id", id);		
		this.id = id;		
		set("example", example);	
	}	

	public Entry(String name, LayoutContainer example, String image,	
				boolean fill) {		
		this(name, example, image);		
		this.fill = fill;	
	}	

	public Entry(String name, LayoutContainer example, String image,	
			boolean fill, boolean closable) {		
		this(name, example, image, fill);		
		this.closable = closable;	
	}	

	public Entry(String name, LayoutContainer example, String image,			
		boolean fill, boolean closable, HideMode hideMode) {		
		this(name, example, image, fill, closable);		
		this.setHideMode(hideMode);	
	}	

	public String getId() {		
		return id;	
	}		

	public HideMode getHideMode() {		
		return hideMode;	
	}	

	public String getName() {		
		return (String) get("name");	
	}	

	public boolean isClosable() {		
		return closable;	
	}	

	public boolean isFill() {		
		return fill;	
	}	

	public void setFill(boolean fill) {		
		this.fill = fill;	
	}	

	public void setHideMode(HideMode hideMode) {		
		this.hideMode = hideMode;	
	}	

	public String toString() {		
		return getName();	
	}
}

☞ 이번에는 Entry클래스의 상위클래스를 생성하자. 이클래스는 Category클래스로 트리에서 parent역할을 담당할 클래스다. model 패키지에 Category.jav를 추가하고 아래와 같이 코딩하자.
package com.gxt.riaapp.client.mvc.model;
import java.io.Serializable;
import com.extjs.gxt.ui.client.Style.HideMode;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
public class Category extends BaseTreeModel implements Serializable {	
	protected Category() {	
	}	

	public Category(String name) {		
		set("name", name);	
	}	

	public Category(String name, String id) {		
		set("name", name);		
		set("id", id);	
	}	

	public String getId(){		
		return  (String)get("id");	
	}	

	public String getName() {		
		return (String) get("name");	
	}	

	public String toString() {		
		return getName();	
	}	

	public void add(String title, LayoutContainer page, String image) {		
		add(new Entry(title, page, image));	
	}	

	public void add(String title, LayoutContainer page, String image,			
		boolean fill) {		
		add(new Entry(title, page, image, fill));	
	}	

	public void add(String title, LayoutContainer page) {		
		add(new Entry(title, page ));	
	}	
	
	public void add(String title, String id, LayoutContainer page) {			
		add(new Entry(title, id,  page ));	
	}	

	public void add(String title, LayoutContainer page, String image,			
		boolean fill, boolean closable, HideMode hideMode) {		
		add(new Entry(title, page, image, fill, closable, hideMode));	
	}
}

☞ NavigationView.java를 수정하자. 수정할 내용은 툴바를 추가하고 툴바내 필터를 삽입한다. 그리고 TreeItem과 ListItem을 구현하도록 하자.
	
private ToolBar toolBar;	
private StoreFilterField filter;
private TreePanel tree;	
private TreeStore treeStore;		
@Override	protected void initialize() {		
	// TODO Auto-generated method stub		
	super.initialize();		
	// 필터 생성		
	filter = new StoreFilterField() {			
		@Override			
		protected boolean doSelect(Store store,					
			ModelData parent, ModelData child, String property,					
			String filter) {								
			String name = child.get("name");				
			name = name.toLowerCase();				
			if (name.indexOf(filter.toLowerCase()) != -1) return true;				
			return false;			
		}		
	};		
	// AppView의 좌측 패널로 정의된 ContentPanel을 얻어온다. 이렇게 얻어오기 위해선		
	// AppView에 Registry.register(WEST_PANEL, westPanel); 와 같이 기술되어 있어야한다.		
	westPanel = (ContentPanel) Registry.get(AppView.WEST_PANEL);		
	westPanel.setHeading("Navigation");		
	westPanel.setLayout(new FitLayout());		
	westPanel.add(createTabPanel());
				
	toolBar = (ToolBar) westPanel.getTopComponent();	    
	IconButton filterBtn = new IconButton("icon-filter");	  
  	    
	filterBtn.setWidth(20);	    
	toolBar.add(filterBtn);	    
	toolBar.add(filter);	    	    

	createListContent();  // ListItem구현	    
	createTreeContent();	// TreeIem구현	
    		
	westPanel.syncSize();	
}	
private void createTreeContent() {	    
	TreeLoader loader = new BaseTreeLoader(new TreeModelReader>()) {	      
		@Override	      
		public boolean hasChildren(ModelData parent) {	        
			return parent instanceof Category;	      
		}	    
	};	    
	treeStore = new TreeStore(loader);	    
	tree = new TreePanel(treeStore);	    
	tree.expandAll();	    
	tree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);	    
	tree.getStyle().setLeafIcon(IconHelper.createStyle("icon-list"));	    
	tree.setDisplayProperty("name");	
    
	SelectionService.get().addListener(new SourceSelectionChangedListener(tree.getSelectionModel()));	    
	SelectionService.get().register(tree.getSelectionModel());	
    
	filter.bind(treeStore);	    

	treeItem.setBorders(false);	    
	treeItem.setLayout(new FitLayout());	    
	treeItem.add(tree);	  
}		
@SuppressWarnings("unchecked")	
private void createListContent() {		
	listItem.setLayout(new FitLayout());	    
	listItem.setBorders(false);	    

	ListView list = new ListView();	    
	list.setDisplayProperty("name");	    
	list.setBorders(false);	    
	list.getSelectionModel().addSelectionChangedListener(new SelectionChangedListener() {	      
		@Override	      
		public void selectionChanged(SelectionChangedEvent se) {	    	  
			Entry e = se.getSelection().get(0);	        
			if (e != null && e instanceof Entry) {	        	
			// 나중에 구현	        		        
			}	      
		}	    
	});	    

	ListStore store = new ListStore();	    
	store.setStoreSorter(new StoreSorter(new Comparator() {	      
		public int compare(Entry e1, Entry e2) {	        
			return e1.getName().compareTo(e2.getName());	      
		}	    
	}));	    

	list.setStore(store);	    
	filter.bind((Store) store);	    
	listItem.add(list);	
}

☞ 여기까지 끝났다면 실행해보도록 하자. 실행 결과를 보면 "Navigation"클자 아래에 필터와 필터버튼이 추가된 것을 제외하곤 달라진 것이 없을 것이다. 아직 Tree에 아무것도 추가하지 않았으므로 보여지는것이 없는 것이다. 이제 좌측메뉴에 트리가 출력되도록 수정해보자.
☞ com.gxt.riaapp.client.mvc하위에 RiaAppModel.java를 추가하자. 이 클래스는 Category와 Entry클래스를 이용해 좌측 TreeItem과 ListItem에 출력될 수 있도록 구성한다. Category는 상위 폴더를 Entry는 폴더아래의 최종 프로그램이라고 할 수 있다.

package com.gxt.riaapp.client.mvc;
import java.util.ArrayList;
import java.util.List;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.data.ModelData;
import com.gxt.riaapp.client.mvc.model.Category;
import com.gxt.riaapp.client.mvc.model.Entry;
public class RiaAppModel  extends BaseTreeModel {	
	protected List entries = new ArrayList();	

	public RiaAppModel() {		
		Category pgm = new Category("시스템관리","SYS00002");
		//		pgm.add("시스템사용자관리", "0001", new SystemManagerMain());		
		pgm.add("시스템사용자관리", "0001");		
		addCategory(pgm);		
		loadEntries(this);	
	}	

	public void addCategory(ModelData child) {		
		if(child != null)			
			add(child);	  
	}	

	public List getEntries() {		
		return entries;	
	}	

	private void loadEntries(TreeModel model) {		
		for (ModelData child : model.getChildren()) {			
			if (child instanceof Entry) {				
				entries.add((Entry) child);			
			} else if (child instanceof Category) {				
				loadEntries((Category) child);			
			}		
		}	
	}
}

☞ 위 클래스에서는 Category를 생성하고 생성된 Category클래스에 pgm.add()를 이용하여 Entry클래스를 하위로 추가한것을 볼 수 있다. Category클래스의 add()메소드를 보자. add메소드가 여러종류가 있지만 위에서 주석이 된 부분과 그 아래 부분의 add()메소드를 보면 주석달린 add(String title, String id, LayoutContainer page) 메소드는 인자로 LayoutContainer를 받고 있다.
☞ 이 클래스는 우리가 여러개의 프로그램을 작성해서 우측 프로그램 영역에 보여지게 할 것인데 이때 작성되는 프로그램은 모두 LayoutContainer를 상속받아야 한다. 두번째 add(String title, String id)메소드는 테스트를 추가한 메소드로 화면상에 보여주기만 하려고 제목과 아이디만 인자로 받고 있다. 이 메소드를 추가하도록 하자 Category.java와 Entry.java에 아래와 같이 메소드를 추가하도록 하자.
// Category.java
public void add(String title, String id) {		
	add(new Entry(title, id ));	
}

// Entry.java	
public Entry(String name, String id) {		
	this.name = name;		
	set("name", name);		
	set("id", id);		
	this.id = id;	
}
☞ 이제 위에서 작성한 RiaAppModel클래스를 TreeItem과 ListItem에 추가하여 좌측메뉴에 "시스템관리->시스템사용자관리"가 보여지도록 수정해보자. RiaApp.java를 아래와 같이 수정하자.
public static final String MODEL = "RiaAppModel";
private RiaAppModel model;	

public void onModuleLoad() {		
	model = new RiaAppModel();	    
	Registry.register(MODEL, model);  // 최초 실행시 Registry에 RiaAppModel을 등록한다.	    		
	dispatcher = Dispatcher.get();	    
	dispatcher.addController(new AppController());	    
	dispatcher.addController(new NavigationController());  // 좌측	    
	dispatcher.dispatch(AppEvents.Init);	
}
☞ NavigationView.java를 열고 아래와 같이 모델이 세팅될 수 있도록 수정한다.
private RiaAppModel model;
...	
	@Override	
	protected void initialize() {		
		super.initialize();		
		model = (RiaAppModel) Registry.get(RiaApp.MODEL);
		...
	}
	private void createTreeContent() {
		...	    
		filter.bind(treeStore);	    
		loader.load(model); // RiaAppModel set	    	    
		treeItem.setBorders(false);
		...
	}	
	@SuppressWarnings("unchecked")	
	private void createListContent() {
		...	    
		ListStore store = new ListStore();	    
		store.setStoreSorter(new StoreSorter(new Comparator() {	      
			public int compare(Entry e1, Entry e2) {	        
				return e1.getName().compareTo(e2.getName());	      
			}	    
		}));	    
		store.add(model.getEntries());	    	    
		list.setStore(store);
		...
	}

☞ 실행해서 결과를 확인해보자. 아래의 그림과 같이 좌측 트리에 "시스템관리->시스템사용자관리"가 출력되는 것을 볼 수 있다. 필터에 키인을 하고 잘 작동하는지도 확인한다.

Posted by 베니94