我尝试使用搜索列表从ListView中的联系人应用列出所有可用的电话号码和显示名称。此外,如果联系人有多个电话号码,则必须在单独的列表项目中显示(因为不同的联系人不是“联系人”应用程序中的同一联系人)。
我已成功检索所有带有显示名称的电话号码并显示在ListView中。联系人的多个电话号码也显示为单独的联系人。
但是,当我搜索时 - 要么为显示名称提供不相关的电话号码,要么在我输入单个字符时无法过滤。请对此提出建议。
这是我的代码:
public class ContactsListFragment extends ListFragment implements
AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor> {
// Defines a tag for identifying log entries
private static final String TAG = "ContactsListFragment";
private ContactsAdapter mAdapter; // The main query adapter
private String mSearchTerm; // Stores the current search query term
// Contact selected listener that allows the activity holding this fragment to be notified of
// a contact being selected
private OnContactsInteractionListener mOnContactSelectedListener;
/**
* Fragments require an empty constructor.
*/
public ContactsListFragment() {}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// Let this fragment contribute menu items
setHasOptionsMenu(true);
// Create the main contacts adapter
mAdapter = new ContactsAdapter(getActivity());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
// Inflate the list fragment layout
return inflater.inflate(R.layout.contact_list_fragment, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Set up ListView, assign adapter and set some listeners. The adapter was previously
// created in onCreate().
setListAdapter(mAdapter);
getListView().setOnItemClickListener(this);
getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
// Assign callback listener which the holding activity must implement. This is used
// so that when a contact item is interacted with (selected by the user) the holding
// activity will be notified and can take further action such as extracting the contact
//details and pass it to AddNewUserDialogFragment
mOnContactSelectedListener = (OnContactsInteractionListener) activity;
}catch(ClassCastException e){
throw new ClassCastException(activity.toString()
+ " must implement OnContactsInteractionListener");
}
}
@Override
public void onPause(){
super.onPause();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// Gets the Cursor object currently bound to the ListView
final Cursor cursor = mAdapter.getCursor();
// Moves to the Cursor row corresponding to the ListView item that was clicked
cursor.moveToPosition(position);
final String displayName = cursor.getString(ContactMobileNumbQuery.DISPLAY_NAME);
final String mobileNumb = cursor.getString(ContactMobileNumbQuery.NUMBER);
mOnContactSelectedListener.onContactSelected(displayName, mobileNumb);
}
/**
* Called when ListView selection is cleared, for example
* when search mode is finished and the currently selected
* contact should no longer be selected.
*/
private void onSelectionCleared() {
// Uses callback to notify activity this contains this fragment
mOnContactSelectedListener.onSelectionCleared();
// Clears currently checked item
getListView().clearChoices();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
// Inflate the menu items
inflater.inflate(R.menu.contact_list_menu, menu);
// Locate the search item
MenuItem searchItem = menu.findItem(R.id.menu_search);
//sets up and configures the ActionBar SearchView
final SearchManager mSearchManager = (SearchManager)getActivity().getSystemService(Context.SEARCH_SERVICE);
// Retrieves the SearchView from the search menu item
final SearchView mSearchView = (SearchView) searchItem.getActionView();
// Assign searchable info to SearchView
mSearchView.setSearchableInfo(mSearchManager.getSearchableInfo(getActivity().getComponentName()));
// Set listeners for SearchView
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Updates
// the search filter, and restarts the loader to do a new query
// using the new search string.
String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
// Don't do anything if the filter is empty
if(mSearchTerm == null && newText == null){
return true;
}
// Don't do anything if the new filter is the same as the current filter
if(mSearchTerm != null && mSearchTerm.equals(newText)){
return true;
}
// Updates current filter to new filter
mSearchTerm = newFilter;
// Restarts the loader. This triggers onCreateLoader(), which builds the
// necessary content Uri from mSearchTerm.
getLoaderManager().restartLoader(ContactMobileNumbQuery.QUERY_ID, null, ContactsListFragment.this);
return true;
}
});
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Nothing to do when the action item is expanded
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// When the user collapses the SearchView the current search string is
// cleared and the loader restarted.
if(!TextUtils.isEmpty(mSearchTerm)){
onSelectionCleared();
}
mSearchTerm = null;
getLoaderManager().restartLoader(ContactMobileNumbQuery.QUERY_ID, null, ContactsListFragment.this);
return true;
}
});
getLoaderManager().restartLoader(ContactMobileNumbQuery.QUERY_ID, null, ContactsListFragment.this);
}
@Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
if(!TextUtils.isEmpty(mSearchTerm)){
// Saves the current search string
outState.putString(SearchManager.QUERY, mSearchTerm);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
return super.onOptionsItemSelected(item);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader starts");
//If this is the loader for finding contacts in the Contacts Provider
if(id == ContactMobileNumbQuery.QUERY_ID){
Uri contentUri;
// There are two types of searches, one which displays all contacts and
// one which filters contacts by a search query. If mSearchTerm is set
// then a search query has been entered and the latter should be used.
if(mSearchTerm == null){
// Since there's no search string, use the content URI that searches the entire
// Contacts table
contentUri = ContactMobileNumbQuery.CONTENT_URI;
}else{
// Since there's a search string, use the special content Uri that searches the
// Contacts table. The URI consists of a base Uri and the search string.
contentUri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(mSearchTerm));
}
// Returns a new CursorLoader for querying the Contacts table. No arguments are used
// for the selection clause. The search string is either encoded onto the content URI,
// or no contacts search string is used. The other search criteria are constants. See
// the ContactsQuery interface.
return new CursorLoader(getActivity(),
contentUri,
ContactMobileNumbQuery.PROJECTION,
ContactMobileNumbQuery.SELECTION,
null,
ContactMobileNumbQuery.SORT_ORDER);
}
Log.e(TAG, "onCreateLoader - incorrect ID provided (" + id + ")");
return null;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// This swaps the new cursor into the adapter.
if(loader.getId() == ContactMobileNumbQuery.QUERY_ID){
mAdapter.swapCursor(data);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
if(loader.getId() == ContactMobileNumbQuery.QUERY_ID){
// When the loader is being reset, clear the cursor from the adapter. This allows the
// cursor resources to be freed.
mAdapter.swapCursor(null);
}
}
/**
* This is a subclass of CursorAdapter that supports binding Cursor columns to a view layout.
* If those items are part of search results, it will be bind to the view layout.
*/
private class ContactsAdapter extends CursorAdapter implements SectionIndexer {
private LayoutInflater mInflater; // Stores the layout inflater
private TextAppearanceSpan highlightTextSpan; // Stores the highlight text appearance style
/**
* Instantiates a new Contacts Adapter.
* @param context A context that has access to the app's layout.
*/
public ContactsAdapter(Context context) {
super(context, null, 0);
// Stores inflater for use later
mInflater = LayoutInflater.from(context);
}
@Override
public Object[] getSections() {
// TODO Auto-generated method stub
return null;
}
@Override
public int getPositionForSection(int section) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getSectionForPosition(int position) {
// TODO Auto-generated method stub
return 0;
}
/**
* Binds data from the Cursor to the provided view.
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
// Gets handles to individual view resources
final ViewHolder holder = (ViewHolder) view.getTag();
final String displayName = cursor.getString(ContactMobileNumbQuery.DISPLAY_NAME);
final String mobileNumb = cursor.getString(ContactMobileNumbQuery.NUMBER);
final int startIndex = indexOfSearchQuery(displayName);
if (startIndex == -1) {
// If the user didn't do a search, or the search string didn't match a display
// name, show the display name without highlighting
holder.text1.setText(displayName);
holder.mobile_text.setText(mobileNumb);
if (TextUtils.isEmpty(mSearchTerm)) {
} else {
}
}else {
// If the search string matched the display name, applies a SpannableString to
// highlight the search string with the displayed display name
// Wraps the display name in the SpannableString
final SpannableString highlightedName = new SpannableString(displayName);
// Sets the span to start at the starting point of the match and end at "length"
// characters beyond the starting point
highlightedName.setSpan(highlightTextSpan, startIndex,
startIndex + mSearchTerm.length(), 0);
// Binds the SpannableString to the display name View object
holder.text1.setText(highlightedName);
}
}
private int indexOfSearchQuery(String displayName) {
if (!TextUtils.isEmpty(mSearchTerm)) {
return displayName.toLowerCase(Locale.getDefault()).indexOf(
mSearchTerm.toLowerCase(Locale.getDefault()));
}
return -1;
}
/**
* Overrides newView() to inflate the list item views.
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
// Inflates the list item layout.
final View itemLayout = mInflater.inflate(R.layout.contacts_list_view_item, viewGroup,false);
// Creates a new ViewHolder in which to store handles to each view resource. This
// allows bindView() to retrieve stored references instead of calling findViewById for
// each instance of the layout.
final ViewHolder holder = new ViewHolder();
holder.text1 = (TextView) itemLayout.findViewById(android.R.id.text1);
holder.text2 = (TextView) itemLayout.findViewById(android.R.id.text2);
holder.mobile_text = (TextView) itemLayout.findViewById(R.id.mobile_text);
// Stores the resourceHolder instance in itemLayout. This makes resourceHolder
// available to bindView and other methods that receive a handle to the item view.
itemLayout.setTag(holder);
// Returns the item layout view
return itemLayout;
}
}
/**
* A class that defines fields for each resource ID in the list item layout. This allows
* ContactsAdapter.newView() to store the IDs once, when it inflates the layout, instead of
* calling findViewById in each iteration of bindView.
*/
private class ViewHolder {
TextView text1;
TextView text2;
TextView mobile_text;
}
/**
* This interface must be implemented by any activity that loads this fragment. When an
* interaction occurs, such as touching an item from the ListView, these callbacks will
* be invoked to communicate the event back to the activity.
*/
public interface OnContactsInteractionListener {
/**
* Called when a contact is selected from the ListView.
* @param contactUri The contact Uri.
*/
public void onContactSelected(String name, String mobile);
/**
* Called when the ListView selection is cleared like when
* a contact search is taking place or is finishing.
*/
public void onSelectionCleared();
// Uses callback to notify activity this contains this fragment
}
/**
* This interface defines constants used by mobile number retrieval queries.
*/
public interface ContactMobileNumbQuery{
final static int QUERY_ID = 1;
//A Content Uri for Phone table
final static Uri CONTENT_URI = Phone.CONTENT_URI;
//The search or filter query Uri
final static Uri FILTER_URI = Phone.CONTENT_FILTER_URI;
final static String SELECTION = Phone.HAS_PHONE_NUMBER + "=1" + " AND " + Phone.DISPLAY_NAME_PRIMARY + "<>''";
final static String SORT_ORDER = Phone.SORT_KEY_PRIMARY;
final static String[] PROJECTION = {
Phone._ID,
Phone.DISPLAY_NAME_PRIMARY,
Phone.LOOKUP_KEY,
Phone.HAS_PHONE_NUMBER,
Phone.NUMBER,
Phone.TYPE,
SORT_ORDER
};
final static int ID = 0;
final static int DISPLAY_NAME = 1;
final static int LOOKUP_KEY = 2;
final static int HAS_PHONE = 3;
final static int NUMBER = 4;
final static int TYPE = 5;
}
}
有人可以帮我解决吗?提前谢谢!
答案 0 :(得分:0)
以下代码修复了我的问题。
@Override
public void bindView(View view, Context context, Cursor cursor) {
// Gets handles to individual view resources
final ViewHolder holder = (ViewHolder) view.getTag();
final String displayName = cursor.getString(ContactMobileNumbQuery.DISPLAY_NAME);
final String mobileNumb = cursor.getString(ContactMobileNumbQuery.NUMBER);
holder.text1.setText(displayName);
holder.mobile_text.setText(mobileNumb);
}