在本文中我们关心的是BlueBill Mobile类尤其是管理所有Search Species屏幕之后逻辑的控制器;因此本文有助于你了解JavaFX的语言性能而且我们会举出一些实例来阐述要介绍的技巧和典型JavaFX结构的陷阱
笔者想应用程序中嵌入了更新的屏播视频播放要求使用QucikTime
这里的概念是在搜索框中键入查询时英文函数或科学名称函数会对清单过滤此外当这些生效的时候BlueBill Mobile还可以执行自动完成输入例如如果在键入查询的时候你仔细查看视频会发现只输入了ardac来选择Ardea Cinerea;或用于Pied Avocet的pie< space>aBlueBill Mobile 会自动会剩余部分进行补充因为在某些情况下不存在其他选择这是用来改善移动设备性能的重要功能你可以以较少的输入达到相同目的
按照MVC模式就非常有必要在单独的控制器中概括这种模式;此外也很容易对这种模式进行单元测试
首先让我们看一下代表了分类群的模式类
package ittidalwavebluebillmfxtaxonmodel;
import javalangComparable;
public class Taxon extends Comparable
{
publicread protected var displayName : String;
publicread protected var scientificName : String;
publicread protected var id : String;
override function compareTo (other : Object)
{
return pareTo((other as Taxon)displayName);
}
override function toString()
{
return {displayName} ({scientificName}) ({id})
}
}
public function displayNameGetter (taxon : Taxon): String
{
return taxondisplayName;
}
public function scientificNameGetter (taxon : Taxon): String
{
return taxonscientificName;
}
public def namePropertyGetters = [displayNameGetter scientificNameGetter];
类托架外面定义的函数和变量相当于Java静态分析
这里我们省略了一些不相关的实际项目基本上该模式暴露了三个属性其中有意思的两个分别是displayName和scientificName我们也可以定义两个函数来处理这两个问题我们会把这些函数放在namePropertyGetters序列中
package ittidalwavebntroller;
import ittidalwavebluebillmfxtaxonmodelTaxon;
public class TaxonSearchController
{
public var selectedTaxon = bind if (selectedTaxonIndex < ) then null else filteredTaxons[selectedTaxonIndex];
public var selectedTaxonIndex : Integer = ;
public var taxons: Taxon[];
public var filter = on replace
{
filteredTaxons = taxons[taxon | matches(taxon filter)];
update();
}
publicread var autoCompleted = ;
public var filteredTaxons: Taxon[];
protected function matches (taxon : Taxon string: String) : Boolean
{
if (string == )
{
return true;
}
for (propertyGetter in TaxonnamePropertyGetters)
{
if (propertyGetter(taxon)toLowerCase()startsWith(filtertoLowerCase()))
{
return true;
}
}
return false;
}
protected function update(): Void
{
def autoCompletedTry = commonLeadingSubstring(filteredTaxons findMatchingPropertyGetter());
//
// Sometimes it cant find a better autocompletion than the current filter since it searches the displayName
// and the scientificName at the same time In this case we just ignore the new value
//
if (autoCompletedTrylength() > filterlength())
{
autoCompleted = autoCompletedTry;
}
selectedTaxonIndex = if (sizeof filteredTaxons == ) then else ;
println(selectedTaxonIndex: {selectedTaxonIndex})
}
protected function findMatchingPropertyGetter(): function (:Taxon): String
{
for (taxon in filteredTaxons)
{
for (propertyGetter in TaxonnamePropertyGetters)
{
if (propertyGetter(taxon)toLowerCase()startsWith(filtertoLowerCase()))
{
return propertyGetter;
}
}
}
return null;
}
// some stuff later
}
这个类揭示了以下的属性
·taxons你需要用完整的鸟类列表来填充
·filter: 字符串包括需要输入到搜索栏中的文本
·filteredTaxons: 种类由filter字符串过滤
·autoCompleted: 控制器猜测的自动完成输入字符串
·selectedTaxon: 如果filter向下细分种类它就会分配到这个变量
·selectedTaxonIndex: 如果无法获取时selectedTaxon的索引
最新的四种属性由客户代码来绑定这样做可以获取更改提示
Filter获取了一个触发事件也就是变量值更改时所执行的代码触发器用JavaFX运算符 ︳执行了过滤操作我们可以将触发事件的第一行当作分配到taxons序列中的filteredTaxons来读取在这一序列中matches()函数返回值为true第二行的代码调用了接下来要介绍的update()函数
出于某些原因这种方法并不一定奏效因为filteredTaxons通常会被整体扫描有多种方法可用来加速选择过程但是本文不会在这一方法真正应用到手机前前作出过早的优化在笔记本上它可以加快个项目的速度
Matches()函数在所有属性上执行了一次迭代以获取函数并检查看相关属性是否以过滤值启动
创建获得属性值函数的序列的一大好处是我们可以通过定义新的函数轻松添加新的匹配标准例如其他语言中的本地化名称控制器可能会使用在搜索过程中使用这些名称而我们则不需要再做多余修改
Update()函数运算出了自动完成输入提示它会提取filteredTaxons序列以及用于当前选择的获取属性函数还会调用刚刚在字符串属性的序列中找到了通用子字符串的commonLeadingSubstring()它不是每次都会作出很好的自动完成输入猜想因此有时建议甚至比当前过滤器还短而这种情况我们大可忽略不计请不要忽视指定临时变量的重要性由于自动完成输入可能被绑定因此我们不想为其指定一个会迅速失效的值
要明白这一点的重要性这不仅仅是避免无用更新还能避免程序被破坏在实际程序中自动完成输入更改时TextBox会更新因此过滤器也会随之更新已经输入了cal后再输入一个i那么TextBox暂时会显示cali然后自动输入完成的猜测失败它会返回一个calTextBox中的字符串会变为cal这时候你要坚持自己的想法!绑定确实很强大但是它也同时具有负面效应
最后一步操作中代码会检查看我们是否获取单独的已选定鸟类
或许你对于自动完成输入失败的原因仍然感到很困惑毕竟我们正在逐步缩小项目列表因此如果你已经输入了cali那么所有经过过滤的种类会以cali开头对吗?如果你过滤的是一套单一名称情况就应该是这样;但是我们是同时对两套名称执行搜索那么就会产生矛盾看看下例由cali过滤器选取的名称组(英语科学的)(Calandra Lark Melanocorypha calandra) (Dunlin Calidris alpina) (California Quail Callipepla californica)
另一个有意思的地方是findMatchPropertyGetter()它必须猜测当前过滤器是否是以英语或科学名称运行而且它还会返回相关的属性获取函数基本上控制器已经获取了matches()函数中的这一信息但是我们会将其移走可能会有人思考让matches()函数返回一个以上的布林值但是这是不可能的因为它是由运算符 ︳过滤序列的时候使用的该运算符需要一个布林值或许我们可以为稍后调用信息的操作指定一个成员变量不过此时的代码应该会更具可读性
为了对文章进一步作补充说明这里给大家列出了最后两个忽略的函数
protected function commonLeadingSubstring (taxons: Taxon[] propertyGetter: function (:Taxon): String): String
{
if (sizeof taxons == )
{
return ;
}
if (sizeof taxons == )
{
return propertyGetter(taxons[]);
}
var common = propertyGetter(taxons[]);
for (other in taxons[])
{
common = commonLeadingSubstring(common propertyGetter(other));
if (common == )
{
break; // dont waste time in further iterations its for sure the final result
}
}
return root;
}
function commonLeadingSubstring (string : String string : String): String
{
return if (stringlength() > stringlength())
{
commonLeadingSubstring(string string);
}
else if (string == )
{
;
}
else if (stringstartsWith(string))
{
string;
}
else
{
commonLeadingSubstring(stringsubstring( stringlength() ) string);
}
}
这里的逻辑很简单通常主要的字符串搜索被分解成了临近字符串对;而对于单一对的搜索则有递归执行
这里显示了视图类是如何绑定到控制器的
package ittidalwavebluebillmfxtaxonview;
public class TaxonSearchScreen
{
public var taxons : Taxon[];
var filter = ;
publicread def controller = TaxonSearchController
{
taxons: bind taxons
filter: bind filter
}
def autoCompleted = bind controllerautoCompleted on replace
{
if (autoCompleted != )
{
filter = autoCompleted;
}
}
def list = ListBox
{
items: bind controllerfilteredTaxons
};
def searchBox = TextBox
{
text: bind filter with inverse
};
}
你必须用所有可得的种类加载taxon;ListBox会随着过滤的种类自动更新
TextBox与过滤器是双重指令型绑定
之所以需要双重绑定是因为向搜索栏中输入时
一个指令用于给控制器发出新的选择命令
另一个则是自动完成输入时的更新