一步一步学android控件(之六) —— MultiAutoCompleteTextView
今天学习的控件是MultiAutoCompleteTextView 。 提到MultiAutoCompleteTextView 我们就自然而然地想到AutoCompleteTextView ,就想知道他们之间到底有什么区别。在讲他们区别之前呢先来看看下面两张图片:
(图1)AutoCompleteTextView (图2)MultiAutoCompleteTextView
这两张图片中使用的都是同样的Adapter , 然而在图1中输入图2中的内容时却得不到任何内容,为什么?
先从他们的关系上说说, MultiAutoCompleteTextView 继承自AutoCompleteTextView(废话 ... 囧) , 在使用上多了一个Tokenizer —— 在图2中,这个Tokenizer就是符号 ‘ , ’ ,当遇到这个符号时会根据光标的位置计算当前关注的信息。如:如果光标在 d 的位置,则 ‘ , ’之前的字串有效;如果光标在 g 位置,则 ‘ , ’ 后面的字串有效;另外如果光标前后都有符号‘ , ’ , 则在两个 ‘ , ’ 中的内容有效。对于这段解释,下面的代码获取更具说服力:
public static class SelfDedineTokenizer implements Tokenizer { private char mTokenizer = ','; public SelfDedineTokenizer() { } public SelfDedineTokenizer(char token) { mTokenizer = token; } public int findTokenEnd(CharSequence text, int cursor) { int i = cursor; int len = text.length(); while (i < len) { if (text.charAt(i) == mTokenizer) { return i; } else { i++; } } return len; } public int findTokenStart(CharSequence text, int cursor) { int i = cursor; while (i > 0 && text.charAt(i - 1) != mTokenizer) { i--; } while (i < cursor && text.charAt(i) == ' ') { i++; } return i; } public CharSequence terminateToken(CharSequence text) { int i = text.length(); while (i > 0 && text.charAt(i - 1) == ' ') { i--; } if (i > 0 && text.charAt(i - 1) == mTokenizer) { return text; } else { if (text instanceof Spanned) { SpannableString sp = new SpannableString(text + String.valueOf(mTokenizer)); TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0); return sp; } else { return text + String.valueOf(mTokenizer); } } } public void setToken(char token) { mTokenizer = token; } public char getToken() { return mTokenizer; } }
这段代码是自定义的Tokenizer , 默认使用 ‘ , ’ 作为分隔符, 若想用其他的符号替换 ‘ , ’ 使用setToken方法即可。
废话也说了那么多了,现在说说今天要做的事情
1、使用异步调用方法(Executors)加载MultiAutoCompleteTextView 中 Adapter 需要的数据(数据跟AutoCompleteTextView一样,有疑问的可参见 点击一步一步学android控件(之五) —— AutoCompleteTextView)
2、在MultiAutoCompleteTextView中使用自定义的Tokenizer而不是默认的Tokenizer。
下面就来实现这些功能,老规矩先准备资源文件
1、 创建布局文件 multi_auto_complete_textview.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <MultiAutoCompleteTextView android:id="@+id/show_multi_complete" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:layout_marginTop="20dp" /> </LinearLayout>
2、创建activity ——WidgetMultiAutoCompleteActivity.java
package com.xy.zt.selfdefinewieget; import java.io.File; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import android.annotation.SuppressLint; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; import android.widget.ArrayAdapter; import android.widget.MultiAutoCompleteTextView; import android.widget.MultiAutoCompleteTextView.Tokenizer; public class WidgetMultiAutoCompleteActivity extends Activity { public static final String[] DEFAULT_DATAS = new String[] { "China", "chengdu", "xueyu", "ting", "baba", "mama", "meimei" }; public static final int MSG_RECEIVE_TASK_DATA = 1024; private ExecutorService mExecutor; private ArrayAdapter<String> mMultiAdapter; private MultiAutoCompleteTextView mShowMulti; // private CommaTokenizer mComma = new CommaTokenizer(); private Future<List<String>> mListFileTask; private static final Callable<List<String>> LIST_FILES = new Callable<List<String>>() { public List<String> call() throws Exception { File rootDir = Environment.getRootDirectory(); LinkedList<File> queue = new LinkedList<File>(); ArrayList<String> result = new ArrayList<String>(100); queue.offer(rootDir); File tmpFile, tmpDirAllFile[]; while ((tmpFile = queue.poll()) != null) { if (tmpFile.isDirectory()) { tmpDirAllFile = tmpFile.listFiles(); if (tmpDirAllFile != null) { for (File f : tmpDirAllFile) { queue.offer(f); } } } else { result.add(tmpFile.getName()); } } return result; } }; Handler mHandler = new Handler() { @SuppressWarnings("unchecked") @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_RECEIVE_TASK_DATA: List<String> datas; try { datas = (List<String>) msg.obj; } catch (ClassCastException e) { datas = useDefaultData(); } mShowMulti.setEnabled(true); mMultiAdapter = new ArrayAdapter<String>( WidgetMultiAutoCompleteActivity.this, R.layout.auto_complete_item, R.id.auto_item_file_name, datas); mShowMulti.setAdapter(mMultiAdapter); break; } ; } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.multi_auto_complete_textview); init(); mExecutor = Executors.newCachedThreadPool(); mListFileTask = mExecutor.submit(LIST_FILES); mExecutor.submit(new Runnable() { @SuppressLint("HandlerLeak") public void run() { List<String> datas; try { datas = mListFileTask.get(); } catch (InterruptedException e) { datas = useDefaultData(); } catch (ExecutionException e) { datas = useDefaultData(); } Message msg = mHandler.obtainMessage(MSG_RECEIVE_TASK_DATA); msg.obj = datas; mHandler.sendMessage(msg); } }); } void init() { mShowMulti = (MultiAutoCompleteTextView) findViewById(R.id.show_multi_complete); mShowMulti.setEnabled(false); mShowMulti.setTokenizer(mCustomerToken); mShowMulti.setThreshold(1); } private List<String> useDefaultData() { List<String> datas = new ArrayList<String>(); for (String s : DEFAULT_DATAS) { datas.add(s); } return datas; } @Override protected void onDestroy() { super.onDestroy(); mExecutor.shutdown(); } SelfDedineTokenizer mCustomerToken = new SelfDedineTokenizer(';'); public static class SelfDedineTokenizer implements Tokenizer { private char mTokenizer = ','; public SelfDedineTokenizer() { } public SelfDedineTokenizer(char token) { mTokenizer = token; } public int findTokenEnd(CharSequence text, int cursor) { int i = cursor; int len = text.length(); while (i < len) { if (text.charAt(i) == mTokenizer) { return i; } else { i++; } } return len; } public int findTokenStart(CharSequence text, int cursor) { int i = cursor; while (i > 0 && text.charAt(i - 1) != mTokenizer) { i--; } while (i < cursor && text.charAt(i) == ' ') { i++; } return i; } public CharSequence terminateToken(CharSequence text) { int i = text.length(); while (i > 0 && text.charAt(i - 1) == ' ') { i--; } if (i > 0 && text.charAt(i - 1) == mTokenizer) { return text; } else { if (text instanceof Spanned) { SpannableString sp = new SpannableString(text + String.valueOf(mTokenizer)); TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0); return sp; } else { return text + String.valueOf(mTokenizer); } } } public void setToken(char token) { mTokenizer = token; } public char getToken() { return mTokenizer; } } }
今天主要的内容就在这个文件中,先来看看
private static final Callable<List<String>> LIST_FILES = new Callable<List<String>>()
Callable接口类似于Runnable,只是他是有返回结果的Runnable , 在他的call方法中做搜索系统文件的工作。 在onCreat函数中有下面代码:
mExecutor = Executors.newCachedThreadPool(); mListFileTask = mExecutor.submit(LIST_FILES);
这段代码第一句创建了一个线程池,第二句将ListFILES提交到线程池进行处理。接下来的代码:
mExecutor.submit(new Runnable() { @SuppressLint("HandlerLeak") public void run() { List<String> datas; try { datas = mListFileTask.get(); } catch (InterruptedException e) { datas = useDefaultData(); } catch (ExecutionException e) { datas = useDefaultData(); } Message msg = mHandler.obtainMessage(MSG_RECEIVE_TASK_DATA); msg.obj = datas; mHandler.sendMessage(msg); } });
这段代码将一个Runnable提交到线程池执行,mListFileTask.get(); 这句在Callable中的call函数没有执行完之前一直处于阻塞状态(不可以放到主线程中),得到数据后发送消息更新UI。
在init函数中有这么一句:
mShowMulti.setTokenizer(mCustomerToken);
表示使用的是自定义的Tokenizer,他的定义如下
SelfDedineTokenizer mCustomerToken = new SelfDedineTokenizer(';');
这样看到的效果跟图二的就有点不同了哦 ^_^......
3、 完善真个工程,下面滴内容也是不可少滴。也写了几个控件了,看看现在ViewData.java中的内容
package com.xy.zt.selfdefinewieget.data; import java.util.ArrayList; final public class ViewData { public final static ArrayList<ViewData> View_Datas = new ArrayList<ViewData>(); public static final int TEXT_VIEW_ID = 90000; public static final String TEXT_VIEW_NAME = "TextView"; public static final int BUTTON_ID = TEXT_VIEW_ID + 1; public static final String BUTTON_NAME = "Button"; public static final int EDIT_TEXT_ID = BUTTON_ID + 1; public static final String EDIT_TEXT_NAME = "EditText"; public static final int AUTO_COMPLETE_TEXTVIEW_ID = EDIT_TEXT_ID + 1; public static final String AUTO_COMPLETE_TEXTVIEW_NAME = "AutoCompleteTextView"; public static final int MULTI_AUTO_COMPLETE_TEXTVIEW_ID = AUTO_COMPLETE_TEXTVIEW_ID + 1; public static final String MULTI_AUTO_COMPLETE_TEXTVIEW_NAME = "MultiAutoCompleteTextView"; private static final ViewData mTextView = new ViewData(TEXT_VIEW_NAME, TEXT_VIEW_ID); private static final ViewData mButton = new ViewData(BUTTON_NAME, BUTTON_ID); private static final ViewData mEditText = new ViewData(EDIT_TEXT_NAME, EDIT_TEXT_ID); private static final ViewData mAutoCompleteTextView = new ViewData( AUTO_COMPLETE_TEXTVIEW_NAME, AUTO_COMPLETE_TEXTVIEW_ID); private static final ViewData mMultiAutoCompleteTextView = new ViewData( MULTI_AUTO_COMPLETE_TEXTVIEW_NAME, MULTI_AUTO_COMPLETE_TEXTVIEW_ID); public final String mViewName; public final int mViewId; private ViewData(String name, int id) { mViewName = name; mViewId = id; } static { View_Datas.add(mTextView); View_Datas.add(mButton); View_Datas.add(mEditText); View_Datas.add(mAutoCompleteTextView); View_Datas.add(mMultiAutoCompleteTextView); } }
最后在WidgetsAdapter的handleItemClicked函数中加入如下内容:
case ViewData.MULTI_AUTO_COMPLETE_TEXTVIEW_ID: intent.setClass(mContext, WidgetMultiAutoCompleteActivity.class); mContext.startActivity(intent); break;
handleItemClicked 最新内容如下:
private void handleItemClicked(int action) { Intent intent = new Intent(); switch (action) { case ViewData.TEXT_VIEW_ID: intent.setClass(mContext, WidgetTextView.class); mContext.startActivity(intent); break; case ViewData.BUTTON_ID: intent.setClass(mContext, WidgetButtonActivity.class); mContext.startActivity(intent); break; case ViewData.EDIT_TEXT_ID: intent.setClass(mContext, WidgetEditTextActivity.class); mContext.startActivity(intent); break; case ViewData.AUTO_COMPLETE_TEXTVIEW_ID: intent.setClass(mContext, WidgetAutoCompleteActivity.class); mContext.startActivity(intent); break; case ViewData.MULTI_AUTO_COMPLETE_TEXTVIEW_ID: intent.setClass(mContext, WidgetMultiAutoCompleteActivity.class); mContext.startActivity(intent); break; } }
MultiAutoCompleteTextView 就介绍到这里了,下一个控件 Toast 。