[Flex]基于flex4技术从零开发flex博客系统 : 5 数据存储之管理Greeting

第五课:存储数据之管理Greeting

Google App Engine for java自今年4月7日开放申请,至今不足二十日,但关于GAE for java的博文已经出现了不少。继数据存储之后,这一课要研究如何通过客户端管理Greeting,即CURD的实现。

一,列表显示
在sban.flexblog.HelloWorld.java中添加一个获取所有Greeting的接口:

public List<Greeting> getAllGreetings() {
                List<Greeting> result;

                try {
                        result = (List<Greeting>) pm.newQuery(Greeting.class).execute();
                } finally {
                        pm.close();
                }

                return result;
        }

在上例中,pm.newQuery(Greeting.class)负责查询所有已存的Greeting信息。List数据在Flex客户端将被映射成ArrayList。

修改Index.mxml客户端代码,修改之后最终代码如下:

<?xml version="1.0" encoding="utf-8"?>
<FxApplication xmlns="http://ns.adobe.com/mxml/2009" initialize="onInit()">

        <Script>
                <![CDATA[
                        import mx.controls.Alert;
                        import flash.net.URLLoader;
                        import flash.net.URLRequest;
                        import mx.rpc.events.ResultEvent;
                        import mx.rpc.events.FaultEvent;
                        import mx.rpc.AbstractOperation;
                        import mx.rpc.remoting.RemoteObject;
                        import mx.messaging.ChannelSet;
                        import mx.messaging.channels.AMFChannel;
                        import mx.collections.ArrayCollection;
                        import mx.managers.CursorManager;
                        import mx.events.IndexChangedEvent
                        
                        [Bindable]
                        private var _greetingData : ArrayCollection;
                        
                        [Bindable]
                        private var _greeting : Object;
                        
                        private var _remotingObj : RemoteObject = new RemoteObject("GenericDestination");
                        
                        private function onInit() : void
                        {
                                configRemoting();
                                getAllGreetings();
                        }
                        
                        private function configRemoting() : void
                        {
                                _remotingObj.source = "sban.flexblog.HelloWorld";
                                _remotingObj.endpoint = "weborb.wo";
                        }
                        
                        private function greetViaRemoting() : void
                        {
                                var op : AbstractOperation = _remotingObj.getOperation("greet2");
                                var handler : Function = function(event : ResultEvent) : void
                                        {
                                                op.removeEventListener(ResultEvent.RESULT, handler);
                                                getAllGreetings();
                                                Alert.show( event.result.toString() );
                                        };
                                
                                op.addEventListener(ResultEvent.RESULT, handler);
                                op.send(vNameTxt.text,vContentTxt.text);
                        }
                        
                        private function getAllGreetings() : void
                        {
                                var op : AbstractOperation = _remotingObj.getOperation("getAllGreetings");
                                var handler : Function = function(event : ResultEvent) : void
                                        {
                                                op.removeEventListener(ResultEvent.RESULT, handler);
                                        
                                                _greetingData = ArrayCollection(event.result);
                                        };
                                        
                                op.addEventListener(ResultEvent.RESULT, handler);
                                op.send();
                        }
                ]]>

        </Script>
        
        <layout>
                <BasicLayout />
        </layout>
        
        <VGroup width="100%">
                <HGroup>
                        <Label text="user:" />
                        <FxTextInput id="vNameTxt" text="sban" />
                </HGroup>
                <HGroup>
                        <Label text="content:" />
                        <FxTextInput id="vContentTxt" text="greeting content" />
                </HGroup>
                
                <HGroup>
                        <FxButton id="vSendBtn" label="remoting greet"click="greetViaRemoting()" />
                </HGroup>
                
                <FxList id="vList1" dataProvider="{_greetingData}" width="60%"itemRenderer="GreetingItemRenderer">
                        <layout>
                                <VerticalLayout />
                        </layout>
                </FxList>
                
        </VGroup>
        
        <TextBox text="by sban" color="gray" bottom="10" right="10" />
        
</FxApplication>

