LWUIT的List运用系列(七) List的终极运用(下篇)

LWUIT的List运用系列(六) List的终极使用(上篇)中我介绍了LWUIT_MakeOver项目,虽然有部分代码看不懂,但这并不阻碍我去模仿它的形式去应用List。这一篇我按照作者的思想写了一个简单的Demo,希望那些跟我一样不理解源代码的同胞们能够加深一下理解。
如果你现在还没有这个项目的源代码,可以到这里下载(不要资源分的)。

既然是模仿别人的程序,在自己动手之前,我们首先要明白源代码的基本含义,至少要能保证大半代码我们能够理解,我先对源代码做一个简单的分析和说明。
我把显示详细信息的showDetail()方法和显示地图的showMap()方法去掉了。

    //创建Form,主要是把Menu前的默认数字给去掉
    private Form createForm(String title) {
        Form f = new Form(title);
        f.getTitleComponent().setAlignment(Component.LEFT);
        f.setMenuCellRenderer(new DefaultListCellRenderer(false));
        return f;
    }
 //主界面,就是表单界面,我们不用理会那些字段的含义
    private void showMainForm() {
        Form mainForm = createForm("Local Search");
        mainForm.setTransitionInAnimator(Transition3D.createCube(500, false));
        mainForm.setTransitionOutAnimator(Transition3D.createCube(500, true));
        mainForm.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
        mainForm.addComponent(new Label("search for:"));
        final TextField searchFor = new TextField("coffee", 50);
        mainForm.addComponent(searchFor);
        mainForm.addComponent(new Label("location:"));
        final TextField location = new TextField("95054", 50);
        mainForm.addComponent(location);
        mainForm.addComponent(new Label("street:"));
        final TextField street = new TextField(50);
        mainForm.addComponent(street);
        mainForm.addComponent(new Label("sort results by:"));
        final ComboBox sortResults = new ComboBox(new String[] {"Distance", "Title", "Rating", "Relevance"});
        mainForm.addComponent(sortResults);
        mainForm.addCommand(exitCommand);
        mainForm.addCommand(defaultThemeCommand);
        mainForm.addCommand(javaThemeCommand);
        mainForm.addCommand(new Command("Search") {
            public void actionPerformed(ActionEvent ev) {
                showSearchResultForm(searchFor.getText(), location.getText(), street.getText(), (String) sortResults.getSelectedItem());
            }
        });
        mainForm.show();
    }
    //报告异常信息的对话框,网络连接异常或者中断时会报此异常
    private void exception(Exception ex) {
        ex.printStackTrace();
        Dialog.show("Error", "Error connecting to search service - Turning on DEMO MODE", "OK", null);
        demoMode = true;
        showMainForm();
    }

