summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget/MultiAutoCompleteTextView.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
commit9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch)
treed88beb88001f2482911e3d28e43833b50e4b4e97 /core/java/android/widget/MultiAutoCompleteTextView.java
parentd83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff)
downloadframeworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/widget/MultiAutoCompleteTextView.java')
-rw-r--r--core/java/android/widget/MultiAutoCompleteTextView.java282
1 files changed, 282 insertions, 0 deletions
diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java
new file mode 100644
index 0000000..05abc26
--- /dev/null
+++ b/core/java/android/widget/MultiAutoCompleteTextView.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spanned;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.method.QwertyKeyListener;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.R;
+
+/**
+ * An editable text view, extending {@link AutoCompleteTextView}, that
+ * can show completion suggestions for the substring of the text where
+ * the user is typing instead of necessarily for the entire thing.
+ * <p>
+ * You must must provide a {@link Tokenizer} to distinguish the
+ * various substrings.
+ *
+ * <p>The following code snippet shows how to create a text view which suggests
+ * various countries names while the user is typing:</p>
+ *
+ * <pre class="prettyprint">
+ * public class CountriesActivity extends Activity {
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * setContentView(R.layout.autocomplete_7);
+ *
+ * ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter&lt;String&gt;(this,
+ * android.R.layout.simple_dropdown_item_1line, COUNTRIES);
+ * MultiAutoCompleteTextView textView = (MultiAutoCompleteTextView) findViewById(R.id.edit);
+ * textView.setAdapter(adapter);
+ * textView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
+ * }
+ *
+ * private static final String[] COUNTRIES = new String[] {
+ * "Belgium", "France", "Italy", "Germany", "Spain"
+ * };
+ * }</pre>
+ */
+
+public class MultiAutoCompleteTextView extends AutoCompleteTextView {
+ private Tokenizer mTokenizer;
+
+ public MultiAutoCompleteTextView(Context context) {
+ this(context, null);
+ }
+
+ public MultiAutoCompleteTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.autoCompleteTextViewStyle);
+ }
+
+ public MultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /* package */ void finishInit() { }
+
+ /**
+ * Sets the Tokenizer that will be used to determine the relevant
+ * range of the text where the user is typing.
+ */
+ public void setTokenizer(Tokenizer t) {
+ mTokenizer = t;
+ }
+
+ /**
+ * Instead of filtering on the entire contents of the edit box,
+ * this subclass method filters on the range from
+ * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd}
+ * if the length of that range meets or exceeds {@link #getThreshold}.
+ */
+ @Override
+ protected void performFiltering(CharSequence text, int keyCode) {
+ if (enoughToFilter()) {
+ int end = getSelectionEnd();
+ int start = mTokenizer.findTokenStart(text, end);
+
+ performFiltering(text, start, end, keyCode);
+ } else {
+ dismissDropDown();
+
+ Filter f = getFilter();
+ if (f != null) {
+ f.filter(null);
+ }
+ }
+ }
+
+ /**
+ * Instead of filtering whenever the total length of the text
+ * exceeds the threshhold, this subclass filters only when the
+ * length of the range from
+ * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd}
+ * meets or exceeds {@link #getThreshold}.
+ */
+ @Override
+ public boolean enoughToFilter() {
+ Editable text = getText();
+
+ int end = getSelectionEnd();
+ if (end < 0 || mTokenizer == null) {
+ return false;
+ }
+
+ int start = mTokenizer.findTokenStart(text, end);
+
+ if (end - start >= getThreshold()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Instead of validating the entire text, this subclass method validates
+ * each token of the text individually. Empty tokens are removed.
+ */
+ @Override
+ public void performValidation() {
+ Validator v = getValidator();
+
+ if (v == null || mTokenizer == null) {
+ return;
+ }
+
+ Editable e = getText();
+ int i = getText().length();
+ while (i > 0) {
+ int start = mTokenizer.findTokenStart(e, i);
+ int end = mTokenizer.findTokenEnd(e, start);
+
+ CharSequence sub = e.subSequence(start, end);
+ if (TextUtils.isEmpty(sub)) {
+ e.replace(start, i, "");
+ } else if (!v.isValid(sub)) {
+ e.replace(start, i,
+ mTokenizer.terminateToken(v.fixText(sub)));
+ }
+
+ i = start;
+ }
+ }
+
+ /**
+ * <p>Starts filtering the content of the drop down list. The filtering
+ * pattern is the specified range of text from the edit box. Subclasses may
+ * override this method to filter with a different pattern, for
+ * instance a smaller substring of <code>text</code>.</p>
+ */
+ protected void performFiltering(CharSequence text, int start, int end,
+ int keyCode) {
+ getFilter().filter(text.subSequence(start, end), this);
+ }
+
+ /**
+ * <p>Performs the text completion by replacing the range from
+ * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd} by the
+ * the result of passing <code>text</code> through
+ * {@link Tokenizer#terminateToken}.
+ * In addition, the replaced region will be marked as an AutoText
+ * substition so that if the user immediately presses DEL, the
+ * completion will be undone.
+ * Subclasses may override this method to do some different
+ * insertion of the content into the edit box.</p>
+ *
+ * @param text the selected suggestion in the drop down list
+ */
+ @Override
+ protected void replaceText(CharSequence text) {
+ int end = getSelectionEnd();
+ int start = mTokenizer.findTokenStart(getText(), end);
+
+ Editable editable = getText();
+ String original = TextUtils.substring(editable, start, end);
+
+ QwertyKeyListener.markAsReplaced(editable, start, end, original);
+ editable.replace(start, end, mTokenizer.terminateToken(text));
+ }
+
+ public static interface Tokenizer {
+ /**
+ * Returns the start of the token that ends at offset
+ * <code>cursor</code> within <code>text</code>.
+ */
+ public int findTokenStart(CharSequence text, int cursor);
+
+ /**
+ * Returns the end of the token (minus trailing punctuation)
+ * that begins at offset <code>cursor</code> within <code>text</code>.
+ */
+ public int findTokenEnd(CharSequence text, int cursor);
+
+ /**
+ * Returns <code>text</code>, modified, if necessary, to ensure that
+ * it ends with a token terminator (for example a space or comma).
+ */
+ public CharSequence terminateToken(CharSequence text);
+ }
+
+ /**
+ * This simple Tokenizer can be used for lists where the items are
+ * separated by a comma and one or more spaces.
+ */
+ public static class CommaTokenizer implements Tokenizer {
+ public int findTokenStart(CharSequence text, int cursor) {
+ int i = cursor;
+
+ while (i > 0 && text.charAt(i - 1) != ',') {
+ i--;
+ }
+ while (i < cursor && text.charAt(i) == ' ') {
+ i++;
+ }
+
+ return i;
+ }
+
+ public int findTokenEnd(CharSequence text, int cursor) {
+ int i = cursor;
+ int len = text.length();
+
+ while (i < len) {
+ if (text.charAt(i) == ',') {
+ return i;
+ } else {
+ i++;
+ }
+ }
+
+ return len;
+ }
+
+ public CharSequence terminateToken(CharSequence text) {
+ int i = text.length();
+
+ while (i > 0 && text.charAt(i - 1) == ' ') {
+ i--;
+ }
+
+ if (i > 0 && text.charAt(i - 1) == ',') {
+ return text;
+ } else {
+ if (text instanceof Spanned) {
+ SpannableString sp = new SpannableString(text + ", ");
+ TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
+ Object.class, sp, 0);
+ return sp;
+ } else {
+ return text + ", ";
+ }
+ }
+ }
+ }
+}