基于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。代码实现思路就不写了,反正是从混沌到清晰的一个过程。直接帖代码了
  
001
<span class="Apple-style-span">import com.smartgwt.client.types.Alignment;
002
import com.smartgwt.client.types.Overflow;
003
import com.smartgwt.client.util.SC;
004
import com.smartgwt.client.widgets.Canvas;
005
import com.smartgwt.client.widgets.IButton;
006
import com.smartgwt.client.widgets.Label;
007
import com.smartgwt.client.widgets.events.ClickEvent;
008
import com.smartgwt.client.widgets.events.ClickHandler;
009
import com.smartgwt.client.widgets.form.DynamicForm;
010
import com.smartgwt.client.widgets.form.fields.TextItem;
011
import com.smartgwt.client.widgets.grid.ListGrid;
012
import com.smartgwt.client.widgets.grid.ListGridRecord;
013
import com.smartgwt.client.widgets.toolbar.ToolStrip;
014
 
015
/**
016
 *
017
 * 分页工具条
018
 *
019
 * 使用方法:
020
 * 1、实例化一个分页工具栏,传入每页记录条数和绑定的grid
021
 * 2、实现一个ListGridDataControl(实例化方法参见类中注释,注意和分页工具的相互调用),并把它设值给分页工具栏的dataControl属性,
022
 *    注意:分页工具条和ListGridDataControl需要紧密配合相互调用。
023
 * 3、调用active()方法,激活分页条并加载grid数据
024
 *
025
 * 提示:
026
 * 1、本分页工具不处理grid的行号,行号理解为grid的列定义范畴
027
 * 2、可以多次调用active()方法,调用的结果相当于把分页栏定位到首页,并重新获取总页数
028
 * 3、如果查询业务数据的参数改变时,可以重新实现并设置ListGridDataControl,然后调用active()
029
 * 4、可以调用setMaskMessage()设置遮罩的提示信息,如果不设置,采用默认值
030
 *
031
 * 说明:
032
 * 之所以需要ListGridDataControl回调分页工具条的方法,导致增加了相互依赖,主要是考虑到获取数据和获取记录总数的方法通常是异步的,
033
 * 为保持操作的同步性,只能通过互相调用的方式保持工具条和grid的正常工作。
034
 *
035
 * @author weichao
036
 * @version 1.0
037
 */