1,用一个Bindable的、ArrayList类型的_greetingData,用于存储Greeting数据列表。
2,getAllGreetings方法用于从远程接口sban.flexblog.HelloWorld.getAllGreetings中读取数据,其在OnInit中被调用。
3,FxList用于显示列表数据,使用了VerticalLayout布局,数据纵向依次排列。如果是HorizontalLayout布局,但是横向排列。关于布局,详见new language tag Private and layout in flex4
4,FxList的itemRenderer,即GreetingItemRenderer,是一个自定义的ItemRenderer,是一个mxml格式的skin文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<ItemRenderer xmlns="http://ns.adobe.com/mxml/2009">
        
    <states>
                <State name="normal"/>
                <State name="hovered"/>
                <State name="selected"/>
        </states>

        <TextBox text="ID:{data.id},User:{data.user}, 
                Greeting:{data.greetingContent}, 
                Date:{data.date}"
 
                verticalCenter="0" left="3" right="3" top="6" bottom="4" />

</ItemRenderer>

这个文件的逻辑很简单,就是把Greeting的数据信息在一个TextBox中显示出来。有关于在flex4中编写ItemRender的skin组件件详见:use skin as dataContainer’s itemRenderer in flex4 gumbo

运行一下,没有问题,但是FxList中内容不能点选。
fxlist-no-selection.png

这是由于我们没有在GreetingItemRenderer中定义点选的状态引起的,修改GreetingItemRenderer的代码,最终代码如下:

<ItemRenderer xmlns="http://ns.adobe.com/mxml/2009">
        
    <states>
                <State name="normal"/>
                <State name="hovered"/>
                <State name="selected"/>
        </states>
        
        <Rect left="0" right="0" top="0" bottom="0">
                <fill>
                        <SolidColor color="0xffffff" 
                                color.selected="0×666666" color.hovered="0×999999" />

                </fill>
        </Rect>

        <TextBox color="0×000000" color.selected="0xffffff" 
                text="ID:{data.id},User:{data.user}, 
                Greeting:{data.greetingContent}, 
                Date:{data.date}"
 
                verticalCenter="0" left="3" right="3" top="6" bottom="4" />

</ItemRenderer>

此时,FxList便可以点选了。当点选时,背景为深灰色,文字为白色,鼠标rollOver时,背景为浅灰。这个效果是由GreetingItemRenderer中的selected与hovered状态决定的。关于flex4中的新状态(State)详见:new state in flex4。运行效果如下图所示:
fxlist-selection-enabled.png

二,可恶的2030错误
在运行时,我发现会遇到以下Error:

Error: Error #2030: 遇到文件尾。

这个Error是flex客户端抛出的。但是根源却在server端,在类HelloWorld.java的getAllGreetings方法内,我们使用了List做为返回数据类型。在这里List仅是接口,具体数据载体是一个ArrayList。而ArrayList这个鬼类型,有容量与列表长度之分,初始创建时ArrayList有10个容量,当容量不足时,会自动增长2/3。这便造成,在接口的返回结果中,有一些列表元素是null,这是造成“遇到文件尾”的罪魁祸首。真不知这个bug应该归罪于WebORB还是flex。

修改getAllGreetings方法,最终代码如下:

   @SuppressWarnings(value="unchecked")
        public List<Greeting> getAllGreetings() {
                List<Greeting> result;

                try {
                        result = (List<Greeting>) pm.newQuery(Greeting.class).execute();
                        while (result.get(result.size()  1) == null) {
                                result.remove(result.size()  1);
                        }
                } finally {
                        pm.close();
                }

                return result;
        }

@SuppressWarnings用于告诉Eclipse不要再提示unchecked警告,不然总会有一个黄色的小灯泡在哪里挂着。

sban:储如2030、2032等等这类与IO、文件等相关的IOError,在flex客户端接口调试中是最常见,也是爆错信息最简单的一类Error。这类Error多是server端代码引起的。

二,删除与更新
在sban.flexblog.HelloWorld这个类中,添加有关删除一个Greeting,删除所有Greeting,编辑单个Greeting,以及获取单个Greeting的接口方法,该类最终代码如下:

package sban.flexblog;

import java.util.Date;
import java.util.List;
import javax.jdo.PersistenceManager;

import sban.flexblog.managers.PMFactory;

/**
 * @author sban.li
 *
 */

public class HelloWorld {
        public String greet(String name) {
                return "Hi " + name + ", this message comes from remoting. Now:"
                                + new Date().toString();
        }

        private PersistenceManager pm = PMFactory.getInstance()
                        .getPersistenceManager();

        public Boolean greet2(String user, String content) {
                Boolean result = true;
                Greeting greeting = new Greeting(user, content, new Date());

                try {
                        pm.makePersistent(greeting);
                } catch (Exception e) {
                        result = false;
                } finally {
                        pm.close();
                }

                return result;
        }
        
