swapCursor() 导致 CursorIndexOutOfBoundsException,行为异常
swapCursor() causes CursorIndexOutOfBoundsException, behaves abnormally
几天前我遇到了这个问题,我正在开发一个程序,其中 ListView
通过自定义 SimpleCursorAdapter
从数据库中获取数据,我曾经使用 listView.setAdapter(adapter)
每次数据更改时更新ListView
中的数据。但这会导致一个问题,每次更新数据时,listView 都会自动滚动到顶部,所以我决定使用 swapCursor()
代替,如下所示:
Cursor cursor = database.getData();
if(listView.getAdapter() == null){
//Toast.makeText(getApplicationContext(),"null",Toast.LENGTH_SHORT).show();
adapter = (new CustomSimpleCursorAdapter(this,R.layout.todolist,cursor,new String[] {LIST_TEXT},new int[]{R.id.text}));
listView.setAdapter(adapter);
}else {
Runnable runnable =new Runnable() {
@Override
public void run() {
Cursor finalCs = database.getData();
todoListAdapter.swapCursor(finalCs);
todoListAdapter.notifyDataSetChanged();
}
};
runOnUiThread(runnable);
}
但是每次我尝试将数据添加到数据库并将其显示在 listView
:
时都会出现错误
**CursorIndexOutOfBoundsException: Index 12 requested, with a size of 12**
(注意,数据是加到数据库里的,之前用listView.setAdapter(adapter)
没发现问题,我试过去掉todoListAdapter.notifyDataSetChanged();
,但一点用都没有。还有,当我尝试从数据库中删除数据并使用更改更新 listView
时,无论我选择哪一个,它总是 listView
中的最后一项被删除(现在的数据实际上是正确地从数据库中删除),当我再次尝试添加数据时,我现在可以添加与之前删除的数据量相同的数据量(如果我删除了 4 项,那么之后我只能添加 4 项或错误会出现。无论如何,swapCursor()
在我使用它时似乎很糟糕,我做错了什么吗?还是我需要其他东西才能让 swapCursor()
工作?提前致谢!
更新:错误回溯上下文:
android.database.CursorIndexOutOfBoundsException: Index 13 requested, with a size of 13
at android.database.AbstractCursor.checkPosition(AbstractCursor.java:460)
at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
at android.support.v4.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:139)
更新:自定义 SimpleCursorAdapter
:
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import java.util.ArrayList;
public class TodoListAdapter extends SimpleCursorAdapter {
private Cursor c;
private Context context;
private ArrayList<Long> itemChecked = new ArrayList<>();
protected int[] mFrom;
protected int[] mTo;
LayoutInflater inflater;
private int mStringConversionColumn = -1;
private CursorToStringConverter mCursorToStringConverter;
private ViewBinder mViewBinder;
MainActivity main;
String[] mOriginalFrom;
public TodoListAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to,1);
this.c = c;
this.context = context;
mTo = to;
mOriginalFrom = from;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public ViewBinder getViewBinder() {
return mViewBinder;
}
public ArrayList returnSelected(){
return itemChecked;
}
public interface MyInterface{
public void foo();
}
public void setViewBinder(ViewBinder viewBinder) {
mViewBinder = viewBinder;
}
public View getView(final int pos, View inView, ViewGroup parent) {
c.moveToPosition(pos);
if (inView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inView = inflater.inflate(R.layout.todolist, null);
}
final TextView todoText = (TextView) inView.findViewById(R.id.titleText);
final CheckBox cBox = (CheckBox) inView.findViewById(R.id.multiSelectionBox);
System.out.println(c.getCount());
newView(context,c,parent);
bindView(inView,context,c);
final long id = getItemId(pos);
cBox.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v.findViewById(R.id.multiSelectionBox);
if (cb.isChecked()) {
if(context.toString().contains("MainActivity")){
((MainActivity)context).addSelectedId(id);
}else if(context.toString().contains("HistoryActivity")){
((HistoryActivity)context).addSelectedId(id);
}
System.out.println("checked " + id);
} else if (!cb.isChecked()) {
System.out.println("unchecked " + id);
if(context.toString().contains("MainActivity")){
((MainActivity)context).removeSelectedId(id);
}else if(context.toString().contains("HistoryActivity")){
((HistoryActivity)context).removeSelectedId(id);
}
}
}
});
return inView;
}
}
终于明白了,只是因为我没有把adapter中的cursor改成传入的cursor,重写bindView
方法,在原代码中加入this.c = cursor
,然后当调用 getView
,光标将成为新光标。那只是我自己犯的一个愚蠢的错误。
特别感谢@pskink 帮助我意识到,毕竟我不需要覆盖 bindView
或 newView
,但你提到 c.moveToPosition()
让我意识到错误。
几天前我遇到了这个问题,我正在开发一个程序,其中 ListView
通过自定义 SimpleCursorAdapter
从数据库中获取数据,我曾经使用 listView.setAdapter(adapter)
每次数据更改时更新ListView
中的数据。但这会导致一个问题,每次更新数据时,listView 都会自动滚动到顶部,所以我决定使用 swapCursor()
代替,如下所示:
Cursor cursor = database.getData();
if(listView.getAdapter() == null){
//Toast.makeText(getApplicationContext(),"null",Toast.LENGTH_SHORT).show();
adapter = (new CustomSimpleCursorAdapter(this,R.layout.todolist,cursor,new String[] {LIST_TEXT},new int[]{R.id.text}));
listView.setAdapter(adapter);
}else {
Runnable runnable =new Runnable() {
@Override
public void run() {
Cursor finalCs = database.getData();
todoListAdapter.swapCursor(finalCs);
todoListAdapter.notifyDataSetChanged();
}
};
runOnUiThread(runnable);
}
但是每次我尝试将数据添加到数据库并将其显示在 listView
:
**CursorIndexOutOfBoundsException: Index 12 requested, with a size of 12**
(注意,数据是加到数据库里的,之前用listView.setAdapter(adapter)
没发现问题,我试过去掉todoListAdapter.notifyDataSetChanged();
,但一点用都没有。还有,当我尝试从数据库中删除数据并使用更改更新 listView
时,无论我选择哪一个,它总是 listView
中的最后一项被删除(现在的数据实际上是正确地从数据库中删除),当我再次尝试添加数据时,我现在可以添加与之前删除的数据量相同的数据量(如果我删除了 4 项,那么之后我只能添加 4 项或错误会出现。无论如何,swapCursor()
在我使用它时似乎很糟糕,我做错了什么吗?还是我需要其他东西才能让 swapCursor()
工作?提前致谢!
更新:错误回溯上下文:
android.database.CursorIndexOutOfBoundsException: Index 13 requested, with a size of 13
at android.database.AbstractCursor.checkPosition(AbstractCursor.java:460)
at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
at android.support.v4.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:139)
更新:自定义 SimpleCursorAdapter
:
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import java.util.ArrayList;
public class TodoListAdapter extends SimpleCursorAdapter {
private Cursor c;
private Context context;
private ArrayList<Long> itemChecked = new ArrayList<>();
protected int[] mFrom;
protected int[] mTo;
LayoutInflater inflater;
private int mStringConversionColumn = -1;
private CursorToStringConverter mCursorToStringConverter;
private ViewBinder mViewBinder;
MainActivity main;
String[] mOriginalFrom;
public TodoListAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to,1);
this.c = c;
this.context = context;
mTo = to;
mOriginalFrom = from;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public ViewBinder getViewBinder() {
return mViewBinder;
}
public ArrayList returnSelected(){
return itemChecked;
}
public interface MyInterface{
public void foo();
}
public void setViewBinder(ViewBinder viewBinder) {
mViewBinder = viewBinder;
}
public View getView(final int pos, View inView, ViewGroup parent) {
c.moveToPosition(pos);
if (inView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inView = inflater.inflate(R.layout.todolist, null);
}
final TextView todoText = (TextView) inView.findViewById(R.id.titleText);
final CheckBox cBox = (CheckBox) inView.findViewById(R.id.multiSelectionBox);
System.out.println(c.getCount());
newView(context,c,parent);
bindView(inView,context,c);
final long id = getItemId(pos);
cBox.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v.findViewById(R.id.multiSelectionBox);
if (cb.isChecked()) {
if(context.toString().contains("MainActivity")){
((MainActivity)context).addSelectedId(id);
}else if(context.toString().contains("HistoryActivity")){
((HistoryActivity)context).addSelectedId(id);
}
System.out.println("checked " + id);
} else if (!cb.isChecked()) {
System.out.println("unchecked " + id);
if(context.toString().contains("MainActivity")){
((MainActivity)context).removeSelectedId(id);
}else if(context.toString().contains("HistoryActivity")){
((HistoryActivity)context).removeSelectedId(id);
}
}
}
});
return inView;
}
}
终于明白了,只是因为我没有把adapter中的cursor改成传入的cursor,重写bindView
方法,在原代码中加入this.c = cursor
,然后当调用 getView
,光标将成为新光标。那只是我自己犯的一个愚蠢的错误。
特别感谢@pskink 帮助我意识到,毕竟我不需要覆盖 bindView
或 newView
,但你提到 c.moveToPosition()
让我意识到错误。