基于SmartGwt的分页组件


        最近发现oschina不给我发每周精彩回顾了,才想起距离上次写东西已经有9个月了,于是狠狠的惭愧了一把。程序员光干活,不总结是不行的,于是便跟大家分享下下面这些开发体验。


        最近公司的外网项目,都准备用Gwt开发,经过之前的一些试验,公司决定采用SmartGwt 2.4作为前台框架。经过一段使用之后,感觉SmartGwt基本上比较容易上手。不过,虽然他的showcase的例子挺丰富,但只覆盖了很少的api,所以基本上还是天天翻着api来开发。


        其组件库涵盖了绝大多数常见的控件,但是没有分页条,居然少了这么常用的组件,着实让人遗憾。先google了一下,没有令人满意的现成的扩展组件,查了半天,只在一个英文论坛里,发现一个半成品,只是个实现了部分页面展示逻辑的页面组件,而且没有数据交互。无奈,找不到现成的,只能自己动手丰衣足食了。

 

一、定义分页条功能

 

功能至少要满足基本需要。

比如对于用户来说,页面功能应该包括:

1、能够定位数据到:首页,上一页,下一页,末页

2、能够展示当前页和总页数

3、直接跳转到指定页(大于零,小于总页数)

4、根据当前页和总页数,控制按钮可用状态

对于程序员来说,这个组件应该可以:

1、方便的初始化工具条组件并能绑定数据展示组件(比如grid

2、定制每页记录数

3、最好和数据查询方法分离,消除对业务查询方法的依赖

4、最好能有个模态遮罩,在数据加载过程中,给予提示,同时限制用户的重复操作。

 

二、代码实现

 

经过分析和简单的设计,决定通过两个类来实现:

1、一个纯页面组件,继承自com.smartgwt.client.widgets.toolbar.ToolStrip。代码实现思路就不写了,反正是从混沌到清晰的一个过程。直接帖代码了

  

import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.toolbar.ToolStrip;

/**
 * 
 * 分页工具条
 * 
 * 使用方法:
 * 1、实例化一个分页工具栏,传入每页记录条数和绑定的grid
 * 2、实现一个ListGridDataControl(实例化方法参见类中注释,注意和分页工具的相互调用),并把它设值给分页工具栏的dataControl属性,
 *    注意:分页工具条和ListGridDataControl需要紧密配合相互调用。
 * 3、调用active()方法,激活分页条并加载grid数据
 * 
 * 提示:
 * 1、本分页工具不处理grid的行号,行号理解为grid的列定义范畴
 * 2、可以多次调用active()方法,调用的结果相当于把分页栏定位到首页,并重新获取总页数
 * 3、如果查询业务数据的参数改变时,可以重新实现并设置ListGridDataControl,然后调用active()
 * 4、可以调用setMaskMessage()设置遮罩的提示信息,如果不设置,采用默认值
 * 
 * 说明:
 * 之所以需要ListGridDataControl回调分页工具条的方法,导致增加了相互依赖,主要是考虑到获取数据和获取记录总数的方法通常是异步的,
 * 为保持操作的同步性,只能通过互相调用的方式保持工具条和grid的正常工作。
 * 
 * @author weichao
 * @version 1.0
 */
public class GridPaginationBar extends ToolStrip{

	/**
	 * 模态遮罩
	 */
	ModalWindow mask;
	
	private String maskMessage = " 数据加载中...... ";

	public void setMaskMessage(String maskMessage) {
		this.maskMessage = maskMessage;
	}
	/**
	 * 每页条数
	 */
	private int pageSize = -1;
	
	/**
	 * 当前页
	 */
	private int pageNum = -1;
	
	/**
	 * 总页数
	 */
	private int totalPage = -1;
	
	/**
	 * 数据记录条数
	 */
	private int totalRowNum = -1;
	
	/**
	 * 绑定的grid
	 */
	private final ListGrid grid;
	
	//首页 	TopImgButton first = new TopImgButton(16,16,"/img/control_start_blue.png","/img/control_start.png");
	
	//上一页
	TopImgButton backward = new TopImgButton(16,16,"/img/control_rewind_blue.png","/img/control_rewind.png");
	
	//下一页
	TopImgButton forward = new TopImgButton(16,16,"/img/control_fastforward_blue.png","/img/control_fastforward.png");
	
	//末页
	TopImgButton last = new TopImgButton(16,16,"/img/control_end_blue.png","/img/control_end.png");	
	
	//输入框form
	DynamicForm pageForm;
	
	//页码输入框
	protected TextItem pageText;
	
	//转到按钮
	IButton go = new IButton("转到");
	
	//总页数
	protected Label totalLabel;
	
	//grid数据控制器
	private ListGridDataControl dataControl;
	
	public void setDataControl(ListGridDataControl dataControl){
		this.dataControl = dataControl;
	}
	
	public GridPaginationBar(ListGrid listGrid, int pageSize) {
		this(listGrid, pageSize,null);
	}

	/**
	 * 
	 * 构造方法中,只初始化工具条元素和事件,不初始化数据,
	 * 构造完成后工具条处于禁用状态,需要首先传入grid数据控制器,
	 * 然后调用active()方法来激活工具条,并开始加载数据。
	 * 
	 * @param listGrid 绑定的grid
	 * @param pageSize 每页记录条数
	 * @param maskCanvas 加载数据过程中,需要用遮罩遮盖的控件,如果传入null,则默认遮盖的对象为绑定的grid
	 */
	public GridPaginationBar(ListGrid listGrid, int pageSize, Canvas maskCanvas) {
		this.grid = listGrid;
		mask = new ModalWindow(maskCanvas == null ? grid : maskCanvas);
		mask.setLoadingIcon("/img/loading.gif");
		this.pageSize = pageSize;
		first.setButtonTip("首页");
		first.setButtonClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				goToPage(1);
			}
		});
		backward.setButtonTip("上一页");
		backward.setButtonClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event){
				goToPage(pageNum - 1);
			}
		});
		pageText = new TextItem();
		pageText.setWidth(40);
		pageText.setHeight(20);
		pageText.setShowTitle(false);
		pageText.setTextAlign(Alignment.RIGHT);
		go.setWidth(34);
		go.setHeight(20);
		go.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				String page = pageText.getValueAsString();
				if(page == null || page.trim().equals("")){
					SC.say("请输入页码");
					return ;
				}else {
					try{
						int pageint = Integer.valueOf(page);
						if(pageint<1){
							SC.say("输入的页码不能小于1");
							return;
						}else if(pageint>totalPage){
							SC.say("输入的页码不能大于总页数 "+totalPage);
							return;
						}else {
							goToPage(pageint);
						}
					}catch(NumberFormatException e){
						SC.say("请输入整数");
						return;
					}
				}
			}});
		
		forward.setButtonTip("下一页");
		forward.setButtonClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event){
				goToPage(pageNum + 1);
			}
		});

		last.setButtonTip("末页");
		last.setButtonClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event){
				goToPage(totalPage);
			}
		});

		totalLabel = new Label();
		totalLabel.setWrap(false);
		totalLabel.setWidth(50);

		setHeight(20);
		setOverflow(Overflow.VISIBLE);
		setStyleName("normal");
		this.setMargin(5);

		pageForm = new DynamicForm();
		pageForm.setNumCols(1);
		pageForm.setWidth(45);
		pageForm.setItems(pageText);
		Label blank1 = new Label();
		blank1.setContents(" ");
		blank1.setWidth(5);
		blank1.setHeight(20);

		Label blank2 = new Label();
		blank2.setContents(" ");
		blank2.setWidth(5);
		blank2.setHeight(20);

		Label blank3 = new Label();
		blank3.setContents(" ");
		blank3.setWidth(15);
		blank3.setHeight(20);

		Label blank4 = new Label();
		blank4.setContents(" ");
		blank4.setWidth(5);
		blank4.setHeight(20);
		
		Label blank5 = new Label();
		blank5.setContents(" ");
		blank5.setWidth(5);
		blank5.setHeight(20);
		
		addMember(first);
		addMember(blank1);
		addMember(backward);
		addMember(blank2);
		addMember(pageForm);
		addMember(go);
		addMember(blank5);
		addMember(forward);
		addMember(blank4);
		addMember(last);
		addMember(blank3);
		addMember(totalLabel);
		setAlign(Alignment.RIGHT);
		resetPaginationState();
	}

	/**
	 * 转到指定页,获取分页数据并装载,
	 * 获取数据过程中,加模态遮罩
	 * 
	 * @param pageNum
	 */
	public void goToPage(int pageNum) { 
		go.disable();
		if (pageNum > totalPage)
			pageNum = totalPage;
		if (pageNum < 1)
			pageNum = 1;
		if (pageNum == this.pageNum) {
			go.enable();
			return;
		}
		mask.show(maskMessage, true);
		//fetch data and reload grid
		this.pageNum = pageNum;
		int startNum = (pageNum - 1) * pageSize;
		dataControl.fetchData(startNum, pageSize);		
	}

	/**
	 * 根据记录总数计算总页数
	 */
	private void countTotalPages()
	{
		if(totalRowNum < 1){
			totalPage = -1;
			return;
		}
		int pages = (int) Math.ceil(((float) totalRowNum) / ((float) pageSize));
		// never return zero pages
		if (pages == 0)
			pages = 1;
		this.totalPage=pages;
		pageNum = 1;
	}
	
	/**
	 * 首次查询数据,获取数据总数等信息,并更新分页条状态
	 * 获取数据过程中,加模态遮罩
	 */
	public void active(){
		if(dataControl == null){
			SC.say("错误","分页栏未配置ListGridDataControl数据控制器,请检查代码");
			return;
		}		
		//首先禁用工具条
		if(pageNum != -1){
			resetPaginationState();
		}
		mask.show(maskMessage, true);
		dataControl.getTotal();		
	}
	
	/**
	 * ListGridDataControl获取总记录数后的回调方法
	 * @param all
	 */
	public void afterGetTotal(int all){
		this.totalRowNum = all;
		countTotalPages();
		if(totalPage==-1){
			//如果没有查询到数据,则清空原来的数据
			afterFetchData(new ListGridRecord[0]);
		} else {
			dataControl.fetchData(0, pageSize);
		}		
	}
	
	/**
	 * ListGridDataControl获取数据后的回调方法
	 * @param datas
	 */
	public void afterFetchData(ListGridRecord[] datas){
		if(datas != null){
			dataControl.loadData(datas);
		}
		updatePageinationState();
		mask.hide();
	}
	
	/**
	 * 清除分页工具条状态,全部置灰,不负责清空grid当前数据
	 */
	private void resetPaginationState(){
		pageNum = -1;
		totalPage = -1;
		totalRowNum = -1;
		first.disable();
		backward.disable();
		last.disable();
		forward.disable();
		pageText.disable();
		go.disable();
		totalLabel.setContents("");
	}

	/**
	 * 依据当前页和总页数控制工具条状态
	 * 规则:
	 * 		当前页为-1,则说明工具条尚未激活或者没有查询到数据,不需要处理;
	 *		当前页等于1,则首页按钮和上一页按钮置灰,否则,激活;
	 * 		当前页等于总页数,则末页按钮和上一页按钮置灰,否则,激活;
	 */
	private void updatePageinationState() {
		if(pageNum==-1||totalPage==-1){
			return;
		}
		totalLabel.setContents("第" + pageNum + "页/共" + totalPage + "页");
		pageText.enable();
		go.enable();
		pageText.setValue(pageNum);
		if (pageNum == 1){
			first.disable();
			backward.disable();
		} else {
			first.enable();
			backward.enable();
		}
		if (pageNum == totalPage){
			last.disable();
			forward.disable();
		} else {
			last.enable();
			forward.enable();
		}
	}
}

 

