当前位置: 代码迷 >> Android >> 一步一步学android控件(之6) —— MultiAutoCompleteTextView
  详细解决方案

一步一步学android控件(之6) —— MultiAutoCompleteTextView

热度:38   发布时间:2016-04-28 07:29:55.0
一步一步学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 。



  相关解决方案