android android-listview nullpointerexception android-cursorloader

android - NullPointerException en onLoaderFinished utilizando SimpleCursorAdapter



android-listview android-cursorloader (2)

newView ResourceCursorAdapter donde utilicé newView y bindView a SimpleCursorAdapter donde estoy usando solo el método getView .

Ahora tengo un error en onLoaderFinished . Aunque me da NullPointerException en adapter.swapCursor(cursor) tanto mi adaptador como el objeto del cursor NO son nulos . Publicaré todo mi código a continuación. Cualquier ayuda es muy apreciada (no le queda mucho pelo para sacar).

import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.widget.ResourceCursorAdapter; import android.util.Log; import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.Window; import android.widget.CheckBox; import android.widget.ListView; import android.widget.TextView; public class ContactSelect extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> { private static final int LOADER_ID = 1; private MyAdapter adapter; private ListView list; private View row; private SparseBooleanArray checkedState = new SparseBooleanArray(); @SuppressLint({ "NewApi", "NewApi" }) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.activity_contact_select); adapter = new MyAdapter(this, R.layout.contacts_select_row, null, null, null, 0); getSupportLoaderManager().initLoader(LOADER_ID, null, this); list = (ListView)findViewById(R.id.list); list.setAdapter(adapter); list.setEmptyView(findViewById(R.id.empty)); } @SuppressLint("NewApi") public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { final String projection[] = new String[]{ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME}; final Uri uri = ContactsContract.Contacts.CONTENT_URI; final String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1" + " AND " + ContactsContract.Contacts.IN_VISIBLE_GROUP + " =1"; final String order = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; final CursorLoader loader = new CursorLoader(this, uri, projection, selection, null, order); return loader; } public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { for(int i=0;i<cursor.getCount();i++){ checkedState.put(i, false); } adapter.swapCursor(cursor); } public void onLoaderReset(Loader<Cursor> loader) { adapter.swapCursor(null); } private class MyAdapter extends SimpleCursorAdapter implements OnClickListener{ private CheckBox markedBox; private TextView familyText; private Context context; private Cursor cursor; public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { super(context, layout, c, from, to, flags); this.context = context; this.cursor = getCursor(); } @Override public View getView(int position, View view, ViewGroup group) { final LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); row = li.inflate(R.layout.contacts_select_row, group, false); view.setTag(cursor.getPosition()); view.setOnClickListener(this); familyText = (TextView)view.findViewById(R.id.contacts_row_family_name); markedBox = (CheckBox)view.findViewById(R.id.contacts_row_check); familyText.setText(cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))); boolean currentlyChecked = checkedState.get(cursor.getPosition()); markedBox.setChecked(currentlyChecked); setProgressBarIndeterminateVisibility(false); return super.getView(position, view, group); } public void onClick(View view) { int rowId = (Integer)view.getTag(); Log.d("OnClick", String.valueOf(rowId)); boolean currentlyChecked = checkedState.get(rowId); markedBox.setChecked(!currentlyChecked); checkedState.put(rowId, !currentlyChecked); Log.d("checkedState", "checkedState(" + rowId + ") = " + checkedState.get(rowId)); } } }


Una llamada del método swapCursor de la clase SimpleCursorAdapter desencadenará una función que correlaciona los nombres de columna de la matriz String proporcionada al constructor (el 4º parámetro) con una matriz de enteros que representa los índices de las columnas. Al pasar null en el constructor de MyAdapter para la matriz String representa los nombres de las columnas del cursor, esto arrojará una NullPointerException más tarde cuando swapCursor intente hacer la asignación (la NullPointerException debería aparecer en un método findColumns , que es el real método que usa los nombres de columna String array).

La solución es pasar una matriz String válida, también puede querer hacer esto para la matriz int representa los ids para las vistas en las que colocar los datos:

String[] from = {ContactsContract.Contacts.DISPLAY_NAME}; int[] to = {R.id.contacts_row_family_name, R.id.contacts_row_check}; adapter = new MyAdapter(this, R.layout.contacts_select_row, null, from, to, 0);

No sé lo que intenta hacer, pero su implementación del método getView no es del todo correcta:

Haces lo normal para el método getView (crear diseños, buscar vistas, datos de enlace) y luego simplemente regresas la vista desde la superclase (?!?), Probablemente solo verás el diseño predeterminado sin nada en él.

La forma en que escribiste el método getView no es muy eficiente, es posible que desees examinar el reciclaje de vistas y el patrón del titular de la vista.

cursor.getPosition() no hará lo que quieras ya que no mueves el cursor a la posición correcta. De forma predeterminada, los adaptadores basados ​​en cursores lo harán por usted en el método getView , pero a medida que anula el método, su trabajo es mover la posición del cursor.

Debe dejar el método getView y usar los dos métodos newView y bindView ya que ofrecen una mejor separación de la lógica.


adapter = new MyAdapter (esto, R.layout.contacts_select_row, null, null, null, 0);

y su clase MyAdapter está pasando el cursor nulo

public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { super(context, layout, c, from, to, flags); this.context = context; this.cursor = getCursor(); }