2、一个控制接口类

即GridPaginationBar 类里的ListGridDataControl ,代码如下:

 

import com.smartgwt.client.widgets.grid.ListGridRecord;

/**
 * 
 * @author weichao
 * listGrid数据获取及装载接口 ,和工具条有紧密的相互调用
 * grid加分页工具条时,需要实现此接口并提供给分页工具条
 * 
 * 
 * 示例:
 * 
 * GridPaginationBar pageBar = new GridPaginationBar(bizGrid,20,bizGrid.getParentElement());
 * 
 * pageBar.setDataControl(new ListGridDataControl(){
 *			public void fetchData(int start, int pageSize) {
 *				service.getBusiness(sid,start,pageSize,
 *		                new AsyncCallback<List<XXXXXVO>>() {
 *		                    public void onFailure(Throwable caught) {
 *		                        Window.alert(caught.getMessage());
 *		                    }
 *		                    public void onSuccess(List<XXXXXVO> result) {
 *		                        list = result;
 *		                        pageBar.afterFetchData(RenderBizData.getNewRecords(result));
 *		                    }
 *		                });
 *			}
 *			public void getTotal() {	
 *				service.getBusinessTotal(sid, new AsyncCallback<Integer>(){
 *					public void onFailure(Throwable arg0) {
 *						Window.alert(arg0.getMessage());
 *					}
 *					public void onSuccess(Integer arg0) {
 *						pageBar.afterGetTotal(arg0);
 *					}} );
 *			}
 *			public void loadData(ListGridRecord[] records) {
 *				 bizGrid.setData(records);
 *			}});
 */
