通过CursorAdapter和CursorLoader实现SearchView实现。 swapCursor提供AStaleDataException

时间:2016-08-25 05:31:21

标签: android sqlite searchview android-cursoradapter android-cursorloader

我试图在片段中实现SearchView,搜索结果通过CursorLoader直接从sqlite查询,并通过自定义CursorAdapter在同一片段中呈现搜索结果。搜索建议也可以直接从sqlite查询,并通过自定义CursorAdapter(CountrySuggestionsAdaptor)呈现。 所以,我有一个CursorLoader用于获取建议(loader id = 0),另一个CursorLoader用于获取搜索结果(loader id = 1)。目前加载建议有3个问题:

1)输入第一个字母并不显示任何建议,即它不会调用自定义CursorAdapter的bindView。它只在第二次打字后开始显示建议

2)如果我输入" Un"它会给出建议,如果我输入" Ur"它会给出这个错误

  

android.database.StaleDataException:试图访问已关闭的   CursorWindow.Most可能的原因:光标在之前被停用   调用这个方法。               在android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:139)               在android.database.AbstractWindowedCursor.getLong(AbstractWindowedCursor.java:74)               在android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:226)               在android.widget.AutoCompleteTextView.buildImeCompletions(AutoCompleteTextView.java:1132)               在android.widget.AutoCompleteTextView.showDropDown(AutoCompleteTextView.java:1091)               在android.widget.AutoCompleteTextView.updateDropDownForFilter(AutoCompleteTextView.java:974)               在android.widget.AutoCompleteTextView.onFilterComplete(AutoCompleteTextView.java:956)               在android.widget.Filter $ ResultsHandler.handleMessage(Filter.java:285)               在android.os.Handler.dispatchMessage(Handler.java:102)               在android.os.Looper.loop(Looper.java:135)               在android.app.ActivityThread.main(ActivityThread.java:5294)               at java.lang.reflect.Method.invoke(Native Method)               在java.lang.reflect.Method.invoke(Method.java:372)               在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:904)               在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

3)首先我输入" Un"它给出了建议,我点击第一个建议,它成功地呈现了一个基于它的列表。然后我另外在搜索视图中键入第三个字母,例如" Uni"它崩溃了,给了我这封信:

  

java.lang.IllegalStateException:尝试重新打开已经关闭的   object:SQLiteQuery:选择_id,countryNameRu,countryId FROM   国家/地区在哪里countryNameRu LIKE' Un%'               在android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)               在android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:58)               在android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:152)               在android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:124)               在android.database.AbstractCursor.moveToPosition(AbstractCursor.java:214)               在android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:225)               在android.widget.AdapterView.rememberSyncState(AdapterView.java:1226)               在android.widget.AdapterView $ AdapterDataSetObserver.onChanged(AdapterView.java:820)               在android.widget.AbsListView $ AdapterDataSetObserver.onChanged(AbsListView.java:6156)               在android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)               在android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)               在android.support.v4.widget.CursorAdapter.swapCursor(CursorAdapter.java:347)               在android.support.v4.widget.CursorAdapter.changeCursor(CursorAdapter.java:315)               在android.support.v4.widget.CursorFilter.publishResults(CursorFilter.java:68)               在android.widget.Filter $ ResultsHandler.handleMessage(Filter.java:282)               在android.os.Handler.dispatchMessage(Handler.java:102)               在android.os.Looper.loop(Looper.java:135)               在android.app.ActivityThread.main(ActivityThread.java:5294)               at java.lang.reflect.Method.invoke(Native Method)               在java.lang.reflect.Method.invoke(Method.java:372)               在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:904)               在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

我现在已经在互联网上寻找解决方案好几天了,我现在有点绝望了。我考虑过实现ContentProvider,但它有什么帮助,是否绝对必要?

片段:

import android.database.Cursor;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v7.widget.SearchView;


import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Callback;

