diff options
author | George Mount <mount@google.com> | 2015-03-31 16:26:13 -0700 |
---|---|---|
committer | George Mount <mount@google.com> | 2015-04-02 10:23:37 -0700 |
commit | 4cd742ffeb11661bac18b9901fed99944e09eaf8 (patch) | |
tree | 77327ab49ab9baa0c1b58003a207a5251f11d03e | |
parent | fcbf46e7d0ea662677ad6ef9190d5f3681410cce (diff) | |
download | frameworks_base-4cd742ffeb11661bac18b9901fed99944e09eaf8.zip frameworks_base-4cd742ffeb11661bac18b9901fed99944e09eaf8.tar.gz frameworks_base-4cd742ffeb11661bac18b9901fed99944e09eaf8.tar.bz2 |
Capture views with IDs and no expressions in Binding.
We want to get all Views with IDs in the Binding to save the
developer effort in calling findViewById.
Change-Id: Ib7dd85ae9ecc0fd31b235364c0eadc2303dd1780
5 files changed, 76 insertions, 20 deletions
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java index 8889b94..6a35bd2 100644 --- a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java @@ -49,9 +49,10 @@ import javax.xml.xpath.XPathFactory; */ public class LayoutFileParser { private static final String XPATH_VARIABLE_DEFINITIONS = "//variable"; - private static final String XPATH_BINDING_2_EXPR = "//@*[starts-with(., '@{') and substring(., string-length(.)) = '}']"; - private static final String XPATH_BINDING_ELEMENTS = XPATH_BINDING_2_EXPR + "/.."; + private static final String XPATH_BINDING_ELEMENTS = "//*[@*[starts-with(., '@{') and substring(., string-length(.)) = '}']]"; + private static final String XPATH_ID_ELEMENTS = "//*[@*[local-name()='id']]"; private static final String XPATH_IMPORT_DEFINITIONS = "//import"; + private static final String XPATH_MERGE_TAG = "/merge"; final String LAYOUT_PREFIX = "@layout/"; public ResourceBundle.LayoutFileBundle parseXml(File xml, String pkg, int layoutId) @@ -130,7 +131,7 @@ public class LayoutFileParser { ParserHelper.INSTANCE$.toClassName(layoutName) + "Binding"; includedLayoutName = layoutName; } else { - className = getFullViewClassName(nodeName); + className = getFullViewClassName(parent); } final Node originalTag = attributes.getNamedItem("android:tag"); final String tag; @@ -156,9 +157,28 @@ public class LayoutFileParser { } } + if (!bindingNodes.isEmpty() || !imports.isEmpty() || !variableNodes.isEmpty()) { + if (isMergeLayout(doc, xPath)) { + L.e("<merge> is not allowed with data binding."); + throw new RuntimeException("<merge> is not allowed with data binding."); + } + final List<Node> idNodes = getNakedIds(doc, xPath); + for (Node node : idNodes) { + if (!bindingNodes.contains(node) && !"include".equals(node.getNodeName())) { + final Node id = node.getAttributes().getNamedItem("android:id"); + final String className = getFullViewClassName(node); + bundle.createBindingTarget(id.getNodeValue(), className, true, null, null); + } + } + } + return bundle; } + private boolean isMergeLayout(Document doc, XPath xPath) throws XPathExpressionException { + return !get(doc, xPath, XPATH_MERGE_TAG).isEmpty(); + } + private List<Node> getBindingNodes(Document doc, XPath xPath) throws XPathExpressionException { return get(doc, xPath, XPATH_BINDING_ELEMENTS); } @@ -171,6 +191,10 @@ public class LayoutFileParser { return get(doc, xPath, XPATH_IMPORT_DEFINITIONS); } + private List<Node> getNakedIds(Document doc, XPath xPath) throws XPathExpressionException { + return get(doc, xPath, XPATH_ID_ELEMENTS); + } + private List<Node> get(Document doc, XPath xPath, String pattern) throws XPathExpressionException { final XPathExpression expr = xPath.compile(pattern); @@ -185,7 +209,16 @@ public class LayoutFileParser { return result; } - private String getFullViewClassName(String viewName) { + private String getFullViewClassName(Node viewNode) { + String viewName = viewNode.getNodeName(); + if ("view".equals(viewName)) { + Node classNode = viewNode.getAttributes().getNamedItem("class"); + if (classNode == null) { + L.e("No class attribute for 'view' node"); + } else { + viewName = classNode.getNodeValue(); + } + } if (viewName.indexOf('.') == -1) { if (ObjectUtils.equals(viewName, "View") || ObjectUtils.equals(viewName, "ViewGroup") || ObjectUtils.equals(viewName, "ViewStub")) { diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt index 56d4394..228e759 100644 --- a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt @@ -395,11 +395,11 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { } fun calculateIndices() : Unit { val numTaggedViews = layoutBinder.getBindingTargets(). - filter{it.isUsed() && !it.isBinder()}.count() + filter{it.isUsed() && it.getTag() != null}.count() layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() != null }.forEach { indices.put(it, Integer.parseInt(it.getTag())); } - layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder()}.withIndex().forEach { + layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() == null && it.getId() != null }.withIndex().forEach { indices.put(it.value, it.index + numTaggedViews); } } @@ -416,13 +416,16 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { tab("sIncludes.put(${it.androidId}, ${indices.get(it)});") } } - val hasViewsWithIds = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && !it.supportsTag()} != null + val hasViewsWithIds = layoutBinder.getBindingTargets().firstOrNull{ + it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) + } != null if (!hasViewsWithIds) { tab("sViewsWithIds = null;") } else { tab("sViewsWithIds = new android.util.SparseIntArray();") - layoutBinder.getBindingTargets().filter{ it.isUsed() && !it.supportsTag() }. - forEach { + layoutBinder.getBindingTargets().filter{ + it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) + }.forEach { tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});") } } diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java index 52b7cb8..f86ac08 100644 --- a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java @@ -83,4 +83,9 @@ public class NoIdTest extends BaseDataBinderTest<NoIdTestBinding> { String expectedValue = view.getResources().getString(android.R.string.ok); assertEquals(expectedValue, view.getTag()); } + + @UiThreadTest + public void testIdOnly() { + assertEquals("hello", mBinder.textView.getText().toString()); + } } diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml index 61cc652..32f06f4 100644 --- a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml @@ -14,4 +14,8 @@ android:text="@{name}" android:tag="@string/app_name"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{name}" android:tag="@android:string/ok"/> + <TextView android:id="@+id/textView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="hello"/> </LinearLayout> diff --git a/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java b/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java index d8247c5..4e93469 100644 --- a/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java +++ b/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java @@ -19,7 +19,6 @@ package android.databinding; import android.annotation.TargetApi; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; -import android.util.Log; import android.util.SparseIntArray; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -296,18 +295,11 @@ public abstract class ViewDataBinding { SparseIntArray viewsWithIds) { boolean visitChildren = true; String tag = (String) view.getTag(); - int id; if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) { int tagIndex = parseTagInt(tag); views[tagIndex] = view; - } else if ((id = view.getId()) > 0) { - int index; - if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0) { - views[index] = view; - } else if (includes != null && (index = includes.get(id, -1)) >= 0) { - views[index] = view; - visitChildren = false; - } + } else { + visitChildren = addViewWithId(view, views, includes, viewsWithIds); } if (visitChildren) { mapTaggedChildViews(view, views, includes, viewsWithIds); @@ -330,10 +322,29 @@ public abstract class ViewDataBinding { protected static View[] mapChildViews(View root, int numViews, SparseIntArray includes, SparseIntArray viewsWithIds) { View[] views = new View[numViews]; - mapTaggedChildViews(root, views, includes, viewsWithIds); + boolean visitChildren = addViewWithId(root, views, includes, viewsWithIds); + if (visitChildren) { + mapTaggedChildViews(root, views, includes, viewsWithIds); + } return views; } + private static boolean addViewWithId(View view, View[] views, SparseIntArray includes, + SparseIntArray viewsWithIds) { + final int id = view.getId(); + boolean visitChildren = true; + if (id > 0) { + int index; + if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0) { + views[index] = view; + } else if (includes != null && (index = includes.get(id, -1)) >= 0) { + views[index] = view; + visitChildren = false; + } + } + return visitChildren; + } + /** * Parse the tag without creating a new String object. This is fast and assumes the * tag is in the correct format. |