问题
I need to convert a lot of stuff in my profession - so I'm building a conversion tool for my phone with some of the conversions I use a lot.
Now, I want to be able to build this properly. So far, here's my code:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Length">
<fx:Script>
<![CDATA[
protected function button1_clickHandler(event:MouseEvent):void
{
var Result:String;
var Finish:Number;
var Start:Number = parseFloat(Input.text);
Finish = Start * convert.selectedItem.data;
Result = String(Finish);
answer.text = Result;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:TextInput id="Input" x="20" y="46"/>
<s:SpinnerListContainer x="140" y="122" width="200" height="200">
<s:SpinnerList id="convert" height="100%" width="100%" labelField="label" selectedIndex="1">
<s:ArrayList>
<fx:Object label="km to mi" data="0.62137119224"></fx:Object>
<fx:Object label="km to yd" data="1093.6132983 "></fx:Object>
<fx:Object label="km to ft" data="3280.839895"></fx:Object>
<fx:Object label="km to in" data="39370.07874"></fx:Object>
<fx:Object label="km to m" data="1000"></fx:Object>
<fx:Object label="km to cm" data="100000"></fx:Object>
<fx:Object label="km to mm" data="1000000"></fx:Object>
</s:ArrayList>
</s:SpinnerList>
</s:SpinnerListContainer>
<s:Label id="answer" x="66" y="533" width="348" text="Answer"/>
<s:Button x="66" y="377" width="338" click="button1_clickHandler(event)" label="Button"/>
</View>
As you can see, I'm going to run into some problems with this:
1) Everything is hard-coded, and if I want to add, remove or change the elements in the array, it's going to be a bit of a pain.
2) I have a view that is essentially the same for my volume and weight conversions. With the same problem.
What I'd like to do, but I'm having some trouble understanding, is getting all that hard-coded stuff in one place and having the same view show it based on my previous view which is just a plain, hard-coded list.
I'm thinking of something like an xml sheet, and adding a category = "length"
or category = "weight"
element to the objects, so that I can show the category from the xml in the List, then when I click "length" it reads the label + data from this list. Is that a good solution? And how exactly do I get the selectedItem
to remember which part of the xml list the view should be populated from?
Would it be better to have several xml files? But that still would mean I have to update a whole bunch of places when I need it.
Basically, I need assistance with:
So - Now the question is two-fold:
1) How to keep a connection to xml/db open across multiple views?
2) How to populate the end-view from information from the db?
Thanks for the advice and help.
回答1:
Have you looked at using resource bundles? Or LSOs?
When persisting Flex data, you have 4 main options.
- Relational Databases (seems like overkill here)
- XML (which you already seem comfortable with)
- Resource Bundles
- Local Shared Objects
The LSO example, linked above (#4), gives a potential solution to your question:
... how exactly do I get the selectedItem to remember which part of the xml list the view should be populated from?
snippet:
public function initApp():void {
mySO = SharedObject.getLocal("mydata");
if (mySO.data.visitDate==null) {
welcomeMessage = "Hello first-timer!"
} else {
welcomeMessage = "Welcome back. You last visited on " +
getVisitDate();
}
}
private function getVisitDate():Date {
return mySO.data.visitDate;
}
private function storeDate():void {
mySO.data.visitDate = new Date();
mySO.flush();
}
private function deleteLSO():void {
// Deletes the SharedObject from the client machine.
// Next time they log in, they will be a 'first-timer'.
mySO.clear();
}
I'd recommend using either a combination of XML and LSOs or resource bundles and LSOs--where your XML/RB stores non-changing, static data and your LSO keeps track of dynamic data (view settings, etc).
回答2:
I just finished an app about a month ago that's uses what I think would be the most "Flex"ible solution. (hehehehe)
(First Answer:) If you're into/familiar with (good) database design, you could design a SQLite db that allows you to add to & modify all the data you're working with.
(If not, I'd recommend either: http://www.amazon.com/The-Art-SQL-Stephane-Faroult/dp/0596008945/ref=sr_1_14?s=books&ie=UTF8&qid=1336262973&sr=1-14
or
http://www.amazon.com/SQL-Demystified-Andrew-Oppel/dp/0072262249/ref=sr_1_1?s=books&ie=UTF8&qid=1336263052&sr=1-1
...this post is going to take longer than I anticipated! hehehhehee ;P =D )
Basically what it'd be is: Tables for categories (e.g. Volume, Length, etc.) and another for specific name/value pairs ("km to mi" = 0.62137119224 [each in separate columns]) with a category id column to.
Then on your home page you have your init() create an DAO (Data Access Object [research if you don't know already]) for the categories table, then fetch the categories into an ArrayCollection and set it as the dataProvider on your category list (on the home view -- or wherever).
(Second Answer:) Have your change handler for the category list grab the selectedItem and pass it as the second param in navigator.pushView(). That will send the VO (Value Object -- another to research if you don't know it) to the new View as the "data" property.
In the "pushed view," use your creationComplete handler to "catch" (use) the data variable, which will contain the category's name and id. Create a new DAO for the values table and then use the data.id value to load all your values with that category id. Then set your new ArrayCollection as the dataProvider of your value list.
Then create another View for choosing values to edit in the same way. Except the final view in that "flow" would be a form with inputs for category, name, & value (with save & cancel buttons) that would get populated with the appropriate data too. (Note: use a category DAO to get the names of the categories so that the category names & ids are available if you change a category.
...Then it's just a matter of implementing insert & update methods on that View and the SQL & methods needed in each DAO.
You can use Lita (http://www.dehats.com/drupal/?q=node/58) build, design, pre-populate your database.
...I may come back with some nice example code/files (if I remember)
I made some examples for those who were reading and hoped I would...
//////////////////////////////////////
//VO (Value Object)
//Category.as
//////////////////////////////////////
package dao
{
[Bindable]//Makes all public properties bindable
public class Category
{
import mx.collections.ArrayCollection;
public var id:int = -1;
public var categoryName:String;
private var categoryDao:CategoryDAO;
public function Category() {}
public function get exists():Boolean {
return this.id > -1;
}
//"Super" convenient methods
//Not really part of Value Objects / Value Object Pattern
//May actually be a bad practice if you have many VO instances,
//you have the potential for a DAO instance in each
//when only one instance could be used.
public function insert():void {
if( !categoryDao ){ categoryDao = new CategoryDAO;}
categoryDao.insert( this );
}
public function update():void {
if( !categoryDao ){ categoryDao = new CategoryDAO;}
categoryDao.update( this );
}
public function deleteRow():void {
if( !categoryDao ){ categoryDao = new CategoryDAO;}
categoryDao.deleteRow( this );
}
}
}
//////////////////////////////////////
//DAO (Data Access Object)
//CatagoryDAO.as
//////////////////////////////////////
package dao
{
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.filesystem.File;
import mx.collections.ArrayCollection;
public class CategoryDAO
{
public static var _sqlConnection:SQLConnection;
public var failed:Boolean;
public var errorMessage:*;
public function CategoryDAO() {
}
public function getAll():ArrayCollection
{
var sql:String = "SELECT * FROM categories"
+ " ORDER BY categoryName ASC";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.execute();
var result:Array = stmt.getResult().data;
if( result ){
var list:ArrayCollection = new ArrayCollection();
for (var i:int=0; i < result.length; i++){
list.addItem( buildVO( result[i] ) );
}
return list;
} else {
return null;
}
}
public function getByCategoryId(id:int):Category
{
var sql:String = "SELECT * FROM categories WHERE id=?";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[0] = id;
stmt.execute();
var result:Array = stmt.getResult().data;
if( result && result.length == 1 ){
return buildVO( result[0] );
} else {
return null;
}
}
public function insert(category:Category):void
{
var sql:String =
"INSERT INTO categories ( categoryName )" +
" VALUES ( :name )";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[":name"] = category.categoryName;
this.execute( stmt );
}
public function update(category:Category):void
{
var sql:String =
"UPDATE categories" +
" SET categoryName = :name" +
" WHERE id = :id";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[":name"] = category.categoryName;
stmt.parameters[":id"] = category.id;
this.execute( stmt );
}
public function deleteRow(category:Category):void {
var sql:String =
"DELETE FROM categories" +
" WHERE id = :id";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[":id"] = category.id;
this.execute( stmt );
}
protected function execute(stmt:SQLStatement):void {
try {
stmt.execute();
} catch(error:Error) {
this.failed = true;
this.errorMessage = error.message;
}
}
protected function buildVO(o:Object):Category
{
var category:Category = new Category();
category.id = o.id;
category.categoryName = o.categoryName;
return category;
}
public function get sqlConnection():SQLConnection
{
if (_sqlConnection) return _sqlConnection;
var file:File =
File.documentsDirectory.resolvePath(DbUtility.DB_FILE_NAME);
var fileExists:Boolean = file.exists;
_sqlConnection = new SQLConnection();
_sqlConnection.open(file);
return _sqlConnection;
}
}
}
//////////////////////////////////////
//CategoryView.mxml
//////////////////////////////////////
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:dao="dao.*"
opaqueBackground="#111111"
title="All Categorys"
creationComplete="init()">
<fx:Script>
<![CDATA[
import dao.DbUtility;
import dao.DropPoint;
import dao.Category;
import dao.CategoryDAO;
protected var dbVerifyUtil:DbUtility
protected function init():void
{
dbVerifyUtil = new DbUtility;
dbVerifyUtil.confirmDb();
if( dbVerifyUtil.dbExists() ){
var categorysDAO:CategoryDAO = new CategoryDAO;
categoryList.dataProvider = categorysDAO.getAll();
}
}
protected function categorySelected():void
{
navigator.pushView( CategoryListView,
categoryList.selectedItem );
}
protected function newCategory():void
{
navigator.pushView( EditCategoryView );
}
protected function viewCategory():void
{
navigator.pushView( CategoryListView,
categoryList.selectedItem );
}
]]>
</fx:Script>
<s:List id="categoryList"
left="10" right="10" top="10" bottom="85"
change="viewCategory()"
dataProvider="{data}"
itemRenderer="irs.CategoryIR">
</s:List>
<s:Button label="Add Category"
left="104" bottom="10" height="43"
click="newCategory()"/>
<s:Label text="Touch a category to view or edit it."
y="326" horizontalCenter="0"/>
</s:View>
//////////////////////////////////////
//CategoryListView.mxml
//////////////////////////////////////
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:dao="dao.*"
creationComplete="init()">
<fx:Script>
<![CDATA[
import dao.Value;//Value VO
import dao.Category;//Category VO
import mx.collections.ArrayCollection;
import spark.events.IndexChangeEvent;
private var category:Category;
protected function init():void
{
var category:Category = data as Category;
listValues.dataProvider =
valueDAO.getByCategoryId(
category.id );
}
protected function editValue():void
{
navigator.pushView( EditValueView,
listValues.selectedItem );
}
protected function editCategory():void
{
navigator.pushView( EditCategoryView, category );
}
]]>
</fx:Script>
<s:viewMenuItems>
<s:ViewMenuItem label="Edit Category"
click="editCategory()"
icon="@Embed('assets/edit.png')"/>
<s:ViewMenuItem label="Add Location"
click="addLocation()"
icon="@Embed('assets/plus.png')"/>
</s:viewMenuItems>
<s:List id="listValues"
left="10" right="10" top="10" bottom="60"
labelFunction="labelValue"
change="editValue()"
itemRenderer="irs.ValueIR">
</s:List>
</s:View>
回答3:
Data retrieve
As I understand your problem, you don't have to keep a connection open.
- Retrieve the data from your database
- Parse and store the retrieved data in a collection ideally an ArrayCollection (you don't want to handle XML based objects, they're fine when manipulating String values, but they instantly become a pain in the .as when you want to perform type-conversion and advanced add and remove operations)
Generic conversion
Then, with binding as in the following sample, all you have to do is always have two conversions when you convert a value from an unit to another :
- conversion into a defined unit (ideally the SI unit, as in my example)
- conversion from the SI unit to the desired unit.
Sample
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="400" minHeight="300">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var unitz:ArrayCollection = new ArrayCollection([
{
label:"Length",
units: new ArrayCollection([
{label: "meter", toSI: 1},
{label: "kilometer", toSI: 1000},
{label: "inch", toSI: 0.0254}
])
},
{
label:"Temperature",
units: new ArrayCollection([
{label: "kelvin", toSI: 1},
{label: "celsius", toSI: 274.15},
{label: "farenheit", toSI: 255.927778}
])
}
]);
private function resetFields():void
{
fromValue.text = "";
toValue.text = "";
}
private function onInputChange():void
{
var fu:Object = fromUnitCB.selectedItem;
var tu:Object = toUnitCB.selectedItem;
toValue.text = (Number(fromValue.text)*fu.toSI/tu.toSI).toString();
}
]]>
</fx:Script>
<s:HGroup width="100%" height="40">
<s:ComboBox
id="categoryCB"
dataProvider="{unitz}"
change="resetFields()"
/>
<s:ComboBox id="fromUnitCB" dataProvider="{categoryCB.selectedItem.units}"/>
<s:ComboBox id="toUnitCB" dataProvider="{categoryCB.selectedItem.units}"/>
</s:HGroup>
<s:HGroup width="100%">
<s:TextInput id="fromValue" change="onInputChange()"/>
<s:Label id="toValue"/>
</s:HGroup>
</s:Application>
来源:https://stackoverflow.com/questions/8406692/how-to-get-a-view-to-read-data