public class RoamingTariffs extends ProgressListFragment implements  LoaderManager.LoaderCallbacks<Cursor>{
    private RoamingTariffSearchResultsAdapter roamingTariffAdapter;
    private CountrySuggestionsAdaptor mSearchViewAdapter;
    RoamingTariffDbHelper dbHelper;

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.roaming_toolbar, menu);
        mSearchViewAdapter = new CountriesSearchResultsAdaptor(this.getActivity(),null);
        SearchView searchView  = (SearchView) menu.findItem(R.id.menu_search).getActionView();

        searchView.setSuggestionsAdapter(mSearchViewAdapter);
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener(){

            @Override
            public boolean onQueryTextSubmit(String s) {
                if(!s.isEmpty())
                  loadSuggestions(s,false);
                return true;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                if(!s.isEmpty())
                    loadSuggestions(s,false);
                return true;
            }
        });

        searchView.setOnSuggestionListener(
                new SearchView.OnSuggestionListener(){

                    @Override
                    public boolean onSuggestionSelect(int position) {
                        Cursor cursor = (Cursor) mSearchViewAdapter.getItem(position);
                        String countryId1 = cursor.getString(1);
                        loadCountryDetails(countryId1);
                        return true;
                    }

                    @Override
                    public boolean onSuggestionClick(int position) {
                        Cursor cursor = (Cursor) mSearchViewAdapter.getItem(position);
                        String countryId1 = cursor.getString(1);
                        loadCountryDetails(countryId1);
                        return false;
                    }
                }
        );
    }

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

        roamingTariffAdapter = new RoamingTariffCursorAdapter(getActivity(), null);
        getListView().setAdapter(roamingTariffAdapter);

        dbHelper = new RoamingTariffDbHelper(getActivity());

        ((LoginActivity) getActivity()).getSupportActionBar().setTitle("Roaming Tariffs");
   RoamingTariffInterface roamingTariffService =
                RoamingTariffClient.getClient().create(RoamingTariffInterface.class);
        Call<List<CountryDto>> call = roamingTariffService.getCountries(countryId);
        call.enqueue(new Callback<List<CountryDto>>() {
            @Override
            public void onResponse(Call<List<CountryDto>> call, Response<List<CountryDto>> response) {
                List<CountryDto> countryList = response.body();
                if (countryList == null)
                    countryList = new ArrayList<CountryDto>();

                // CREATE SQLITE TABLE AND SAVE
                RoamingTariffDbHelper helper = new RoamingTariffDbHelper(getActivity());

                for (CountryDto countryDto : countryList) {
                    helper.addCountryEntry(countryDto);
                }

            }

            @Override
            public void onFailure(Call<List<CountryDto>> call, Throwable t) {
                showError();
            }
        });
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        showProgress();
        CursorLoader cursorLoader = null;
        switch (id){
            case 0://loadSuggestions
                final String queryText = args.getString("queryText");
                cursorLoader =  new CursorLoader(getActivity()) {
                    @Override
                    public Cursor loadInBackground() {
                        Cursor cursor =  dbHelper.getCountryNamesBySearchLetters(queryText);
                        return cursor;
                    }
                };
                break;
            case 1://load results
                final String countryId = args.getString("countryId");
                cursorLoader =  new CursorLoader(getActivity()) {
                    @Override
                    public Cursor loadInBackground() {
                        Cursor cursor =  dbHelper.getOperatorsByCountryId(countryId);
                        return cursor;
                    }
                };
                break;
        }
        return cursorLoader;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        int loaderId = loader.getId();
        switch(loaderId){
            case 0://suggestions are ready to show
                if (data != null)
                    mSearchViewAdapter.swapCursor(data);
                break;
            case 1://search results are ready to show
                if(data !=null){  
                roamingTariffAdapter.swapCursor(data);}
                break;
        }
        showContent();
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mSearchViewAdapter.swapCursor(null);
        roamingTariffAdapter.swapCursor(null);
    }

    public void loadSuggestions(String query){
        Bundle bundle = new Bundle();
        bundle.putString("queryText", query);
        getLoaderManager().restartLoader(0, bundle, this);
    }

    public void loadCountryDetails(String countryId){
        Bundle bundle = new Bundle();
        bundle.putString("countryId", countryId);
        getLoaderManager().restartLoader(1, bundle, this);
    }
...
}

建议适配器:

public class CountrySuggestionsAdaptor extends CursorAdapter {
    private Context context = null;

    public CountrySuggestionsAdaptor(Context context, Cursor c) {
        super(context, c, 0);
    }


    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return LayoutInflater.from(context).inflate(R.layout.country_suggestion_item,parent, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        TextView countryView = (TextView)view.findViewById(R.id.country_item);
        countryView.setText(cursor.getString(1));
    }
}

RoamingTariffDbHelper的相关查询方法:

public Cursor getCountryNamesBySearchLetters(String startingStr){
        SQLiteDatabase db = this.getReadableDatabase();
        String query = RoamingTariffDbContract.SQL_SELECT_COUNTRIES_START_WITH+
                Helper.firstLetterToCapital(startingStr)+"%'";
        Cursor cursor = db.rawQuery(query,null);
        if(cursor == null){
            return null;
        }
        else if(!cursor.moveToFirst()) {
            cursor.close();
            return null;
        }
        return cursor;
    }

修改:我按照@ pskink的建议删除了querytextlisteners,并添加了

 FilterQueryProvider fqp =new FilterQueryProvider() {
            @Override
            public Cursor runQuery(CharSequence constraint) {
                Cursor cursor = null;
                if(constraint.length()!=0)
                   cursor = dbHelper.getCountryNamesBySearchLetters(constraint.toString());
              return cursor;
            }
        };

问题2和3消失了。问题编号1仍然存在。我在runQuery内调试,第一个类型约束由于某种原因为null,因此没有建议。虽然约束应该是我输入的第一个字母。原因是什么?

0 个答案:

没有答案