038
public class GridPaginationBar extends ToolStrip{
039
 
040
    /**
041
     * 模态遮罩
042
     */
043
    ModalWindow mask;
044
     
045
    private String maskMessage = " 数据加载中...... ";
046
 
047
    public void setMaskMessage(String maskMessage) {
048
        this.maskMessage = maskMessage;
049
    }
050
    /**
051
     * 每页条数
052
     */
053
    private int pageSize = -1;
054
     
055
    /**
056
     * 当前页
057
     */
058
    private int pageNum = -1;
059
     
060
    /**
061
     * 总页数
062
     */
063
    private int totalPage = -1;
064
     
065
    /**
066
     * 数据记录条数
067
     */
068
    private int totalRowNum = -1;
069
     
070
    /**
071
     * 绑定的grid
072
     */
073
    private final ListGrid grid;
074
     
075
    //首页</span><span class="Apple-style-span">  TopImgButton first = new TopImgButton(16,16,"/img/control_start_blue.png","/img/control_start.png");
076
     
077
    //上一页
078
    TopImgButton backward = new TopImgButton(16,16,"/img/control_rewind_blue.png","/img/control_rewind.png");
079
     
080
    //下一页
081
    TopImgButton forward = new TopImgButton(16,16,"/img/control_fastforward_blue.png","/img/control_fastforward.png");
082
     
083
    //末页
084
    TopImgButton last = new TopImgButton(16,16,"/img/control_end_blue.png","/img/control_end.png");
085
     
086
    //输入框form
087
    DynamicForm pageForm;
088
     
089
    //页码输入框
090
    protected TextItem pageText;
091
     
092
    //转到按钮
093
    IButton go = new IButton("转到");
094
     
095
    //总页数
096
    protected Label totalLabel;
097
     
098
    //grid数据控制器
099
    private ListGridDataControl dataControl;
100
     
101
    public void setDataControl(ListGridDataControl dataControl){
102
        this.dataControl = dataControl;
103
    }
104
     
105
    public GridPaginationBar(ListGrid listGrid, int pageSize) {
106
        this(listGrid, pageSize,null);
107
    }
108
 
109
    /**
110
     *
111
     * 构造方法中,只初始化工具条元素和事件,不初始化数据,
112
     * 构造完成后工具条处于禁用状态,需要首先传入grid数据控制器,
113
     * 然后调用active()方法来激活工具条,并开始加载数据。
114
     *
115
     * @param listGrid 绑定的grid
116
     * @param pageSize 每页记录条数
117
     * @param maskCanvas 加载数据过程中,需要用遮罩遮盖的控件,如果传入null,则默认遮盖的对象为绑定的grid
118
     */
119
    public GridPaginationBar(ListGrid listGrid, int pageSize, Canvas maskCanvas) {
120
        this.grid = listGrid;
121
        mask = new ModalWindow(maskCanvas == null ? grid : maskCanvas);
122
        mask.setLoadingIcon("/img/loading.gif");
123
        this.pageSize = pageSize;
124
        first.setButtonTip("首页");
125
        first.setButtonClickHandler(new ClickHandler(){
126
            public void onClick(ClickEvent event) {
127
                goToPage(1);
128
            }
129
        });
130
        backward.setButtonTip("上一页");
131
        backward.setButtonClickHandler(new ClickHandler(){
132
            public void onClick(ClickEvent event){
133
                goToPage(pageNum - 1);
134
            }
135
        });
136
        pageText = new TextItem();
137
        pageText.setWidth(40);
138
        pageText.setHeight(20);
139
        pageText.setShowTitle(false);
140
        pageText.setTextAlign(Alignment.RIGHT);
141
        go.setWidth(34);
142
        go.setHeight(20);
143
        go.addClickHandler(new ClickHandler(){
144
            public void onClick(ClickEvent event) {
145
                String page = pageText.getValueAsString();
146
                if(page == null || page.trim().equals("")){
147
                    SC.say("请输入页码");
148
                    return ;
149
                }else {
150
                    try{
151
                        int pageint = Integer.valueOf(page);
152
                        if(pageint<1){
153
                            SC.say("输入的页码不能小于1");
154
                            return;
155
                        }else if(pageint>totalPage){
156
                            SC.say("输入的页码不能大于总页数 "+totalPage);
157
                            return;
158
                        }else {
159
                            goToPage(pageint);
160
                        }
161
                    }catch(NumberFormatException e){
162
                        SC.say("请输入整数");
163
                        return;
164
                    }
165
                }
166
            }});
167
         
168
        forward.setButtonTip("下一页");
169
        forward.setButtonClickHandler(new ClickHandler(){
170
            public void onClick(ClickEvent event){
171
                goToPage(pageNum + 1);
172
            }
173
        });
174
 
175
        last.setButtonTip("末页");
176
        last.setButtonClickHandler(new ClickHandler(){
177
            public void onClick(ClickEvent event){
178
                goToPage(totalPage);
179
            }
180
        });
181
 
182
        totalLabel = new Label();
183
        totalLabel.setWrap(false);
184
        totalLabel.setWidth(50);
185
 
186
        setHeight(20);
187
        setOverflow(Overflow.VISIBLE);
188
        setStyleName("normal");
189
        this.setMargin(5);
190
 
191
        pageForm = new DynamicForm();
192
        pageForm.setNumCols(1);
193
        pageForm.setWidth(45);
194
        pageForm.setItems(pageText);
195
        Label blank1 = new Label();
196
        blank1.setContents(" ");
197
        blank1.setWidth(5);
198
        blank1.setHeight(20);
199
 
200
        Label blank2 = new Label();
201
        blank2.setContents(" ");
202
        blank2.setWidth(5);
203
        blank2.setHeight(20);
204
 
205
        Label blank3 = new Label();
206
        blank3.setContents(" ");
207
        blank3.setWidth(15);
208
        blank3.setHeight(20);
209
 
210
        Label blank4 = new Label();
211
        blank4.setContents(" ");
212
        blank4.setWidth(5);
213
        blank4.setHeight(20);
214
         
215
        Label blank5 = new Label();
216
        blank5.setContents(" ");
217
        blank5.setWidth(5);
218
        blank5.setHeight(20);
219
         
220
        addMember(first);
221
        addMember(blank1);
222
        addMember(backward);
223
        addMember(blank2);
224
        addMember(pageForm);
225
        addMember(go);
226
        addMember(blank5);
227
        addMember(forward);
228
        addMember(blank4);
229
        addMember(last);
230
        addMember(blank3);
231
        addMember(totalLabel);
232
        setAlign(Alignment.RIGHT);
233
        resetPaginationState();
234
    }
235
 
236
    /**
237
     * 转到指定页,获取分页数据并装载,
238
     * 获取数据过程中,加模态遮罩
239
     *
240
     * @param pageNum
241
     */
242
    public void goToPage(int pageNum) {
243
        go.disable();
244
        if (pageNum > totalPage)
245
            pageNum = totalPage;
246
        if (pageNum < 1)
247
            pageNum = 1;
248
        if (pageNum == this.pageNum) {
249
            go.enable();
250
            return;
251
        }
252
        mask.show(maskMessage, true);
253
        //fetch data and reload grid
254
        this.pageNum = pageNum;
255
        int startNum = (pageNum - 1) * pageSize;
256
        dataControl.fetchData(startNum, pageSize);     
257
    }
258
 
259
    /**
260
     * 根据记录总数计算总页数
261
     */
262
    private void countTotalPages()
263
    {
264
        if(totalRowNum < 1){
265
            totalPage = -1;
266
            return;
267
        }
268
        int pages = (int) Math.ceil(((float) totalRowNum) / ((float) pageSize));
269
        // never return zero pages
270
        if (pages == 0)
271
            pages = 1;
272
        this.totalPage=pages;
273
        pageNum = 1;
274
    }
275
     
276
    /**
277
     * 首次查询数据,获取数据总数等信息,并更新分页条状态
278
     * 获取数据过程中,加模态遮罩
279
     */
280
    public void active(){
281
        if(dataControl == null){
282
            SC.say("错误","分页栏未配置ListGridDataControl数据控制器,请检查代码");
283
            return;
284
        }      
285
        //首先禁用工具条
286
        if(pageNum != -1){
287
            resetPaginationState();
288
        }
289
        mask.show(maskMessage, true);
290
        dataControl.getTotal();    
291
    }
292
     
293
    /**
294
     * ListGridDataControl获取总记录数后的回调方法
295
     * @param all
296
     */
297
    public void afterGetTotal(int all){
298
        this.totalRowNum = all;
299
        countTotalPages();
300
        if(totalPage==-1){
301
            //如果没有查询到数据,则清空原来的数据
302
            afterFetchData(new ListGridRecord[0]);
303
        } else {
304
            dataControl.fetchData(0, pageSize);
305
        }      
306
    }
307
     
308
    /**
309
     * ListGridDataControl获取数据后的回调方法
310
     * @param datas
311
     */
312
    public void afterFetchData(ListGridRecord[] datas){
313
        if(datas != null){
314
            dataControl.loadData(datas);
315
        }
316
        updatePageinationState();
317
        mask.hide();
318
    }
319
     
320
    /**
321
     * 清除分页工具条状态,全部置灰,不负责清空grid当前数据
322
     */
323
    private void resetPaginationState(){
324
        pageNum = -1;
325
        totalPage = -1;
326
        totalRowNum = -1;
327
        first.disable();
328
        backward.disable();
329
        last.disable();
330
        forward.disable();
331
        pageText.disable();
332
        go.disable();
333
        totalLabel.setContents("");
334
    }
335
 
336
    /**
337
     * 依据当前页和总页数控制工具条状态
338
     * 规则:
339
     *      当前页为-1,则说明工具条尚未激活或者没有查询到数据,不需要处理;
340
     *      当前页等于1,则首页按钮和上一页按钮置灰,否则,激活;
341
     *      当前页等于总页数,则末页按钮和上一页按钮置灰,否则,激活;
342
     */
343
    private void updatePageinationState() {
344
        if(pageNum==-1||totalPage==-1){
345
            return;
346
        }
347
        totalLabel.setContents("第" + pageNum + "页/共" + totalPage + "页");
348
        pageText.enable();
349
        go.enable();
350
        pageText.setValue(pageNum);
351
        if (pageNum == 1){
352
            first.disable();
353
            backward.disable();
354
        } else {
355
            first.enable();
356
            backward.enable();
357
        }
358
        if (pageNum == totalPage){
359
            last.disable();
360
            forward.disable();
361
        } else {
362
            last.enable();
363
            forward.enable();
364
        }
365
    }
366
}</span>
 
