3つのタブがあるSherlockFragmentActivityを拡張するアクティビティを使用しています。3つのタブは、LoaderManager.LoaderCallbacksを実装するListFragmentsです。アクティビティのOnCreateメソッドは、次のようにタブをロードします
bar.addTab(bar.newTab()
.setTag("venues_list")
.setText(getString(R.string.list_venues_header))
.setTabListener(new TabListener<VenueListFragment>(
this, getString(R.string.list_invites_header), VenueListFragment.class, null)));
// I do the EXACT same thing for the other two tabs, using their respective ListFragments
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
タブをロードするレイアウトは非常に単純です。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
</LinearLayout>
各タブには、これと同じクラスがあり、単に異なるものと呼ばれます。
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockListFragment;
import com.lateral.oursvp.R;
import com.lateral.oursvp.database.SimpleCursorLoader;
import com.lateral.oursvp.database.VenuesDataSource;
/**
* @author rabbott
*
*/
public class VenueListFragment extends SherlockListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
SharedPreferences appPreferences;
private CursorAdapter cursorAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// bind the columns of the cursor to the list
String[] from = new String[] { VenuesDataSource.KEY_NAME, VenuesDataSource.KEY_DESCRIPTION };
int[] to = new int[] { R.id.list_item_title, R.id.list_item_subtitle };
cursorAdapter = new SimpleCursorAdapter( getActivity(), R.layout.list_item, null, from, to, 0);
// retrieve the listview to populate
ListView lv = (ListView) getActivity().findViewById(android.R.id.list);
// set the adapter on the listview
lv.setAdapter(cursorAdapter);
// click event for each row of the list
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View view,
int position, long id) {
Cursor cursor = cursorAdapter.getCursor();
cursor.moveToPosition(position);
Toast.makeText(getActivity(), "Tapped row " + position + "!", Toast.LENGTH_SHORT).show();
}
});
// Start out with a progress indicator.
setListShown(false);
// load the data
getActivity().getSupportLoaderManager().initLoader(0, null, this);
}
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
return new VenueCursorLoader(getActivity());
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
cursorAdapter.swapCursor(cursor);
// the list should now be shown
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
public void onLoaderReset(Loader<Cursor> loader) {
cursorAdapter.swapCursor(null);
}
public static final class VenueCursorLoader extends SimpleCursorLoader {
Context mContext;
public VenueCursorLoader(Context context) {
super(context);
mContext = context;
}
@Override
public Cursor loadInBackground() {
VenuesDataSource datasource = new VenuesDataSource(mContext);
return datasource.getAllVenues(((EventActivity) mContext).getEventId());
}
}
}
ここで定義されたSimpleCursorLoadedを使用します。
import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;
public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
private Cursor mCursor;
public SimpleCursorLoader(Context context) {
super(context);
}
/* Runs on a worker thread */
@Override
public abstract Cursor loadInBackground();
/* Runs on the UI thread */
@Override
public void deliverResult(Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
/**
* Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
* will be called on the UI thread. If a previous load has been completed and is still valid
* the result may be passed to the callbacks immediately.
* <p/>
* Must be called from the UI thread
*/
@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread
*/
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
}
私が抱えている問題は、say ..を選択すると、2番目のタブ(3番目のタブも同じです)がカーソルを読み込もうとしますが、list_itemsにデータを入力しようとすると、次のようなエラーが発生します。
E/AndroidRuntime(2055): java.lang.IllegalArgumentException: column 'GIVEN_VARIABLE' does not exist
いくつかのブレークポイントを設定した後、使用しようとしているカーソルが最初のタブのカーソルであることがわかりました。これはインフレで正常に読み込まれましたが、明らかに閉じられておらず、私が( think)getAllVenues()とは異なるカーソルを送信しています
編集:これは、スタックトレースが私に送信する最後の場所です。 いくつかのブレークポイントを設定すると、ここに提供されているカーソルが、[会場]タブではなく、最初のタブからのカーソルであることがわかります。
編集:getAllVenuesメソッドを表示するVenuesDataSourceコード
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.util.Log;
/**
* @author rabbott
*
*/
public class VenuesDataSource extends appSQLiteHelper {
public static final String TABLE_NAME = "venues";
// venue columns
public static final String KEY_NAME = "name";
public static final String KEY_DESCRIPTION = "description";
public static final String KEY_START_TIME = "start_time";
public static final String KEY_ADDRESS = "address";
public static final String KEY_CITY = "city";
public static final String KEY_STATE = "state";
public static final String KEY_ZIP = "postal_code";
public static final String KEY_LNG = "lng";
public static final String KEY_LAT = "lat";
public static final String KEY_PHONE = "phone";
public static String COLUMNS_VENUES[] = {
DatabaseConstants.KEY_ROWID,
DatabaseConstants.KEY_EVENT_ID,
KEY_NAME,
KEY_DESCRIPTION,
KEY_START_TIME,
KEY_ADDRESS,
KEY_CITY,
KEY_STATE,
KEY_ZIP,
KEY_LNG,
KEY_LAT,
KEY_PHONE,
DatabaseConstants.KEY_CREATED_AT
};
public static final String CREATE_STATEMENT = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "event_id INTEGER NOT NULL,"
+ "name TEXT NOT NULL,"
+ "description TEXT,"
+ "start_time TEXT,"
+ "address TEXT NOT NULL,"
+ "city TEXT NOT NULL,"
+ "state TEXT NOT NULL,"
+ "postal_code TEXT,"
+ "lat TEXT,"
+ "lng TEXT,"
+ "phone TEXT,"
+ "created_at TEXT);";
public VenuesDataSource(Context context) {
super(context);
Log.i("VenueDataSource", "Constructor");
}
// create a new contact locally
public long createVenue(Integer venue_id, Integer event_id, String name, String description, String start_time, String address, String city, String state, String postal_code, String lat, String lng, String phone, String created_at) {
ContentValues initialValues = new ContentValues();
initialValues.put(DatabaseConstants.KEY_ROWID, event_id);
initialValues.put(DatabaseConstants.KEY_EVENT_ID, event_id);
initialValues.put(KEY_NAME, name);
initialValues.put(KEY_DESCRIPTION, description);
initialValues.put(KEY_START_TIME, start_time);
initialValues.put(KEY_ADDRESS, address);
initialValues.put(KEY_CITY, city);
initialValues.put(KEY_STATE, state);
initialValues.put(KEY_ZIP, postal_code);
initialValues.put(KEY_PHONE, phone);
initialValues.put(KEY_LAT, lat);
initialValues.put(KEY_LNG, lng);
initialValues.put(DatabaseConstants.KEY_CREATED_AT, created_at);
return getWritableDatabase().insert(TABLE_NAME, null, initialValues);
}
// retrieve a venue from the local database
public Cursor getVenue(long rowId) throws SQLException {
Cursor mCursor = getWritableDatabase().query(true, TABLE_NAME, COLUMNS_VENUES, DatabaseConstants.KEY_ROWID + "=" + rowId, null, null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
// update a local venue
public long updateVenue(Integer venue_id, Integer event_id, String name, String description, String start_time, String address, String city, String state, String postal_code, String lat, String lng, String phone) {
ContentValues initialValues = new ContentValues();
initialValues.put(DatabaseConstants.KEY_EVENT_ID, event_id);
initialValues.put(KEY_NAME, name);
initialValues.put(KEY_DESCRIPTION, description);
initialValues.put(KEY_START_TIME, start_time);
initialValues.put(KEY_ADDRESS, address);
initialValues.put(KEY_CITY, city);
initialValues.put(KEY_STATE, state);
initialValues.put(KEY_ZIP, postal_code);
initialValues.put(KEY_PHONE, phone);
initialValues.put(KEY_LAT, lat);
initialValues.put(KEY_LNG, lng);
return getWritableDatabase().update(TABLE_NAME, initialValues, "_id=?", new String[] { Long.toString(venue_id) });
}
// delete a local venue
public boolean deleteVenue(long rowId) {
return (getWritableDatabase().delete(TABLE_NAME, DatabaseConstants.KEY_ROWID + "=" + rowId, null) > 0);
}
// retrieve all local venues
public Cursor getAllVenues(long event_id) {
Cursor mCursor = getWritableDatabase().query(true, TABLE_NAME, COLUMNS_VENUES, DatabaseConstants.KEY_EVENT_ID + "=" + event_id,
null, null, null, null, null);
return mCursor;
}
public boolean venueExists(int venue_id) {
Cursor mCursor = getWritableDatabase().query(true, TABLE_NAME, COLUMNS_VENUES, DatabaseConstants.KEY_ROWID + "=" + venue_id,
null, null, null, null, null);
if (mCursor.getCount() == 0) {
mCursor.close();
return false;
} else {
mCursor.close();
return true;
}
}
public void parseVenue(JSONObject venueJson) throws JSONException {
boolean venue_exists = false;
int event_id, venue_id;
String venue_name, venue_description, start_time, venue_address, venue_city, venue_state, venue_postal_code, lat, lng, venue_phone, created_at;
venue_id = venueJson.getInt(DatabaseConstants.KEY_REMOTE_ID);
event_id = venueJson.getInt(DatabaseConstants.KEY_EVENT_ID);
venue_name = venueJson.getString(KEY_NAME);
venue_description = venueJson.getString(KEY_DESCRIPTION);
start_time = venueJson.getString(KEY_START_TIME);
venue_address = venueJson.getString(KEY_ADDRESS);
venue_city = venueJson.getString(KEY_CITY);
venue_state = venueJson.getString(KEY_STATE);
venue_postal_code = venueJson.getString(KEY_ZIP);
venue_phone = venueJson.getString(KEY_PHONE);
lat = venueJson.getString(KEY_LAT);
lng = venueJson.getString(KEY_LNG);
created_at = venueJson.getString(DatabaseConstants.KEY_CREATED_AT);
// check to see if this venue already exists
venue_exists = this.venueExists(venue_id);
if (venue_exists == true) {
this.updateVenue(
venue_id,
event_id,
venue_name,
venue_description,
start_time,
venue_address,
venue_city,
venue_state,
venue_postal_code,
lat,
lng,
venue_phone);
} else {
this.createVenue(
venue_id,
event_id,
venue_name,
venue_description,
start_time,
venue_address,
venue_city,
venue_state,
venue_postal_code,
lat,
lng,
venue_phone,
created_at);
}
}
public void parseVenues(JSONArray venuesArray) throws JSONException {
JSONObject venueJson;
for (int i = 0; i < venuesArray.length(); i++) {
// Iterate through each venue
venueJson = venuesArray.getJSONObject(i);
this.parseVenue(venueJson);
}
}
}