在前文開源類庫iQuery Android版使用說明和類jQuery selector的控件查詢iQuery開源類庫介紹中,介紹了iQuery的基本用法。css
iQuery是一個開源的自動化測試框架項目,有興趣的朋友能夠在這裏下載:html
https://github.com/vowei/iQuery/downloads java
源碼位置:
https://github.com/vowei/iQuery node
iQuery的一個主要目標就是提供一個跨平臺的控件查詢機制,那就須要考慮以下幾個平臺差別性:android
「>> ExpandableListView」
「>> UIAScrollView」
「:radio」
「:button [name = ‘肯定’]」
「:button [mText = ‘肯定’]」
「:button [:text = ‘肯定’]」
爲了儘量的支持更多的框架,iQuery將上面的性質封裝成兩個接口,所以對新平臺的支持,只要實現這兩個接口就能夠了git
本文介紹擴展僞類、僞屬性和添加對新平臺支持的方法,後續文章會解釋支持其餘編程語言的作法。github
在Java版本中,在iQA.Runtime.jar包裏,能夠經過iQueryParser. registerPseudoClass這個函數註冊一個新的僞類,步驟以下:
編程
iQueryParser. createParser(String iquery, boolean registerPseudo)
parser.registerPseudoClass("text", new IPseudoClass() { public boolean resolve(ITreeNode node) { return filterByNameEndsWith(node, "EditText"); } });
在JavaScript版本中,暫時不支持擴展僞類的作法,後續版本會添加這個功能。框架
在Java版中,經過iQueryParser.registerPseudoAttribute函數註冊一個新的僞屬性,步驟以下:編程語言
parser.registerPseudoAttribute("bottom", new IPseudoAttribute() { public String resolve(ITreeNode node) { return node.getProperty("mBottom").getValue(); } });
在iOS的JavaScript版本中的作法是:
#import "common.js"; #import "antlr3-all-min.js"; #import "iQueryLexer.js"; #import "iQueryParser.js"; #import "error.js";
var iq = new iQuery(selector);
iq.parser.registerPseudoAttrs("bottom", function(uiaobj) { if ( uiaobj != undefined && uiaobj.rect != undefined ) { var rect = uiaobj.rect(); return rect.origin.y + rect.size.height; } });
當前支持Java版本的擴展,擴展方式是:
cc.iqa.iquery.android.SoloTreeNode.java:
package cc.iqa.iquery.android; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import android.view.View; import android.view.ViewGroup; import cc.iqa.iquery.*; public class SoloTreeNode implements ITreeNode { private View _view = null; public SoloTreeNode(View view) { _view = view; } public SoloTreeNode(View view, SoloTreeNode parent) { this(view); _parent = parent; } public View getView() { return _view; } @Override public String getName() { return _view.getClass().toString(); } public boolean containsProperty(String key) { return getMethod(key) != null || getField(key) != null; } public IProperty getProperty(String key) { Method method = getMethod(key); Object value = null; if (method != null) { try { value = method.invoke(_view); } catch (IllegalArgumentException e) { return null; } catch (IllegalAccessException e) { return null; } catch (InvocationTargetException e) { return null; } } else { Field field = getField(key); if ( field != null ) { field.setAccessible(true); try { value = field.get(_view); } catch (IllegalArgumentException e) { return null; } catch (IllegalAccessException e) { return null; } } } return new SoloProperty(key, value.toString()); } private ITreeNode _parent; @Override public ITreeNode getParent() { return _parent; } private List<ITreeNode> _children; @Override public List<ITreeNode> getChildren() { if (_children == null) { _children = new ArrayList<ITreeNode>(); if (_view instanceof ViewGroup) { addChildren(_children, (ViewGroup) _view); } } return _children; } private Method getMethod(String key) { Class<?> cls = _view.getClass(); String getter = String.format("%1$s", key); try { return cls.getMethod(getter); } catch (SecurityException e) { return null; } catch (NoSuchMethodException e) { return null; } } private Field getField(String key) { Class<?> cls = _view.getClass(); Field ret = null; while ( ret == null && cls != null ) { try { ret = cls.getDeclaredField(key); } catch (SecurityException e) { } catch (NoSuchFieldException e) { } if ( ret != null ) break; try { ret = cls.getField(key); } catch (SecurityException e) { } catch (NoSuchFieldException e) { } cls = cls.getSuperclass(); } return ret; } private void addChildren(List<ITreeNode> children, ViewGroup viewGroup) { for (int i = 0; i < viewGroup.getChildCount(); i++) { final View child = viewGroup.getChildAt(i); children.add(new SoloTreeNode(child, this)); } } @Override public String getType() { return _view.getClass().toString(); } @Override public String getText() { return getProperty("mText").getValue(); } }
cc.iqa.iquery.android.SoloProperty.java:
package cc.iqa.iquery.android; import cc.iqa.iquery.*; public class SoloProperty implements IProperty { public SoloProperty(String name, String value) { this.name = name; this.value = value; } private String name; public String getName() { return name; } private String value; public String getValue() { return value; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final IProperty other = (IProperty) obj; if (this.name != other.getName() && (this.name == null || !this.name.equals(other.getName()))) { return false; } return !(this.value != other.getValue() && (this.value == null || !this.value .equals(other.getValue()))); } @Override public int hashCode() { int hash = 5; hash = 61 * hash + (this.name != null ? this.name.hashCode() : 0); hash = 61 * hash + (this.value != null ? this.value.hashCode() : 0); return hash; } }