2、一个控制接口类
即GridPaginationBar 类里的ListGridDataControl ,代码如下:
 
01
import com.smartgwt.client.widgets.grid.ListGridRecord;
02
 
03
/**
04
 *
05
 * @author weichao
06
 * listGrid数据获取及装载接口 ,和工具条有紧密的相互调用
07
 * grid加分页工具条时,需要实现此接口并提供给分页工具条
08
 *
09
 *
10
 * 示例:
11
 *
12
 * GridPaginationBar pageBar = new GridPaginationBar(bizGrid,20,bizGrid.getParentElement());
13
 *
14
 * pageBar.setDataControl(new ListGridDataControl(){
15
 *          public void fetchData(int start, int pageSize) {
16
 *              service.getBusiness(sid,start,pageSize,
17
 *                      new AsyncCallback<List<XXXXXVO>>() {
18
 *                          public void onFailure(Throwable caught) {
19
 *                              Window.alert(caught.getMessage());
20
 *                          }
21
 *                          public void onSuccess(List<XXXXXVO> result) {
22
 *                              list = result;
23
 *                              pageBar.afterFetchData(RenderBizData.getNewRecords(result));
24
 *                          }
25
 *                      });
26
 *          }
27
 *          public void getTotal() {   
28
 *              service.getBusinessTotal(sid, new AsyncCallback<Integer>(){
29
 *                  public void onFailure(Throwable arg0) {
30
 *                      Window.alert(arg0.getMessage());
31
 *                  }
32
 *                  public void onSuccess(Integer arg0) {
33
 *                      pageBar.afterGetTotal(arg0);
34
 *                  }} );
35
 *          }
36
 *          public void loadData(ListGridRecord[] records) {
37
 *               bizGrid.setData(records);
38
 *          }});
39
 */
