SearchView in SherlockListFragment

2019-08-16 02:40发布

问题:

I have some troubles using SearchView in my SherlockListFragment.There is a custom ArrayAdapter and a listview which contains one image and two TextViews for each item. Everything works well until I applied SearchView for my ListFragment. The search icon is expandable and I'm also able to enter words into the search bar.

PROBLEM

The custom adapter is able to filter the listview now, but when I delete the last letter from the expandable searchview, or close the searchview, the app crashed.

I have attached my ArrayAdapter class, as well as, my SherlockListFragment below.

Custom ArrayAdapter with Filter Class

 public static class ShopListAdapter extends ArrayAdapter<ShopEntry>
 {
    private final LayoutInflater mInflater;

    private List<ShopEntry> filteredData;
    private List<ShopEntry> originalData;

    public ShopListAdapter(Context context, List<ShopEntry> filteredData)
    {
        super(context, android.R.layout.simple_list_item_2, filteredData);
        mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.filteredData = filteredData;

    }

    public void setData(List<ShopEntry> data)
    {
        clear();
        if(data != null)
        {
            for(ShopEntry ShopEntry : data)
            {
                add(ShopEntry);

            }
        }
        filteredData = data;
        originalData = data;

    }

    @Override
    public int getCount()
    {
        return filteredData.size();
    }

    @Override
    public ShopEntry getItem(int pos)
    {
        return filteredData.get(pos);
    }

    /**
     * Populate new items in the list.
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        View view;

        if(convertView == null)
        {
            view = mInflater.inflate(R.layout.specific_shop_list_item,
                    parent, false);
        }
        else
        {
            view = convertView;
        }

        ShopEntry item = filteredData.get(position);
        String url = item.getImg();
        ((SmartImageView) view.findViewById(R.id.shopImg)).setImageUrl(url);
        ((TextView) view.findViewById(R.id.shopType)).setText(item
                .getShopName());
        view.setBackgroundColor(0xff1e8e8);

        return view;
    }

    private class MyFilter extends Filter
    {

        @SuppressWarnings("unchecked" )
        @Override
        protected void publishResults(CharSequence constraint,
                FilterResults results)
        {
            if(results != null && results.count > 0)
            {
                filteredData = (ArrayList<ShopEntry>) results.values;
                notifyDataSetChanged();

            }
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint)
        {
            constraint = constraint.toString();
            filteredData = originalData;
            if(constraint.length() == 0 || constraint.equals(""))
            {
                FilterResults original = new FilterResults();
                original.count = filteredData.size();
                original.values = filteredData;
                return original;
            }
            else
            {
                List<ShopEntry> filtered = new ArrayList<ShopEntry>();
                for(ShopEntry l : filteredData)
                {
                    if(l.getShopName().contains(constraint))
                    { // YOU NEED TO CHANGE THIS
                        filtered.add(l);
                        Log.d("filter", "filter " + constraint + " "
                                + l.getShopName().toString());
                    }
                }
                FilterResults newFilterResults = new FilterResults();
                newFilterResults.count = filtered.size();
                newFilterResults.values = filtered;
                return newFilterResults;
            }
        }
    }

    MyFilter mFilter;

    @Override
    public Filter getFilter()
    {
        if(mFilter == null)
        {
            mFilter = new MyFilter();
        }
        return mFilter;
    }

}

My ListFragment Class

public class ShopListFragment extends SherlockListFragment implements
    LoaderManager.LoaderCallbacks<List<ShopEntry>>, OnQueryTextListener
 {

List<ShopEntry> shopEntry = new ArrayList<ShopEntry>();

// This is the Adapter being used to display the list's data.
ShopListAdapter mAdapter;
SearchView searchView;
// If non-null, this is the current filter the user has provided.
String mCurFilter = "";

OnQueryTextListenerCompat mOnQueryTextListenerCompat;

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);

    // Give some text to display if there is no data. In a real
    // application this would come from a resource.
    setEmptyText("No Such Shop");

    // We have a menu item to show in action bar.
    setHasOptionsMenu(true);

    // Create an empty adapter we will use to display the loaded data.
    mAdapter = new ShopListAdapter(getActivity(), shopEntry);
    setListAdapter(mAdapter);

    // Start out with a progress indicator.
    setListShown(false);

    // Prepare the loader. Either re-connect with an existing one,
    // or start a new one.

    getLoaderManager().initLoader(0, null, this);

}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
    // Place an action bar item for searching.
    // super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.menu_with_search, menu);

    MenuItem item = menu.findItem(R.id.action_search);

    searchView = new SearchView(getSherlockActivity().getSupportActionBar()
            .getThemedContext());
    searchView.setQueryHint(getString(R.string.search_hint));

    searchView.setOnQueryTextListener(this);

    int currentapiVersion = android.os.Build.VERSION.SDK_INT;
    if(currentapiVersion >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    {
        item.setOnActionExpandListener(new OnActionExpandListener()
        {

            @Override
            public boolean onMenuItemActionCollapse(MenuItem item)
            {
                searchView.onActionViewCollapsed();
                searchView.setQuery(null, true);
                searchView.clearFocus();
                return true; // Return true to collapse action view
            }

            @Override
            public boolean onMenuItemActionExpand(MenuItem item)
            {
                // TODO Auto-generated method stub

                return true;
            }
        });
    }
    else
    {
        // do something for phones running an SDK before froyo
        searchView.setOnCloseListener(new OnCloseListener()
        {

            @Override
            public boolean onClose()
            {
                searchView.onActionViewCollapsed();
                searchView.setQuery(null, true);
                searchView.clearFocus();
                return false;
            }
        });
    }

    item.setActionView(searchView);
    super.onCreateOptionsMenu(menu, inflater);

}

@Override
public boolean onQueryTextChange(String newText)
{ // called when the action bar search text has changed. Update
    // the search filter, and restart the loader to do a new query
    // with this filter.
    String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
    // Don't do anything if the filter hasn't actually changed.
    // Prevents restarting the loader when restoring state.
    if(mCurFilter == null && newFilter == null)
    {
        return true;
    }
    if(mCurFilter != null && mCurFilter.equals(newFilter))
    {
        return true;
    }
    mCurFilter = newFilter;
    mAdapter.getFilter().filter(mCurFilter.toString());

    getLoaderManager().restartLoader(0, null, this);

    return true;
}

@Override
public boolean onQueryTextSubmit(String query)
{
    // TODO Auto-generated method stub
    return true;
}

@Override
public Loader<List<ShopEntry>> onCreateLoader(int id, Bundle args)
{
    // This is called when a new Loader needs to be created. This
    // sample only has one Loader with no arguments, so it is simple.

    return new ShopListLoader(getActivity());
}

@Override
public void onLoadFinished(Loader<List<ShopEntry>> loader,
        List<ShopEntry> data)
{
    // Set the new data in the adapter.
    mAdapter.setData(data);

    // The list should now be shown.
    if(isResumed())
    {
        setListShown(true);
    }
    else
    {
        setListShownNoAnimation(true);
    }
}

@Override
public void onLoaderReset(Loader<List<ShopEntry>> loader)
{
    // Clear the data in the adapter.
    mAdapter.setData(null);
}
 }

Log File

08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.SpannableStringBuilder.sendTextChanged(SpannableStringBuilder.java:962)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:496)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:212)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:30)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.method.BaseKeyListener.backspaceOrForwardDelete(BaseKeyListener.java:94)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.method.BaseKeyListener.backspace(BaseKeyListener.java:49)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.method.BaseKeyListener.onKeyDown(BaseKeyListener.java:155)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.method.QwertyKeyListener.onKeyDown(QwertyKeyListener.java:356)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.text.method.TextKeyListener.onKeyDown(TextKeyListener.java:136)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.widget.TextView.doKeyDown(TextView.java:5385)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.widget.TextView.onKeyDown(TextView.java:5204)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.widget.AutoCompleteTextView.onKeyDown(AutoCompleteTextView.java:716)
    08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.KeyEvent.dispatch(KeyEvent.java:2609)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.View.dispatchKeyEvent(View.java:7205)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1359)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1920)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1395)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.app.Activity.dispatchKeyEvent(Activity.java:2370)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at com.actionbarsherlock.app.SherlockFragmentActivity.dispatchKeyEvent(SherlockFragmentActivity.java:121)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1847)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3701)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewRootImpl.handleImeFinishedEvent(ViewRootImpl.java:3651)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:2818)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.os.Handler.dispatchMessage(Handler.java:99)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.os.Looper.loop(Looper.java:137)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at android.app.ActivityThread.main(ActivityThread.java:5041)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at java.lang.reflect.Method.invokeNative(Native Method)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at java.lang.reflect.Method.invoke(Method.java:511)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
08-20 01:44:44.549: E/AndroidRuntime(4246):     at dalvik.system.NativeStart.main(Native Method)

Can anyone help me ? thx

回答1:

When the SearchView in Sherlock is expanded it sets the TextView component to "", you can see it from the source:

 @Override
public void onActionViewExpanded() {
    if (mExpandedInActionView) return;

    mExpandedInActionView = true;
    mCollapsedImeOptions = mQueryTextView.getImeOptions();
    mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
    mQueryTextView.setText("");
    setIconified(false);
}

That's why your filter returns the original data. Contrary, comparing to the android.widget.SearchView it does not set the query to "" when the action view is also collapsed. Long story short you need to omit filtering with "" (unless needed), and also check if you are invalidating the ListView when the filtering is performed, since you havent posted the filtering implementation.

Edit: Example of filtering listview

private class MyFilter extends Filter {

@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
    if (results!=null && results.count > 0) {
        items = (ArrayList<Data>) results.values;
        notifyDataSetChanged();

    }
}

@Override
protected FilterResults performFiltering(CharSequence constraint) {
    constraint = constraint.toString().toLowerCase();
    items = mOriginalData;
    if (constraint.length() == 0 || constraint.equals("")) {
        FilterResults original = new FilterResults();
        original.count = items.size();
        original.values = items;
        return original;
    } else {
        List<Data> filtered = new ArrayList<Data>();
        for (Data l : items) {
            if ( SOME CONDITION TO FILTER ){ // YOU NEED TO CHANGE THIS
             filtered.add(l);
            }
        }
        FilterResults newFilterResults = new FilterResults();
        newFilterResults.count = filtered.size();
        newFilterResults.values = filtered;
        return newFilterResults;
    }
}

};

In your adapter:

@Override
public Filter getFilter() {
    if (mFilter == null) {
        mFilter = new MyFilter();
    }
    return mFilter;
}