public interface ListGridDataControl {

	/**
	 * 获取分页数据,并回调分页工具栏的afterFetchData()方法,
	 * 如果需要异步获取数据,请注意回调afterFetchData()的时机,保持数据的同步操作。
	 * 
	 * @param start
	 * @param pageSize
	 */
	public void fetchData(int start,int pageSize);
	
	/**
	 * 获取数据记录总数,并回调分页工具栏的afterGetTotal()方法
	 * 如果需要异步获取数据,请注意回调afterGetTotal()的时机,保持数据的同步操作。
	 * 
	 * @return 总数
	 */
	public void getTotal();
	
	/**
	 * 把数据装载入grid 
	 * @param records
	 */
	public void loadData(ListGridRecord[] records);
}

 

3、从网上找了个模态窗口ModalWindow ,直接使用了,这里就不贴代码了:

需要的可以从这里找

http://code.google.com/p/smartgwt-extensions/source/browse/trunk/mainprojects/src/main/java/com/smartgwt/extensions#extensions%2Fmodal%2Fclient%253Fstate%253Dclosed

 

4、TopImgButton类是自己实现的一个按钮类,当然你可以用smartgwt的按钮 ,或者你自己写个。


import com.smartgwt.client.types.Cursor;
import com.smartgwt.client.widgets.Img;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;

/**
 * @author weichao
 * 有激活和置灰两种状态的图片按钮
 */
public class TopImgButton extends HLayout{
	Img enable = new Img();
	Img disable = new Img();
	private boolean _enable = true;
	public TopImgButton(int buttonWidth,int buttonHeight,String enableImgSrc,String disableImgSrc){
		setWidth(buttonWidth);
		setHeight(buttonHeight);
		enable.setSrc(enableImgSrc);
		enable.setWidth(buttonWidth);
		enable.setHeight(buttonHeight);
		enable.setCursor(Cursor.POINTER);
		disable.setSrc(disableImgSrc);
		disable.setWidth(buttonWidth);
		disable.setHeight(buttonHeight);
		addMember(enable);
	}
	
	public void enable(){
		if(!_enable){
			removeMember(disable);
			addMember(enable);
			_enable = !_enable;
		}
	}
	
	public void disable(){
		if(_enable){
			removeMember(enable);
			addMember(disable);
			_enable = !_enable;
		}
	}
	
	public void setButtonClickHandler(ClickHandler handler){
		enable.addClickHandler(handler);
	}
	
	public void setButtonTip(String tipMessage){
		enable.setTooltip(tipMessage);
	}
}

三、效果

 

注释里写的比较多了,还有示例也帖在类的注释里了,所以就不再解释怎么用了。

经过几番测试,感觉运行比较稳定。希望能对需要smartgwt分页的朋友有所帮助。

你可能感兴趣的:(分页,gwt,pagination,PAGER,smartgwt)