SearchView的层级结构:search_view.xml <?xml version="1.0" encoding="utf-8"?> <!-- /* * Copyright (C) 2010 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. */ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/search_bar" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <!-- --> <TextView android:id="@+id/search_badge" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:layout_marginBottom="2dip" android:drawablePadding="0dip" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="?android:attr/textColorPrimary" android:visibility="gone" /> <ImageView android:id="@+id/search_button" style="?android:attr/actionButtonStyle" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:src="?android:attr/searchViewSearchIcon" android:contentDescription="@string/searchview_description_search" /> <LinearLayout android:id="@+id/search_edit_frame" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" android:layout_marginTop="4dip" android:layout_marginBottom="4dip" android:layout_marginLeft="8dip" android:layout_marginRight="8dip" android:orientation="horizontal"> <ImageView android:id="@+id/search_mag_icon" android:layout_width="@dimen/dropdownitem_icon_width" android:layout_height="wrap_content" android:scaleType="centerInside" android:layout_marginLeft="@dimen/dropdownitem_text_padding_left" android:layout_gravity="center_vertical" android:src="?android:attr/searchViewSearchIcon" android:visibility="gone" /> <!-- Inner layout contains the app icon, button(s) and EditText --> <LinearLayout android:id="@+id/search_plate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" android:orientation="horizontal" android:background="?android:attr/searchViewTextField"> <view class="android.widget.SearchView$SearchAutoComplete" android:id="@+id/search_src_text" android:layout_height="36dip" android:layout_width="0dp" android:layout_weight="1" android:minWidth="@dimen/search_view_text_min_width" android:layout_gravity="bottom" android:paddingLeft="@dimen/dropdownitem_text_padding_left" android:paddingRight="@dimen/dropdownitem_text_padding_right" android:singleLine="true" android:ellipsize="end" android:background="@null" android:inputType="text|textAutoComplete|textNoSuggestions" android:imeOptions="actionSearch" android:dropDownHeight="wrap_content" android:dropDownAnchor="@id/search_edit_frame" android:dropDownVerticalOffset="0dip" android:dropDownHorizontalOffset="0dip" android:contentDescription="@string/searchview_description_query" /> <ImageView android:id="@+id/search_close_btn" android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingLeft="8dip" android:paddingRight="8dip" android:layout_gravity="center_vertical" android:background="?android:attr/selectableItemBackground" android:src="?android:attr/searchViewCloseIcon" android:focusable="true" android:contentDescription="@string/searchview_description_clear" /> </LinearLayout> <LinearLayout android:id="@+id/submit_area" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="?android:attr/searchViewTextFieldRight"> <ImageView android:id="@+id/search_go_btn" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:paddingLeft="16dip" android:paddingRight="16dip" android:background="?android:attr/selectableItemBackground" android:src="?android:attr/searchViewGoIcon" android:visibility="gone" android:focusable="true" android:contentDescription="@string/searchview_description_submit" /> <ImageView android:id="@+id/search_voice_btn" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:paddingLeft="16dip" android:paddingRight="16dip" android:src="?android:attr/searchViewVoiceIcon" android:background="?android:attr/selectableItemBackground" android:visibility="gone" android:focusable="true" android:contentDescription="@string/searchview_description_voice" /> </LinearLayout> </LinearLayout> </LinearLayout> 3. 实例框架: private void traversSearchView(View view, int index) { if (view instanceof SearchView) { SearchView searchView = (SearchView)view; for (int i = 0; i < searchView.getChildCount(); ++i) { traverseSearchView(searchView.getChildAt(i), i); } } else if (view instanceof LinearLayout) { if (/*id value from search_view.xml*/ == view.getId()) { //TODO } LinearLayout linearlayout = (LinearLayout)view; for (int i = 0; i < linearlayout.getChildAt(i), i) { traverseSearchView(linearlayout.getChildAt(i), i); } } else if (view instanceof EditText) { if (/*id value from search_view.xml*/ == view.getId()) { //TODO } //TODO } else if (view instanceof ImageView) { if (/*id value from search_view.xml*/ == view.getId()) { //TODO } //TODO } //other 'else if' clause }返回
以上就是searchView的源码,其实searchView是一个linerlayout,通过遍历里面的子view来进行相关处理。下面我们用searchView+listView来实现查找联系人。
main.xml
android:iconifiedByDefault表示搜索图标是否在输入框内。true效果更加
android:imeOptions设置IME options,即输入法的回车键的功能,可以是搜索、下一个、发送、完成等等。这里actionSearch表示搜索
android:inputType输入框文本类型
android:queryHint输入框默认文本
java代码:
myAdapter
import java.util.ArrayList; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.TextView; public class MyAdapter extends BaseAdapter implements Filterable { private Context context; private List<String> data, copyData; public MyAdapter(Context context, List<String> data) { super(); this.context = context; this.data = data; copyData = data; } @Override public int getCount() { return data.size(); } @Override public Object getItem(int postion) { return data.get(postion); } @Override public long getItemId(int postion) { return postion; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(context).inflate( android.R.layout.simple_list_item_1, null); } TextView tv = (TextView) convertView.findViewById(android.R.id.text1); tv.setText(data.get(position)); return convertView; } private Filter filter; /*单利模式*/ @Override public Filter getFilter() { if (filter == null) { filter = new MyFilter(); } return filter; } class MyFilter extends Filter { /*调用一个工作线程过滤数据。子类必须实现该方法来执行过滤操作。过滤结果以Filter.FilterResults的形式返回*/ @Override protected FilterResults performFiltering(CharSequence constraint) { List<String>filterData=new ArrayList<String>(); if(constraint!=null&&constraint.toString().trim().length()>0){//输入的内容不为空 String key=constraint.toString().trim().toLowerCase();//把输入的转为大写 for (String i:copyData) {//遍历数据 if(i.toLowerCase().indexOf(key)!=-1){//取出的数据包含输入的那个字母 filterData.add(i); } } }else{ filterData=copyData; } FilterResults filterResults=new FilterResults();//过滤集 filterResults.values=filterData; filterResults.count=filterData.size(); return filterResults; } /*通过调用UI线程在用户界面发布过滤结果。来显示performFiltering(CharSequence)的过滤结果。*/ @Override protected void publishResults(CharSequence constraint, FilterResults results) { data=(List<String>) results.values; if(data!=null){//重绘当前可见区域 notifyDataSetChanged();
}else{//重绘控件(还原到初始状态) notifyDataSetInvalidated(); } } } } 搞那么久开发了,其实对notifyDataSetChanged()和notifyDataSetInvalidated()的区别还不是很了解,以前只知道两个方法都是刷新adapter的方法,现在我想说一下:<span style="color:#ff0000;">notifyDataSetChanged</span>方法通过一个外部的方法控制,如果适配器的内容改变时需要强制调用getView来刷新每个Item的内容,调用onChanged事件。每当发现数据集有改变的情况,或者读取到数据的新状态时,就会调用此方法。而<span style="color:#ff0000;">notifyDataSetInvalidated()</span><span style="color:#000000;">调用onInvalidated事件。每当发现数据集监控有改变的情况,比如该数据集不再有效,就会调用此方法。</span>
SearchViewActivity.java
import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.provider.ContactsContract; import android.provider.ContactsContract.RawContacts; import android.view.inputmethod.InputMethodManager; import android.widget.ListView; import android.widget.SearchView; import android.widget.SearchView.OnCloseListener; import android.widget.SearchView.OnQueryTextListener; public class SearchViewActivity extends Activity { private SearchView searchview; private ListView listview; private MyAdapter mAdapter; private List<String>data; private Cursor mCursor; static final String[] PROJECTION = new String[] {//联系人id和姓名 ContactsContract.RawContacts._ID, ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); findView(); } private void findView() { searchview=(SearchView) findViewById(R.id.searchview); listview=(ListView) findViewById(R.id.listview); listview.setTextFilterEnabled(true);//设置对字符串过滤 对应适配器 searchview.setIconifiedByDefault(true); //表示搜索图标是否在输入框内。true效果更加 searchview.onActionViewExpanded(); //表示在内容为空时不显示取消的x按钮,内容不为空时显示. searchview.setSubmitButtonEnabled(true);//编辑框后显示search按钮 searchview.setFocusable(false); searchview.clearFocus(); data = new ArrayList<String>() ; // data.add("Hello"); data.add("Hebe") ; data.add("nick") ; data.add("ahha") ; data.add("jack") ; data.add("hello") ; data.add("welcome") ; mCursor = getContentResolver().query(RawContacts.CONTENT_URI, PROJECTION, null, null, null); if(mCursor!=null){ while(mCursor.moveToNext()){ String name = mCursor.getString(mCursor.getColumnIndex(ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY)); data.add(name); } } mAdapter=new MyAdapter(this, data); listview.setAdapter(mAdapter); /*输入框文字listener*/ searchview.setOnQueryTextListener(new OnQueryTextListener() {// 监听 SearchView 中的数据变化 /*开始搜索listener*/ @Override public boolean onQueryTextSubmit(String queryText) { String selection = RawContacts.DISPLAY_NAME_PRIMARY + " LIKE '%" + queryText + "%' " + " OR " + RawContacts.SORT_KEY_PRIMARY + " LIKE '%" + queryText + "%' "; mCursor = getContentResolver().query(RawContacts.CONTENT_URI, PROJECTION, selection, null, null); if(mCursor!=null){ while(mCursor.moveToNext()){ String name = mCursor.getString(mCursor.getColumnIndex(ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY)); data.add(name); } } mAdapter.notifyDataSetChanged(); return true; } /*搜索变化listener*/ @Override public boolean onQueryTextChange(String newText) { if(newText!=null&&newText.length()>0){ listview.setFilterText(newText); }else{ listview.clearTextFilter(); } if (searchview!= null) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow( searchview.getWindowToken(), 0); } searchview.clearFocus(); } return true; } }); /*点击取消按钮listener,默认点击搜索输入框*/ searchview.setOnCloseListener(new OnCloseListener() { @Override public boolean onClose() { return true; } }); } }
以上就是整个的代码,里面包含了一部分contentProvider的内容,本来listView用的是simpleCurrsorAdapter,那样的话就不要把游标改成list集合了,而我的习惯是用baseAdapter,所以呢就走了点小弯路,以致输入文本时适配器刷新写了重复的代码,消耗了性能。然后也学了一下filter这个类的编写,以前还真的没有写过,记得一定要在
listView加上setTextFilterEnabled(true)不然写的那个文本过滤可就完全没有效果了。最后记得在清单文件里加上
写的不好,就到这里了,至于效果图就不贴了,呵呵呵,如果你觉得对你有帮助就看一下,勿喷就行,欢迎给给意见
源码下载地址http://download.csdn.net/detail/u013278099/8000693