//显示搜索结果页面,传的那些值都来自于MainForm的文本框,这个页面时用来显示List的
    //测试时有虽然有396条数据,结果却显示的非常流畅
    private void showSearchResultForm(String searchFor, String location, String street, String sortOrder) {
        final Form resultForm = createForm("result list");
        resultForm.setScrollable(false);
        resultForm.setLayout(new BorderLayout());
        InfiniteProgressIndicator tempIndicator = null;
        try {
            tempIndicator = new InfiniteProgressIndicator(Image.createImage("/wait-circle.png"));
        } catch (IOException ex) {
            tempIndicator = null;
            ex.printStackTrace();
        }
        final InfiniteProgressIndicator indicator = tempIndicator;
        final List resultList = new List(new LocalResultModel(searchFor, location, sortOrder, street)) {
            public boolean animate() {
                boolean val = super.animate();

                // return true of animate only if there is data loading, this saves battery and CPU
                if(indicator.animate()) {
                    int index = getSelectedIndex();
                    index = Math.max(0, index - 4);
                    ListModel model = getModel();
                    int dest = Math.min(index + 4, model.getSize());
                    for(int iter = index ; iter < dest ; iter++) {
                        if(model.getItemAt(index) == LOADING_MARKER) {
                            return true;
                        }
                    }
                }
                return val;
            }
        };
        Links pro = new Links();
        pro.title = "prototype";
        pro.tel = "9999999999";
        pro.distance = "9999999";
        pro.address = "Long address string";
        pro.rating = "5";
        resultList.setRenderingPrototype(pro);
        resultList.setFixedSelection(List.FIXED_NONE_CYCLIC);
        resultList.getStyle().setBorder(null);

        //这一部分属于关键代码,还是比较容易懂的,作为Controller控制界面的显示
        //我一直理解ListCellaRenderer为Controller,不知道理解有没有错。
        resultList.setListCellRenderer(new DefaultListCellRenderer(false) {
            private Label focus;
            private Container selected;
            private Label firstLine;
            private Label secondLine;
            private boolean loading;

            //代码块,会在构造方法执行时一起执行
            {
                selected = new Container(new BoxLayout(BoxLayout.Y_AXIS));
                firstLine = new Label("First Line");
                secondLine = new Label("Second Line");
                int iconWidth = 20;
                firstLine.getStyle().setMargin(LEFT, iconWidth);
                secondLine.getStyle().setMargin(LEFT, iconWidth);
                selected.addComponent(firstLine);
                selected.addComponent(secondLine);
            }

            public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {
                if(value == null || value == LOADING_MARKER) {
                    loading = true;
                    if(isSelected) {
                        firstLine.setText("Loading...");
                        secondLine.setText("Loading...");
                        return selected;
                    }
                    return indicator;
                }
                loading = false;
                //如果为选中状态就显示Label为白色
                if(isSelected) {
                    int listSelectionColor = list.getStyle().getFgSelectionColor();
                    firstLine.getStyle().setFgColor(0xffffff);
                    secondLine.getStyle().setFgColor(0xffffff);
                    firstLine.getStyle().setBgTransparency(0);
                    secondLine.getStyle().setBgTransparency(0);
                    Links l = (Links)value;
                    firstLine.setText(l.address + " " + l.tel);
                    secondLine.setText(l.distance + " miles " + ("".equals(l.rating) ? "" : ", " + l.rating + "*"));
                    return selected;
                }
                //如果为未选中状态,恢复Label的默认显示
                super.getListCellRendererComponent(list, ((Links)value).title, index, isSelected);
                return this;
            }

            public void paint(Graphics g) {
                if(loading) {
                    indicator.setX(getX());
                    indicator.setY(getY());
                    indicator.setWidth(getWidth());
                    indicator.setHeight(getHeight());
                    indicator.paint(g);
                } else {
                    super.paint(g);
                }
            }

            //在List的选中项前添加一个箭头图标
            public Component getListFocusComponent(List list) {
                if(focus == null) {
                    try {
                        focus = new Label(Image.createImage("/svgSelectionMarker.png"));
                        focus.getStyle().setBgTransparency(0);
                    } catch (IOException ex1) {
                        ex1.printStackTrace();
                    }
                }
                return focus;
            }
        });
        resultForm.addComponent(BorderLayout.CENTER, resultList);
        resultForm.addCommand(new Command("Map") {
            public void actionPerformed(ActionEvent ev) {
                showMap(resultForm, resultList.getSelectedItem());
            }
        });
        resultForm.addCommand(new Command("Details") {
            public void actionPerformed(ActionEvent ev) {
                showDetails(resultForm, resultList.getSelectedItem());
            }
        });
        resultForm.addCommand(new Command("Back") {
            public void actionPerformed(ActionEvent ev) {
                showMainForm();
            }
        });
        resultForm.addCommand(exitCommand);
        resultForm.show();
    }

 /**
     * A list model that lazily fetches a result over the web if its unavailable
     * 这个类就非常关键了,它是整个List动态加载数据的核心
     */
    class LocalResultModel implements ListModel {
        private Vector cache;
        private Arg[] args;
        private boolean fetching;
        private Vector fetchQueue = new Vector();
        private Vector dataListeners = new Vector();

        private Vector selectionListeners = new Vector();

        private int selectedIndex = 0;
        private boolean firstTime = true;

        public LocalResultModel(String searchFor, String location, String sortOrder, String street) {
            cache = new Vector();
            cache.setSize(1);
            args = new Arg[]{
                new Arg("output", "json"),
                new Arg("appid", APPID),
                new Arg("query", searchFor),
                new Arg("location", location),
                new Arg("sort", sortOrder.toLowerCase()),
                null,
                null
            };
            final String str = street;
            if (!"".equals(str)) {
                args[6] = new Arg("street", str);
            }
        }

        /**
         * 这里就是它动态加载的强大之处了,startOffset相当于一个索引值,
         * 396条数据并不是1次性的加载,手机屏幕就那么大,满屏也只能显示几条数据,
         * 作者就让它只加载10条数据,如果用户想浏览后面的数据,它会根据这个startOffset值
         * 请求服务器,返回10条数据。比如第一次请求startOffset = 1,那么第二次就是11,
         * 每次只请求10条数据。
         * @param startOffset
         */
        private void fetch(final int startOffset) {
            int count = Math.min(cache.size(), startOffset + 9);
            for(int iter = startOffset - 1 ; iter < count ; iter++) {
                if(cache.elementAt(iter) == null) {
                    cache.setElementAt(LOADING_MARKER, iter);
                }
            }
            if(!fetching) {
                fetching = true;
                new Thread() {
                    public void run() {
                        if(firstTime) {
                            firstTime = false;
                            try {
                                // yield a bit CPU the first time around since the model
                                // call might occur before the display is refreshed
                                Thread.sleep(400);
                            } catch (InterruptedException ex) {
                                ex.printStackTrace();
                            }
                        }
                        fetchThread(startOffset);
                        while(fetchQueue.size() > 0) {
                            int i = ((Integer)fetchQueue.elementAt(0)).intValue();
                            fetchQueue.removeElementAt(0);
                            fetchThread(i);
                        }
                        fetching = false;
                    }
                }.start();
            } else {
                fetchQueue.addElement(new Integer(startOffset));
            }
        }

        //这个方法就是根据startOffset进行请求,然后返回相应的数据
        private void fetchThread(int startOffset) {
            try {
                Response response;
                args[5] = new Arg("start", Integer.toString(startOffset));
                if (!demoMode) {
                    response = Request.get(LOCAL_BASE, args, null, null);
                } else {
                    response = Request.get(Request.DEMO_URL, args, null, null);
                }
                final Exception ex = response.getException();
                if (ex != null || response.getCode() != HttpConnection.HTTP_OK) {
                    Dialog.show("Error", "Error connecting to search service - Turning on DEMO MODE", "OK", null);
                    demoMode = true;
                    showMainForm();
                    return;
                }

                Result result = response.getResult();
                //String mapAllLink = result.getAsString("ResultSet.ResultSetMapUrl");
                int totalResultsAvailable = result.getAsInteger("ResultSet.totalResultsAvailable");
                final int resultCount = result.getSizeOfArray("ResultSet.Result");

                // this is the first time... set the size of the vector to match the results!
                if(startOffset == 1) {
                    cache.setSize(totalResultsAvailable);
                }

                for(int i = 0 ; i < resultCount ; i++) {
                    String title = result.getAsString("ResultSet.Result["+i+"].Title");

                    Links link = new Links();
                    link.title = title;
                    link.address = result.getAsString("ResultSet.Result["+i+"].Address");
                    link.map = result.getAsString("ResultSet.Result["+i+"].MapUrl");
                    link.listing = result.getAsString("ResultSet.Result["+i+"].ClickUrl");
                    link.business = result.getAsString("ResultSet.Result["+i+"].BusinessClickUrl");
                    link.tel = result.getAsString("ResultSet.Result["+i+"].Phone");
                    link.latitude = result.getAsString("ResultSet.Result["+i+"].Latitude");
                    link.longitude = result.getAsString("ResultSet.Result["+i+"].Longitude");
                    link.rating = result.getAsString("ResultSet.Result["+i+"].Rating.AverageRating");
                    link.distance = result.getAsString("ResultSet.Result["+i+"].Distance");
                    cache.setElementAt(link, startOffset + i - 1);
                    fireDataChangedEvent(DataChangedListener.CHANGED, startOffset + i - 1);
                }
            } catch (Exception ex) {
                exception(ex);
            }
        }

        public Object getItemAt(int index) {
            Object val = cache.elementAt(index);
            if(val == null) {
                fetch(index + 1);
                return LOADING_MARKER;
            }
            return val;
        }

        public int getSize() {
            return cache.size();
        }

        public void setSelectedIndex(int index) {
            int oldIndex = selectedIndex;
            this.selectedIndex = index;
            fireSelectionEvent(oldIndex, selectedIndex);
        }

        public void addDataChangedListener(DataChangedListener l) {
            dataListeners.addElement(l);
        }

        public void removeDataChangedListener(DataChangedListener l) {
            dataListeners.removeElement(l);
        }

        private void fireDataChangedEvent(final int status, final int index){
            if(!Display.getInstance().isEdt()) {
                Display.getInstance().callSeriallyAndWait(new Runnable() {
                    public void run() {
                        fireDataChangedEvent(status, index);
                    }
                });
                return;
            }
            // we query size with every iteration and avoid an Enumeration since a data
            // changed event can remove a listener instance thus break the enum...
            for(int iter = 0 ; iter < dataListeners.size() ; iter++) {
                DataChangedListener l = (DataChangedListener)dataListeners.elementAt(iter);
                l.dataChanged(status, index);
            }
        }

        public void addSelectionListener(SelectionListener l) {
            selectionListeners.addElement(l);
        }

        public void removeSelectionListener(SelectionListener l) {
            selectionListeners.removeElement(l);
        }

        private void fireSelectionEvent(int oldIndex, int newIndex){
            Enumeration listenersEnum = selectionListeners.elements();
            while(listenersEnum.hasMoreElements()){
                SelectionListener l = (SelectionListener)listenersEnum.nextElement();
                l.selectionChanged(oldIndex, newIndex);
            }
        }

        public void addItem(Object item) {
        }

        public void removeItem(int index) {
        }

        public int getSelectedIndex() {
            return selectedIndex;
        }
    }

