Android 通过CursorAdapter和CursorLoader实现SearchView。swapCursor正在给AsaledataException
我试图在片段中实现SearchView,通过CursorLoader直接从sqlite查询搜索结果,并通过自定义CursorAdapter在同一片段中呈现搜索结果。搜索建议也可以直接从sqlite查询,并通过自定义游标适配器(CountrySuggestionsAdapter)呈现。 因此,我有一个用于获取建议的游标加载程序(加载程序id=0),另一个用于获取搜索结果的游标加载程序(加载程序id=1)。目前装载建议存在3个问题: 1) 键入第一个字母不会显示任何建议,即它不会调用自定义游标适配器的bindView。它只在第二次输入后才开始显示建议 2) 如果我输入“Un”,它将给出建议,如果我输入“Ur”,它将给出此错误 android.database.StaleDataException:尝试访问已关闭的 光标窗口。最可能的原因:光标在 调用此方法。 位于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) 位于java.lang.reflect.Method.invoke(本机方法) 位于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:尝试重新打开已关闭的 对象:SQLiteQuery:从中选择_id、countryNameRu、countryId countryNameRu喜欢“Un%”的国家 在android.database.sqlite.SQLiteClosable.AcquisiteReference(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) 位于java.lang.reflect.Method.invoke(本机方法) 位于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,但它会有什么帮助?它是绝对必要的吗 片段:Android 通过CursorAdapter和CursorLoader实现SearchView。swapCursor正在给AsaledataException,android,sqlite,searchview,android-cursoradapter,android-cursorloader,Android,Sqlite,Searchview,Android Cursoradapter,Android Cursorloader,我试图在片段中实现SearchView,通过CursorLoader直接从sqlite查询搜索结果,并通过自定义CursorAdapter在同一片段中呈现搜索结果。搜索建议也可以直接从sqlite查询,并通过自定义游标适配器(CountrySuggestionsAdapter)呈现。 因此,我有一个用于获取建议的游标加载程序(加载程序id=0),另一个用于获取搜索结果的游标加载程序(加载程序id=1)。目前装载建议存在3个问题: 1) 键入第一个字母不会显示任何建议,即它不会调用自定义游标适配器
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);
}
...
}
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,因此没有任何建议。虽然约束应该是我键入的第一个字母。原因是什么?问题是光标已关闭。检查您的代码以确定确切的点我添加了另一个问题,您可以查看它吗请向Helper提供调试结果firstLetterToCapital(startingStr)或粘贴代码Yes,您应该在
CursorAdapter
上使用自定义ContentProvider
和设置FilterQueryProvider
而不是自定义CursorLoader
@Ramit,这没什么。只是一个大写的公共静态字符串firstletttocapital(字符串区域设置){return Character.toUpperCase(locale.charAt(0))+locale.substring(1);}问题已经解决。检查您的代码以确定确切的点我添加了另一个问题,您能看一下吗
FilterQueryProvider fqp =new FilterQueryProvider() {
@Override
public Cursor runQuery(CharSequence constraint) {
Cursor cursor = null;
if(constraint.length()!=0)
cursor = dbHelper.getCountryNamesBySearchLetters(constraint.toString());
return cursor;
}
};