        public Greeting getGreetingById(Long id)
        {
                return (Greeting) pm.getObjectById(Greeting.class, id);
        }
        
        public Boolean editGreeting(Long id, String content)
        {
                Boolean result = true;
                
                try {
                        Greeting greeting = getGreetingById(id);
                        greeting.setGreetingContent(content);
                } catch (Exception e) {
                        result = false;
                } finally {
                        pm.close();
                }
                
                return result;
        }

        public Boolean deleteById(Long id) {
                Boolean result = true;

                try {
                        Greeting greeting = getGreetingById(id);
                        pm.deletePersistent(greeting);
                } catch (Exception e) {
                        result = false;
                } finally {
                        pm.close();
                }

                return result;
        }
        @SuppressWarnings(value="unchecked")
        public Boolean deleteAllGreetings() {
                Boolean result = true;

                try {
                        List<Greeting> greetings = (List<Greeting>) pm.newQuery(
                                        Greeting.class).execute();
                        pm.deletePersistentAll(greetings);
                } catch (Exception e) {
                        result = false;
                } finally {
                        pm.close();
                }

                return result;
        }

        @SuppressWarnings(value="unchecked")
        public List<Greeting> getAllGreetings() {
                List<Greeting> result;

                try {
                        result = (List<Greeting>) pm.newQuery(Greeting.class).execute();
                        while (result.get(result.size()  1) == null) {
                                result.remove(result.size()  1);
                        }
                } finally {
                        pm.close();
                }

                return result;
        }
}

1,pm.deletePersistent用于删除单个对象
2,pm.deletePersistentAll用于删除一个集合
3,pm.getObjectById用于获取单个对象
4,更新Greeting对象时,只需设置该对象属性,在pm.close之前,数据会自动存储。需在sban.flexblog.Greeting中,添加如下setter:

public void setGreetingContent(String v)
        {
                this.greetingContent = v;
        }

修改Index.mxml客户端代码,最终如下:

<?xml version="1.0" encoding="utf-8"?>
<FxApplication xmlns="http://ns.adobe.com/mxml/2009" initialize="onInit()">

        <Script>
                <![CDATA[
                        import mx.controls.Alert;
                        import flash.net.URLLoader;
                        import flash.net.URLRequest;
                        import mx.rpc.events.ResultEvent;
                        import mx.rpc.events.FaultEvent;
                        import mx.rpc.AbstractOperation;
                        import mx.rpc.remoting.RemoteObject;
                        import mx.messaging.ChannelSet;
                        import mx.messaging.channels.AMFChannel;
                        import mx.collections.ArrayCollection;
                        import mx.managers.CursorManager;
                        import mx.events.IndexChangedEvent
                        
                        [Bindable]
                        private var _greetingData : ArrayCollection;
                        
                        [Bindable]
                        private var _greeting : Object;
                        
                        private var _remotingObj : RemoteObject = new RemoteObject("GenericDestination");
                        
                        private function onInit() : void
                        {
                                configRemoting();
                                getAllGreetings();
                        }
                        
                        private function configRemoting() : void
                        {
                                _remotingObj.source = "sban.flexblog.HelloWorld";
                                _remotingObj.endpoint = "weborb.wo";
                        }
                        
                        private function greetViaRemoting() : void
                        {
                                var op : AbstractOperation = _remotingObj.getOperation("greet2");
                                var handler : Function = function(event : ResultEvent) : void
                                        {
                                                op.removeEventListener(ResultEvent.RESULT, handler);
                                                getAllGreetings();
                                                Alert.show( event.result.toString() );
                                        };
                                
                                op.addEventListener(ResultEvent.RESULT, handler);
                                op.send(vNameTxt.text,vContentTxt.text);
                        }
                        
                        private function getAllGreetings() : void
                        {
                                var op : AbstractOperation = _remotingObj.getOperation("getAllGreetings");
                                var handler : Function = function(event : ResultEvent) : void
                                        {
                                                op.removeEventListener(ResultEvent.RESULT, handler);
                                        
                                                _greetingData = ArrayCollection(event.result);
                                        };
                                        
                                op.addEventListener(ResultEvent.RESULT, handler);
                                op.send();
                        }
                        
                        private function deleteAllGreetings() : void
                        {
                                var op : AbstractOperation = _remotingObj.getOperation("deleteAllGreetings");
                                var handler : Function = function(event : ResultEvent) : void
                                        {
                                                op.removeEventListener(ResultEvent.RESULT, handler);
                                                getAllGreetings();
                                                //Alert.show(event.result.toString());
                                        };
                                        
                                op.addEventListener(ResultEvent.RESULT, handler);
                                op.send();
                        }
                        
                        private function deleteItem() : void
                        {
                                if(vList1.selectedItem)
                                {
                                        var itemId : Number = vList1.selectedItem.id;
                                        
                                        var op : AbstractOperation = _remotingObj.getOperation("deleteById");
                                        var handler : Function = function(event : ResultEvent) : void
                                                {
                                                        op.removeEventListener(ResultEvent.RESULT, handler);
                                                        getAllGreetings();
                                                        //Alert.show(event.result.toString());
                                                };
                                                
                                        op.addEventListener(ResultEvent.RESULT, handler);
                                        op.send(itemId);
                                }
                        }
                        
                        private function selectionChangedHandler(event : IndexChangedEvent) : void
                        {
                                var itemId : Number = vList1.selectedItem.id;
                                var op : AbstractOperation = _remotingObj.getOperation("getGreetingById");
                                
                                var handler : Function = function(event : ResultEvent) : void
                                        {
                                                op.removeEventListener(ResultEvent.RESULT, handler);
                                                
                                                _greeting = event.result;
                                        };
                                        
                                op.addEventListener(ResultEvent.RESULT, handler);
                                op.send(itemId);
                        }
                        
                        private function edit() : void
                        {
                                var content : String = vContentEditTxt.text;
                                var op : AbstractOperation = _remotingObj.getOperation("editGreeting");
                                
                                var handler : Function = function(event : ResultEvent) : void
                                        {
                                                op.removeEventListener(ResultEvent.RESULT, handler);
                                                getAllGreetings();
                                        };
                                        
                                op.addEventListener(ResultEvent.RESULT, handler);
                                op.send(_greeting.id, content);
                        }
                ]]>

        </Script>
        
        <layout>
                <BasicLayout />
        </layout>
        
        <VGroup width="100%">
                <HGroup>
                        <Label text="user:" />
                        <FxTextInput id="vNameTxt" text="sban" />
                </HGroup>
                <HGroup>
                        <Label text="content:" />
                        <FxTextInput id="vContentTxt" text="greeting content" />
                </HGroup>
                
                <HGroup>
                        <FxButton id="vSendBtn" label="remoting greet"click="greetViaRemoting()" />
                        <FxButton id="vDelAllBtn" label="deoete all greetings"click="deleteAllGreetings()" />
                        <FxButton id="vdelItemBtn" label="delete selected item"click="deleteItem()" />
                </HGroup>
                
                <FxList id="vList1" selectionChanged="selectionChangedHandler(event)" 
                        dataProvider="{_greetingData}" width="60%"itemRenderer="GreetingItemRenderer">

                        <layout>
                                <VerticalLayout />
                        </layout>
                </FxList>
                
                <Form borderStyle="solid">
                        <FormItem label="ID:">
                                <Label text="{_greeting.id}" />
                        </FormItem>
                        <FormItem label="User:">
                                <Label text="{_greeting.user}" />
                        </FormItem>
                        <FormItem label="Content:">
                                <FxTextInput id="vContentEditTxt"text="{_greeting.greetingContent}" />
                        </FormItem>
                        <ControlBar>
                                <FxButton id="vSubmitBtn" label="submit" click="edit()" />
                        </ControlBar>
                </Form>
                
        </VGroup>
        
        <TextBox text="by sban" color="gray" bottom="10" right="10" />
        
</FxApplication>

为了让逻辑简单,在编辑时,只充许修改Greeting的内容。这块逻辑比较简要,不再一一述说。读者们可自行下载源码运行查看。我把Index.mxml改名为Greeting.mxml,布署到了http://flex-blog.appspot.com/Greeting.html。附运行截图一张:
flex4-lesson4-5-screenshot.png

布署时要删除war/WEB-INF/appengine-generated目录下的文件,否则可能导致上传不成功。

本课最终源码:flex4-lessons4-5.zip
sban 2009/4/26 于北京
本文为sban原创,作者保留所有权利,如需转载请保留作者原文链接
flex选修课系列:基于flex4技术从零开发flex博客系统

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