注意一点:private void fetch(final int startOffset)方法在我实际的测试中,第一次请求startOffset = 1,第二次请求startOffset = 2,至今我还不明白为什么第一次请求的时候那个线程没有运行,从第三次请求开始startoffSet = 12,以后的请求都很正常,都是返回10条数据。

可能我对以上代码解释的不够清楚,但现在又了一个大概的了解,我对实现这种List做了一个简要的需求:

1、手机在首次获取数据或者刷新数据时,将从服务器发起请求,如果有很多条数据,并不是一次性返回给手机端供手机接收。
2、在手机端用List显示数据时需要按时间降序来显示,所以服务器端返回的数据也是按时间降序的形式返回xml或者json。
3、请求的方式按照这种方式来进行:第一次请求时返回10条数据,如果用户浏览完这10条数据,想继续浏览更多的数据,这再次发送请求,依次类推,每次请求只返回10条数据。
4、服务器端对数据要做分页处理,根据请求的起始索引和请求的数据条数(最后一次请求可能没有10条数据),返回相应的数据。进行web请求时,至少要提供startOffset和resultCount这两个参数。
5、List在显示时,不能用默认的List的MVC写法,其中Model部分和Controller部分需要自己进行构造。这样做的作用:一是能够节约内存并加快数据加载速度,二是能够比较灵活的构造List中的View部分。(目前最后一点我还不是很确信,但是感觉上比我用原有的List快一些,原有的List的Model部分是不用像这样继承几口然后,接口里面有一堆方法要实现,如果这个确实能够加快速度,多写点代码也不为过,毕竟大量的数据显示性能非常重要)。

