我试图在片段中实现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,因此没有建议。虽然约束应该是我输入的第一个字母。原因是什么?