[컴][웹] GWT 사용하기 - DataGrid 에서 lazy loading 구현하기

DataGrid 에서 lazy loading 구현하기 / DataGrid 에서 ScrollPanel 에 접근하기



DataGrid 에서 ScrollPanel 을 접근하는 방법은 ref. 1 에서

ScrollHandler 에 대한 code 는 ref.2 에서 찾을 수 있다.

ref. 2 의 경우는 모든 data 를 한번에 load 해 놓은 상태에서 조금씩 보여주는 경우이다. 만약 scroll 을 통해 server 에 request 를 해서 load more 를 하려고 하는 것이라면 ref. 1 을 참고하자.


아래에 AsyncDataProvider<T> 와 자동으로 load more 를 하는 scroll 을 이용한 예제이다.


Flow

code 의 흐름은 대략적으로 아래와 같다.
DataProvider.addDataDisplay() >> onRangeChanged >> updateRowData >> newPage + getRowCount() >> setVisibleRange()

addDataDisplay() 사용시 주의점

만약에 DataProvider 를 새롭게 만들어서 사용하려고 한다면, 잊지말고 removeDataDisplay() 를 해주도록 하자.

DataProvider 를 새로 만들어서 사용하는데, 그전의 DataProvider 가 사라지지 않아서 살펴보니 addDataDisplay(display) 를 하게 되면, DataProvider 의 addRangeChangeHandler 부분에서 DataProvider 를 사용하고 있어서 display 가 사라지지 않는 이상 DataProvider 를 붙잡고 있는 것 같다.