下面是我删减后的代码,有了上面的基础,理解起来应该更轻松了!

/**
 *
 * @author 水货程序员
 */
public class MembersForm extends Form {

    static final Object LOADING_MARKER = new Object();
    //InfiniteProgressIndicator这个类在LWUIT_MakeOver中有,是用来显示动画的。
    //我在http://blog.csdn.net/pjw100/archive/2009/12/14/5006882.aspx解释了这个类
    InfiniteProgressIndicator indicator = null;
    InfiniteProgressIndicator tempIndicator = null;

    //构造方法
    public MembersForm() {
        setScrollable(false);
        setLayout(new BorderLayout());
        try {
            tempIndicator = new InfiniteProgressIndicator(Image.createImage("/wait-circle.png"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        indicator = tempIndicator;
        showSearchResultForm();
    }

    private void showSearchResultForm() {
        final List resultList = new List(new LocalResultModel()) {

            public boolean animate() {
                boolean val = super.animate();
                // return true of animate only if there is data loading, this saves battery and CPU
                if (indicator.animate()) {
                    int index = getSelectedIndex();
                    index = Math.max(0, index - 4);
                    ListModel model = getModel();
                    int dest = Math.min(index + 4, model.getSize());
                    for (int iter = index; iter < dest; iter++) {
                        if (model.getItemAt(iter) == LOADING_MARKER) {
                            return true;
                        }
                    }
                }
                return val;
            }
        };

        Person pro = new Person();

        pro.photo = null;
        pro.name = "Sunny";
        pro.sex = "male";
        pro.address = "Long address string";
        resultList.setRenderingPrototype(pro);
        resultList.setFixedSelection(List.FIXED_NONE_CYCLIC);
        resultList.getStyle().setBorder(null);

        resultList.setListCellRenderer(new DefaultListCellRenderer(false) {

            private Label focus;
            private Container selected;
            private Container selectedInfo;
            private Label selectedPhoto;
            private Label selectedName;
            private Label selectedSex;
            private Label selectedAddress;
            private boolean loading;


            {
                selected = new Container(new BoxLayout(BoxLayout.X_AXIS));
                selectedInfo = new Container(new BoxLayout(BoxLayout.Y_AXIS));

                selectedPhoto = new Label();
                selectedName = new Label();
                selectedSex = new Label();
                selectedAddress = new Label();

                int iconWidth = 20;
                selectedPhoto.getStyle().setMargin(LEFT, iconWidth);

                selectedInfo.addComponent(selectedName);
                //selectedInfo.addComponent(selectedSex);
                selectedInfo.addComponent(selectedAddress);
                selected.addComponent(selectedPhoto);
                selected.addComponent(selectedInfo);


            }

            public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {
                if (value == null || value == LOADING_MARKER) {
                    loading = true;
                    if (isSelected) {
                        //unselectedPhoto.setText("Loading...");
                        return selected;
                    }
                    return indicator;
                }
                loading = false;

                if (isSelected) {
                    int listSelectionColor = list.getStyle().getFgSelectionColor();
                    selectedName.getStyle().setFgColor(listSelectionColor);
                    selectedSex.getStyle().setFgColor(listSelectionColor);
                    selectedAddress.getStyle().setFgColor(listSelectionColor);
                    selectedName.getStyle().setBgTransparency(0);
                    selectedSex.getStyle().setBgTransparency(0);
                    selectedAddress.getStyle().setBgTransparency(0);

                    Person person = (Person) value;
                    selectedPhoto.setIcon(person.photo);
                    selectedName.setText(person.name);
                    selectedSex.setText(person.sex);
                    selectedAddress.setText(person.address);

                    return selected;
                }

                super.getListCellRendererComponent(list, ((Person) value).name, index, isSelected);
                return this;
            }

            public void paint(Graphics g) {
                if (loading) {
                    indicator.setX(getX());
                    indicator.setY(getY());
                    indicator.setWidth(getWidth());
                    indicator.setHeight(getHeight());
                    indicator.paint(g);
                } else {
                    super.paint(g);
                }
            }

            public Component getListFocusComponent(List list) {
                if (focus == null) {
                    try {
                        focus = new Label(Image.createImage("/svgSelectionMarker.png"));
                        focus.getStyle().setBgTransparency(0);
                    } catch (IOException ex1) {
                        ex1.printStackTrace();
                    }
                }
                return focus;
            }
        });

        this.addComponent(BorderLayout.CENTER, resultList);
    }

    private void exception(Exception ex) {
        ex.printStackTrace();
        Dialog.show("Error", "Error connecting to search service - Turning on DEMO MODE", "OK", null);
    }

    class LocalResultModel implements ListModel {

        private Vector cache;
        //private Arg[] args;
        private boolean fetching;
        private Vector fetchQueue = new Vector();
        private Vector dataListeners = new Vector();
        private Vector selectionListeners = new Vector();
        private int selectedIndex = 0;
        private boolean firstTime = true;

        public LocalResultModel() {
            cache = new Vector();
            cache.setSize(1);
        }

        private void fetch(final int startOffset) {
            int count = Math.min(cache.size(), startOffset + 9);
            for (int iter = startOffset - 1; iter < count; iter++) {
                if (cache.elementAt(iter) == null) {
                    cache.setElementAt(LOADING_MARKER, iter);
                }
            }
            if (!fetching) {
                fetching = true;
                new Thread() {

                    public void run() {
                        if (firstTime) {
                            firstTime = false;
                            try {
                                // yield a bit CPU the first time around since the model
                                // call might occur before the display is refreshed
                                Thread.sleep(400);
                            } catch (InterruptedException ex) {
                                ex.printStackTrace();
                            }
                        }
                        fetchThread(startOffset);
                        while (fetchQueue.size() > 0) {
                            int i = ((Integer) fetchQueue.elementAt(0)).intValue();
                            fetchQueue.removeElementAt(0);
                            fetchThread(i);
                        }
                        fetching = false;
                    }
                }.start();
            } else {
                fetchQueue.addElement(new Integer(startOffset));
            }
        }

        private void fetchThread(final int startOffset) {
            System.out.println("startOffset:" + startOffset);
            try {
                //下面这两个变量都是进行模拟使用的,totalResultsAvailable代表记录总数
                int totalResultsAvailable = 26;
                int resultCount = 10;
                //cache集合用来盛装数据,第一次请求时设置cache的大小
                if (startOffset == 1) {
                    cache.setSize(totalResultsAvailable);
                }

                //并不是每次请求都能够返回10条数据,比如最后一次请求可能只有5条数据,resultCount就不等于10了。
                if (totalResultsAvailable - startOffset < 10) {
                    resultCount = totalResultsAvailable - startOffset + 1;
                }

                Image pic = null;
                try {
                    pic = Image.createImage("/smallphoto.jpg");
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                //模拟从服务器端请求的数据,返回一个对象数组,然后遍历数组,把数据添加到cache中,缓存起来。
                Person[] personArr = getPersons(startOffset, resultCount);

                for (int i = 0; i < resultCount; i++) {
                    cache.setElementAt(personArr[i], startOffset + i - 1);
                    fireDataChangedEvent(DataChangedListener.CHANGED, startOffset + i - 1);
                }
            } catch (Exception ex) {
                exception(ex);
            }
        }

        //由于自己的Demo没有服务端,我在这里做了模拟数据,startOffset为索引值,count为每次请求的记录条数
        private Person[] getPersons(int startOffset, int count) {
            Image pic = null;
            try {
                pic = Image.createImage("/smallphoto.jpg");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            Person[] personArr = new Person[26];
            for (int i = 0; i < 26; i++) {
                char  c= (char)(65+i);
                String sname = String.valueOf(c);
                personArr[i] = new Person();
                personArr[i].name = sname;
                personArr[i].sex = "";
                personArr[i].address = "深圳";
                personArr[i].photo = pic;
            }
            Person[] datas = new Person[count];
            for(int i = 0;i
  
  
   
   new Person();
                datas[i] = personArr[startOffset + i -1];
            }
            
   
   return datas;
        }

        
   
   public Object getItemAt(
   
   int index) {
            Object val = cache.elementAt(index);
            
   
   if (val == 
   
   null) {
                fetch(index + 1);
                
   
   return LOADING_MARKER;
            }
            
   
   return val;
        }

        
   
   public 
   
   int getSize() {
            
   
   return cache.size();
        }

        
   
   public 
   
   void setSelectedIndex(
   
   int index) {
            
   
   int oldIndex = selectedIndex;
            
   
   this.selectedIndex = index;
            fireSelectionEvent(oldIndex, selectedIndex);
        }

        
   
   public 
   
   void addDataChangedListener(DataChangedListener l) {
            dataListeners.addElement(l);
        }

        
   
   public 
   
   void removeDataChangedListener(DataChangedListener l) {
            dataListeners.removeElement(l);
        }

        
   
   private 
   
   void fireDataChangedEvent(
   
   final 
   
   int status, 
   
   final 
   
   int index) {
            
   
   if (!Display.getInstance().isEdt()) {
                Display.getInstance().callSeriallyAndWait(
   
   new Runnable() {

                    
   
   public 
   
   void run() {
                        fireDataChangedEvent(status, index);
                    }
                });
                
   
   return;
            }
            
   
   // we query size with every iteration and avoid an Enumeration since a data
            
   
   // changed event can remove a listener instance thus break the enum...
            
   
   for (
   
   int iter = 0; iter < dataListeners.size(); iter++) {
                DataChangedListener l = (DataChangedListener) dataListeners.elementAt(iter);
                l.dataChanged(status, index);
            }
        }

        
   
   public 
   
   void addSelectionListener(SelectionListener l) {
            selectionListeners.addElement(l);
        }

        
   
   public 
   
   void removeSelectionListener(SelectionListener l) {
            selectionListeners.removeElement(l);
        }

        
   
   private 
   
   void fireSelectionEvent(
   
   int oldIndex, 
   
   int newIndex) {
            Enumeration listenersEnum = selectionListeners.elements();
            
   
   while (listenersEnum.hasMoreElements()) {
                SelectionListener l = (SelectionListener) listenersEnum.nextElement();
                l.selectionChanged(oldIndex, newIndex);
            }
        }

        
   
   public 
   
   void addItem(Object item) {
        }

        
   
   public 
   
   void removeItem(
   
   int index) {
        }

        
   
   public 
   
   int getSelectedIndex() {
            
   
   return selectedIndex;
        }
    }

    
   
   //实体类
    
   
   private 
   
   static 
   
   class Person {

        Image photo;
        String name;
        String sex;
        String address;
    }
}
  
  

附上效果截图:

Technorati 标签:


第一张:加载Form
第二张:首次加载时的效果
第三张:滑动滚动条时,动态加载的效果,加载时间1秒都不到,很流畅!

0 1 2

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值