/* * 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.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.util.Log; /** * <p>根据过滤模式限制数据的过滤器.</p> * <p>过滤器通常由实现了 {@link android.widget.Filterable} 接口的子类来生成.</p> * * <p>过滤操作是通过调用 {@link #filter(CharSequence)} 或 * {@link #filter(CharSequence, android.widget.Filter.FilterListener)} * 这些异步方法来完成的.调用方法后,过滤请求会递交到请求队列中等待处理. * 任何对以上方法的调用,都将取消之前未执行的过滤请求.</p> * @see android.widget.Filterable * @author translate by henly.zhang * @author translate by cnmahj * @author convert by cnmahj */ public abstract class Filter { private static final String LOG_TAG = "Filter"; private static final String THREAD_NAME = "Filter"; private static final int FILTER_TOKEN = 0xD0D0F00D; private static final int FINISH_TOKEN = 0xDEADBEEF; private Handler mThreadHandler; private Handler mResultHandler; private Delayer mDelayer; private final Object mLock = new Object(); /** * <p>创建一个新的异步过滤器.</p> */ public Filter() { mResultHandler = new ResultsHandler(); } /** * Provide an interface that decides how long to delay the message for a given query. Useful * for heuristics such as posting a delay for the delete key to avoid doing any work while the * user holds down the delete key. * * @param delayer The delayer. * @hide */ public void setDelayer(Delayer delayer) { synchronized (mLock) { mDelayer = delayer; } } /** * <p>启动异步过滤操作.对该方法的调用将取消之前队列中等待处理的过滤请求, * 并递交新的过滤请求等待执行.</p> * * @param constraint 过滤数据的约束条件 * * @see #filter(CharSequence, android.widget.Filter.FilterListener) */ public final void filter(CharSequence constraint) { filter(constraint, null); } /** * <p>启动异步过滤操作.对该方法的调用将取消之前队列中等待处理的过滤请求, * 并递交新的过滤请求等待执行.</p> * <p>完成过滤操作之后,通知监听器.</p> * * @param constraint 过滤数据的约束条件 * @param listener 过滤操作完成后发出的通知的监听器 * * @see #filter(CharSequence) * @see #performFiltering(CharSequence) * @see #publishResults(CharSequence, android.widget.Filter.FilterResults) */ public final void filter(CharSequence constraint, FilterListener listener) { synchronized (mLock) { if (mThreadHandler == null) { HandlerThread thread = new HandlerThread( THREAD_NAME, android.os.Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mThreadHandler = new RequestHandler(thread.getLooper()); } final long delay = (mDelayer == null) ? 0 : mDelayer.getPostingDelay(constraint); Message message = mThreadHandler.obtainMessage(FILTER_TOKEN); RequestArguments args = new RequestArguments(); // make sure we use an immutable copy of the constraint, so that // it doesn't change while the filter operation is in progress args.constraint = constraint != null ? constraint.toString() : null; args.listener = listener; message.obj = args; mThreadHandler.removeMessages(FILTER_TOKEN); mThreadHandler.removeMessages(FINISH_TOKEN); mThreadHandler.sendMessageDelayed(message, delay); } } /** * <p>根据约束条件调用工作线程过滤数据.子类必须实现该方法来执行过滤操作. * 过滤结果以 {@link android.widget.Filter.FilterResults} 形式返回, * 通过 {@link #publishResults(CharSequence, android.widget.Filter.FilterResults)} * 发布到 UI 线程.</p> * * <p><strong>约定:</strong> 当约束条件为 null 时,必须恢复原始数据.</p> * * @param constraint 用于过滤数据的约束条件 * @return 过滤结果 * * @see #filter(CharSequence, android.widget.Filter.FilterListener) * @see #publishResults(CharSequence, android.widget.Filter.FilterResults) * @see android.widget.Filter.FilterResults */ protected abstract FilterResults performFiltering(CharSequence constraint); /** * <p>在 UI 线程中执行,用于向用户接口发布过滤结果.子类必须实现该方法, * 用于发布 {@link #performFiltering} 的处理结果.</p> * @param constraint 用于过滤数据的约束条件 * @param results 过滤操作的结果 * * @see #filter(CharSequence, android.widget.Filter.FilterListener) * @see #performFiltering(CharSequence) * @see android.widget.Filter.FilterResults */ protected abstract void publishResults(CharSequence constraint, FilterResults results); /** * 将已过滤的集合的内容转换为 CharSequence.子类必须实现该方法,以转换处理结果. * <p>默认实现是对于 null 返回空字符串,其他的返回参数的默认字符串.</p> * @param resultValue 要转换为 CharSequence 文本的对象 * @return 代表转换结果的 CharSequence. */ public CharSequence convertResultToString(Object resultValue) { return resultValue == null ? "" : resultValue.toString(); } /** * <p>用于保持过滤操作的结果.结果包含过滤操作的结果及其个数.</p> */ protected static class FilterResults { public FilterResults() { // nothing to see here } /** * <p>包含过滤操作的所有结果.</p> * */ public Object values; /** * <p>包含过滤操作的结果数量.</p> */ public int count; } /** * <p>用于接受过滤操作完成后发出的通知的监听器.</p> */ public static interface FilterListener { /** * <p>过滤操作结束的通知.</p> * * @param count 过滤结果的数量. */ public void onFilterComplete(int count); } /** * <p>Worker thread handler. When a new filtering request is posted from * {@link android.widget.Filter#filter(CharSequence, android.widget.Filter.FilterListener)}, * it is sent to this handler.</p> */ private class RequestHandler extends Handler { public RequestHandler(Looper looper) { super(looper); } /** * <p>Handles filtering requests by calling * {@link Filter#performFiltering} and then sending a message * with the results to the results handler.</p> * * @param msg the filtering request */ public void handleMessage(Message msg) { int what = msg.what; Message message; switch (what) { case FILTER_TOKEN: RequestArguments args = (RequestArguments) msg.obj; try { args.results = performFiltering(args.constraint); } catch (Exception e) { args.results = new FilterResults(); Log.w(LOG_TAG, "An exception occured during performFiltering()!", e); } finally { message = mResultHandler.obtainMessage(what); message.obj = args; message.sendToTarget(); } synchronized (mLock) { if (mThreadHandler != null) { Message finishMessage = mThreadHandler.obtainMessage(FINISH_TOKEN); mThreadHandler.sendMessageDelayed(finishMessage, 3000); } } break; case FINISH_TOKEN: synchronized (mLock) { if (mThreadHandler != null) { mThreadHandler.getLooper().quit(); mThreadHandler = null; } } break; } } } /** * <p>Handles the results of a filtering operation. The results are * handled in the UI thread.</p> */ private class ResultsHandler extends Handler { /** * <p>Messages received from the request handler are processed in the * UI thread. The processing involves calling * {@link Filter#publishResults(CharSequence, * android.widget.Filter.FilterResults)} * to post the results back in the UI and then notifying the listener, * if any.</p> * * @param msg the filtering results */ @Override public void handleMessage(Message msg) { RequestArguments args = (RequestArguments) msg.obj; publishResults(args.constraint, args.results); if (args.listener != null) { int count = args.results != null ? args.results.count : -1; args.listener.onFilterComplete(count); } } } /** * <p>Holds the arguments of a filtering request as well as the results * of the request.</p> */ private static class RequestArguments { /** * <p>The constraint used to filter the data.</p> */ CharSequence constraint; /** * <p>The listener to notify upon completion. Can be null.</p> */ FilterListener listener; /** * <p>The results of the filtering operation.</p> */ FilterResults results; } /** * @hide */ public interface Delayer { /** * @param constraint The constraint passed to {@link Filter#filter(CharSequence)} * @return The delay that should be used for * {@link Handler#sendMessageDelayed(android.os.Message, long)} */ long getPostingDelay(CharSequence constraint); } }