40
public interface ListGridDataControl {
41
 
42
    /**
43
     * 获取分页数据,并回调分页工具栏的afterFetchData()方法,
44
     * 如果需要异步获取数据,请注意回调afterFetchData()的时机,保持数据的同步操作。
45
     *
46
     * @param start
47
     * @param pageSize
48
     */
49
    public void fetchData(int start,int pageSize);
50
     
51
    /**
52
     * 获取数据记录总数,并回调分页工具栏的afterGetTotal()方法
53
     * 如果需要异步获取数据,请注意回调afterGetTotal()的时机,保持数据的同步操作。
54
     *
55
     * @return 总数
56
     */
57
    public void getTotal();
58
     
59
    /**
60
     * 把数据装载入grid
61
     * @param records
62
     */
63
    public void loadData(ListGridRecord[] records);
64
}
 
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的按钮 ,或者你自己写个。

01
import com.smartgwt.client.types.Cursor;
02
import com.smartgwt.client.widgets.Img;
03
import com.smartgwt.client.widgets.events.ClickHandler;
04
import com.smartgwt.client.widgets.layout.HLayout;
05
 
06
/**
07
 * @author weichao
08
 * 有激活和置灰两种状态的图片按钮
09
 */
10
public class TopImgButton extends HLayout{
11
    Img enable = new Img();
12
    Img disable = new Img();
13
    private boolean _enable = true;
14
    public TopImgButton(int buttonWidth,int buttonHeight,String enableImgSrc,String disableImgSrc){
15
        setWidth(buttonWidth);
16
        setHeight(buttonHeight);
17
        enable.setSrc(enableImgSrc);
18
        enable.setWidth(buttonWidth);
19
        enable.setHeight(buttonHeight);
20
        enable.setCursor(Cursor.POINTER);
21
        disable.setSrc(disableImgSrc);
22
        disable.setWidth(buttonWidth);
23
        disable.setHeight(buttonHeight);
24
        addMember(enable);
25
    }
26
     
27
    public void enable(){
28
        if(!_enable){
29
            removeMember(disable);
30
            addMember(enable);
31
            _enable = !_enable;
32
        }
33
    }
34
     
35
    public void disable(){
36
        if(_enable){
37
            removeMember(enable);
38
            addMember(disable);
39
            _enable = !_enable;
40
        }
41
    }
42
     
43
    public void setButtonClickHandler(ClickHandler handler){
44
        enable.addClickHandler(handler);
45
    }
46
     
47
    public void setButtonTip(String tipMessage){
48
        enable.setTooltip(tipMessage);
49
    }
50
}

三、效果
 
注释里写的比较多了,还有示例也帖在类的注释里了,所以就不再解释怎么用了。
经过几番测试,感觉运行比较稳定。希望能对需要smartgwt分页的朋友有所帮助。

你可能感兴趣的:(基于SmartGwt的分页组件)