public void addDataDisplay(final HasData<T> display) {
    ...

    // Add a handler to the display.
    HandlerRegistration handler = display.addRangeChangeHandler(
        new RangeChangeEvent.Handler() {
          public void onRangeChange(RangeChangeEvent event) {
            AbstractDataProvider.this.onRangeChanged(display);
          }
        });


그래서 아래와 같은 방식으로 coding 을 하는 것이 좋다.

public void init(int requestLimit, int productId, HasData<DomainInfo> ... newDisplays) {
    if(newDisplays == null)
      return;

    if(dataProvider != null && this.displays != null){
      for(HasData<DomainInfo> d : this.displays){
        dataProvider.removeDataDisplay(d);
        d.setRowCount(0);
        d.setVisibleRange(0, requestLimit);
      }
    }

    dataProvider = new DomainAsyncDataProvider(requestLimit);
    for(HasData<DomainInfo> d : newDisplays){
      dataProvider.addDataDisplay(d);
    }
    dataProvider.setProductId(productId);
    this.displays = newDisplays;

}





예제


private DomainAsyncDataProvider dataProvider = new DomainAsyncDataProvider();

// How to use
dataGrid = new DataGridExtended<DomainInfo>(DomainInfo.KEY_PROVIDER);

/**
 *
 * This must be equal to the {@val #incrementSize}.
 *
 * Please refer to the comment "About Increment size."
 * in {@link ProductOverviewActivity#setUpActivity}
 */
final int INCREMENT_SIZE = 10;
dataGrid.setIncrementSize(INCREMENT_SIZE);
dataProvider.setRequestLimit(INCREMENT_SIZE);
dataGrid.setVisibleRange(0, dataGrid.getIncrementSize());
dataGrid.addLoadMoreScrollHandler();
dataProvider.addDataDisplay(display); // onRangeChanged event 를 발생시킨다.


DomainAsyncDataProvider 

setVisibleRange() 가 호출되면, onRangeChanged event 가 발생한다.


// DomainAsyncDataProvider.java
public class DomainAsyncDataProvider extends AsyncDataProvider<DomainDatabase.DomainInfo> {


    private static int DEFAULT_INCREMENT_SIZE = 10;
    private int requestLimit = DEFAULT_INCREMENT_SIZE;
    private int lastRowCount = 0;
    private int lastRangeLength = 0;

    private static int testCount = 0;

    public void setRequestLimit(int size){
        this.requestLimit = size;
    }


    @Override
    protected void onRangeChanged(HasData display) {

        // Get the new range.
        final Range range = display.getVisibleRange();

        /*
        * Query the data asynchronously. If you are using a database, you can
        * make an RPC call here. We'll use a Timer to simulate a delay.
        */

        final int start = range.getStart();
        final int length = range.getLength();   // range length grows by the setVisibleRange in
                                                // {@ref DataGridExtended#addLoadMoreScrollHandler}

        ParamForStatsData param = new ParamForStatsData();
        param.set(NewStatsParamKey.PRODUCT_ID, productId);
        param.set(NewStatsParamKey.START_INDEX, StartIndexOfUpdateRow);
        param.set(NewStatsParamKey.LIMIT, requestLimit);



        new RequestDomains(param, new RequestCallback() {
            @Override
            public void onResponseReceived(Request req, Response res) {
                if (200 == res.getStatusCode()) {

                    final DomainsJavaScriptObject domainsJson = JsonUtils.safeEval(res.getText());
                    List newData = new ArrayList();

                    // the below Math.min() covers the case
                    // that the size of the received has more than {@val requestLimit}
                    int len = Math.min(domainsJson.getSize(), requestLimit);
                    for ( int i = 0; i < len; i++ ){

                        // create data
                        newData.add(
                                new DomainInfo(
                                        domainsJson.getDomainSeq(i), domainsJson.getPathId(i),
                                        domainsJson.getDomain(i), domainsJson.getPath(i),
                                        domainsJson.getStatus(i))
                        );
                    }


                    updateRowData(StartIndexOfUpdateRow, newData);


                }

            }

            @Override
            public void onError(Request request, Throwable exception) {

            }
        }).send();

    }
}

DataGridExtended<T>

load more 를 지원하는 scrollhandler 를 구현했다.


// DataGridExtended<T>
public class DataGridExtended<T> extends DataGrid {


    /**
     * The last scroll position.
     */
    private int lastScrollPos = 0;
    /**
     * The default increment size.
     */
    private static final int DEFAULT_INCREMENT = 20;
    /**
     * The increment size.
     */
    private int incrementSize = DEFAULT_INCREMENT;

    public DataGridExtended(ProvidesKey<T> keyProvider) {
        super(keyProvider);
    }

    public ScrollPanel getScrollPanel() {
        HeaderPanel header = (HeaderPanel) getWidget();
        return (ScrollPanel) header.getContentWidget();
    }

    public void addLoadMoreScrollHandler(){

        final ScrollPanel scrollPanel = getScrollPanel();
        scrollPanel.addScrollHandler(new ScrollHandler() {

            @Override
            public void onScroll(ScrollEvent event) {
                // If scrolling up, ignore the event.
                int oldScrollPos = lastScrollPos;
                lastScrollPos = scrollPanel.getVerticalScrollPosition();
                if (oldScrollPos >= lastScrollPos) {
                    return;
                }

                // Height of grid contents (including outside the viewable area) - height of the scroll panel
                int maxScrollTop = scrollPanel.getWidget().getOffsetHeight() -
                        scrollPanel.getOffsetHeight();

                if (lastScrollPos >= maxScrollTop) {

                    /**
                     * {@link #getRowCount()} is determined by the {@link #updateRowCount()}
                     *
                     * The below statement assumes that
                     * the getRowCount() and getLength() is same in usual cases.
                     *
                     * This can be the stop condition, but the known issue is that this will make
                     * the {@link #onRangeChanged} NOT called any more.
                     *
                     *
                     * Math.min(getRowCount(), ..) is used to make the newPageSize
                     * have same value as the last newPageSize, which is used for not calling #onRangeChange.
                     * If the getRowCount() has same as the last getRowCount(), newPageSize value would be same as
                     * the last one. Thus the setVisibleRange() does not call {@link AsyncDataProvider#onRangeChange}
                     *
                     */
//                    final int newPageSize
//                            = Math.min(DataGridExtended.this.getRowCount(),
//                            DataGridExtended.this.getVisibleRange().getLength())
//                            + incrementSize;
                    final int newPageSize
                            = DataGridExtended.this.getRowCount() + incrementSize;

                    /**
                     * If newPageSize is different from existing page size,
                     * this invokes the function {@link com.google.gwt.view.client.AsyncDataProvider#onRangeChanged()}
                     */
                    DataGridExtended.this.setVisibleRange(0, newPageSize);

                }
            }
        });
    }


    /**
     * Get the number of rows by which the range is increased when the scrollbar
     * reaches the bottom.
     *
     * @return the increment size
     */
    public int getIncrementSize() {
        return incrementSize;
    }

    /**
     * Set the number of rows by which the range is increased when the scrollbar
     * reaches the bottom.
     *
     * @param incrementSize the incremental number of rows
     */
    public void setIncrementSize(int incrementSize) {
        this.incrementSize = incrementSize;
    }

}

References

  1. http://stackoverflow.com/questions/11997130/lazy-load-data-in-a-gwt-datagrid
  2. http://gwt.googleusercontent.com/samples/Showcase/Showcase.html#%21CwCellList

댓글 없음:

댓글 쓰기