ICU-20138 Adding a tech-preview API, constrainFieldAndValue, and updating matchhesField.
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
index 0380b0e..60a5467 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
@@ -601,7 +601,7 @@
                 continue;
             }
             // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
-            if (cfpos.matchesField(NumberFormat.Field.INTEGER)
+            if (cfpos.matchesField(NumberFormat.Field.INTEGER, null)
                     && i > zero
                     // don't return the same field twice in a row:
                     && i - zero > cfpos.getLimit()
@@ -614,7 +614,7 @@
             }
             // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
             if (numericField != null
-                    && cfpos.matchesField(numericField)
+                    && cfpos.matchesField(numericField, null)
                     && i > zero
                     // don't return the same field twice in a row:
                     && (i - zero > cfpos.getLimit() || cfpos.getField() != numericField)
@@ -634,7 +634,7 @@
                 continue;
             }
             // Case 3: check for field starting at this position
-            if (cfpos.matchesField(_field)) {
+            if (cfpos.matchesField(_field, null)) {
                 fieldStart = i - zero;
                 currField = _field;
             }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java b/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java
index 20d3641..a4aa0eb 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java
@@ -3,6 +3,7 @@
 package com.ibm.icu.text;
 
 import java.text.Format.Field;
+import java.util.Objects;
 
 /**
  * Represents a span of a string containing a given field.
@@ -51,7 +52,17 @@
          *
          * FormattedValue implementations should not change the field when this constraint is active.
          */
-        FIELD
+        FIELD,
+
+        /**
+         * Represents that the field value is constrained.
+         *
+         * This is the value of fConstraint
+         * after {@link #constrainField} is called.
+         *
+         * FormattedValue implementations should not change the field or value with this constraint.
+         */
+        VALUE
     };
 
     private ConstraintType fConstraint;
@@ -97,7 +108,7 @@
      * Sets a constraint on the field.
      *
      * When this instance of ConstrainedFieldPosition is passed to {@link FormattedValue#nextPosition}, positions are
-     * skipped unless they have the given category and field.
+     * skipped unless they have the given field.
      *
      * Any previously set constraints are cleared.
      *
@@ -126,6 +137,7 @@
         fConstraint = ConstraintType.FIELD;
         fClassConstraint = Object.class;
         fField = field;
+        fValue = null;
     }
 
     /**
@@ -158,6 +170,36 @@
         fConstraint = ConstraintType.CLASS;
         fClassConstraint = classConstraint;
         fField = null;
+        fValue = null;
+    }
+
+    /**
+     * Sets a constraint on field and field value.
+     *
+     * When this instance of ConstrainedFieldPosition is passed to {@link FormattedValue#nextPosition}, positions are
+     * skipped unless both the field and the field value are equal.
+     *
+     * Any previously set constraints are cleared.
+     *
+     * For example, to find the span a date interval corresponding to the first date:
+     *
+     * <pre>
+     * ConstrainedFieldPosition cfpos;
+     * cfpos.constrainFieldAndValue(DateIntervalFormat.SpanField.DATE_INTERVAL_SPAN, 0);
+     * while (fmtval.nextPosition(cfpos)) {
+     *   // handle the span of the first date in the date interval
+     * }
+     * </pre>
+     *
+     * @param field The field to fix when iterating.
+     * @param fieldValue The field value to fix when iterating.
+     * @internal ICU 64 Technical Preview
+     */
+    public void constrainFieldAndValue(Field field, Object fieldValue) {
+        fConstraint = ConstraintType.VALUE;
+        fClassConstraint = Object.class;
+        fField = field;
+        fValue = fieldValue;
     }
 
     /**
@@ -253,7 +295,7 @@
      * @param field
      *            The new field.
      * @param value
-     *            The new field value.
+     *            The new field value. Should be null if there is no value.
      * @param start
      *            The new inclusive start index.
      * @param limit
@@ -263,7 +305,7 @@
      */
     public void setState(Field field, Object value, int start, int limit) {
         // Check matchesField only as an assertion (debug build)
-        assert matchesField(field);
+        assert matchesField(field, value);
 
         fField = field;
         fValue = value;
@@ -272,17 +314,18 @@
     }
 
     /**
-     * Determines whether a given field should be included given the
+     * Determines whether a given field and value should be included given the
      * constraints.
      *
      * Intended to be used by FormattedValue implementations.
      *
      * @param field The field to test.
+     * @param fieldValue The field value to test. Should be null if there is no value.
      * @return Whether the field should be included given the constraints.
      * @draft ICU 64
      * @provisional This API might change or be removed in a future release.
      */
-    public boolean matchesField(Field field) {
+    public boolean matchesField(Field field, Object fieldValue) {
         if (field == null) {
             throw new IllegalArgumentException("field must not be null");
         }
@@ -293,6 +336,9 @@
             return fClassConstraint.isAssignableFrom(field.getClass());
         case FIELD:
             return fField == field;
+        case VALUE:
+            // Note: Objects.equals is Android API level 19 and Java 1.7
+            return fField == field && Objects.equals(fValue, fieldValue);
         default:
             throw new AssertionError();
         }
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java
index dda4bf4..7aea483 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java
@@ -31,7 +31,7 @@
         assertAllPartsEqual(
             "basic",
             cfpos,
-            7,
+            15,
             null,
             null,
             0,
@@ -47,18 +47,29 @@
         assertAllPartsEqual(
             "setters 1",
             cfpos,
-            2,
+            10,
             NumberFormat.Field.COMPACT,
             null,
             0,
             0,
             0L);
 
+        cfpos.constrainFieldAndValue(NumberFormat.Field.COMPACT, 42);
+        assertAllPartsEqual(
+            "setters 1.2",
+            cfpos,
+            8,
+            NumberFormat.Field.COMPACT,
+            42,
+            0,
+            0,
+            0L);
+
         cfpos.constrainClass(NumberFormat.Field.class);
         assertAllPartsEqual(
             "setters 1.5",
             cfpos,
-            3,
+            11,
             null,
             null,
             0,
@@ -69,7 +80,7 @@
         assertAllPartsEqual(
             "setters 2",
             cfpos,
-            3,
+            11,
             null,
             null,
             0,
@@ -80,7 +91,7 @@
         assertAllPartsEqual(
             "setters 3",
             cfpos,
-            3,
+            11,
             NumberFormat.Field.COMPACT,
             BigDecimal.ONE,
             5,
@@ -91,7 +102,7 @@
         assertAllPartsEqual(
             "setters 4",
             cfpos,
-            7,
+            15,
             null,
             null,
             0,
@@ -103,7 +114,7 @@
     public void testIllegalArgumentException() {
         ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition();
         try {
-            cfpos.matchesField(null);
+            cfpos.matchesField(null, null);
             fail("Expected an IllegalArgumentException");
         } catch (IllegalArgumentException e) {
             // pass
@@ -126,11 +137,13 @@
         assertEquals(messagePrefix + ": context", context, cfpos.getInt64IterationContext());
 
         assertEquals(messagePrefix + ": integer field",
-            ((matching & 1) != 0), cfpos.matchesField(NumberFormat.Field.INTEGER));
+            ((matching & 1) != 0), cfpos.matchesField(NumberFormat.Field.INTEGER, null));
         assertEquals(messagePrefix + ": compact field",
-            ((matching & 2) != 0), cfpos.matchesField(NumberFormat.Field.COMPACT));
+            ((matching & 2) != 0), cfpos.matchesField(NumberFormat.Field.COMPACT, null));
         assertEquals(messagePrefix + ": date field",
-            ((matching & 4) != 0), cfpos.matchesField(DateFormat.Field.AM_PM));
+            ((matching & 4) != 0), cfpos.matchesField(DateFormat.Field.AM_PM, null));
+        assertEquals(messagePrefix + ": compact field with value",
+            ((matching & 8) != 0), cfpos.matchesField(NumberFormat.Field.COMPACT, 42));
     }
 
     public static void checkFormattedValue(String message, FormattedValue fv, String expectedString,