データベースに接続されたカスタム CursorAdapter を持つ ListFragment があります。ListHeader を含むデータベース内の項目が ListView に表示されず、代わりに糸車だけが表示されます。
以前、ListFragment の SimpleCursorAdapter でアプリが正常に動作していました。空でないデータベースでこれをテストしました。これが機能しない理由について私が持っている唯一の手がかりは、ContextActionMenu で [すべて削除] オプションを試したときに、LogCat で次のエラーが発生することです。
ViewRootImpl sendUserActionEvent() == null
リストフラグメントは次のとおりです。
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.ActionMode.Callback;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import info.mariegrap.dancejournal.R.id;
import info.mariegrap.database.DanceContentProvider;
import info.mariegrap.database.DanceDatabaseHelper;
import info.mariegrap.database.StyleCursorAdapter;
import info.mariegrap.database.StyleTable;
import info.mariegrap.model.Style;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.database.Cursor;
import android.database.SQLException;
public class StyleFragment extends SherlockListFragment implements LoaderManager.LoaderCallbacks<Cursor>{
private static final int LOADER_ID = 0;
private Context context;
private OnStyleSelectedListener callback;
private StyleCursorAdapter styleAdapter;
private DanceDatabaseHelper myDbHelper;
private Callback mActionModeCallback;
private ActionMode mActionMode;
private long selectedId;
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
context = this.getActivity();
setHasOptionsMenu(true);
//setRetainInstance(true);
this.getListView().setBackgroundColor(getResources().getColor(R.color.style_background));
//this.getListView().setDividerHeight(0);
this.getListView().setScrollingCacheEnabled(true);
this.getListView().addHeaderView(new ListHeader(context,
this.getResources().getString(R.string.list_header_style_title),
this.getResources().getString(R.string.list_header_style_subtitle)));
this.setActionModeCallback();
this.setLongClickListener();
/*myDbHelper = new DanceDatabaseHelper(this.context, null,
null, 1);
try {
myDbHelper.openDataBase();;
}catch(SQLException sqle){
throw sqle;
}*/
this.styleAdapter = new StyleCursorAdapter(context, null, 0);
getLoaderManager().initLoader(LOADER_ID, null, this);
this.getListView().setAdapter(this.styleAdapter);
Log.d("mgrap", "Adapter: " + styleAdapter);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
callback = (OnStyleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnStyleSelectedListener");
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id){
if (position == 0){
return;
}
this.setListAdapter(null);
this.getListView().getChildAt(position);
callback.onStyleSelected(position);
}
public interface OnStyleSelectedListener {
/** Called by StyleFragment when a list item is selected */
public void onStyleSelected(int position);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.style_actionbar, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.style_add:
ContentValues cv = new ContentValues();
cv.put(StyleTable.STYLE_KEY_NAME, "Tada!");
Uri uri = Uri.parse("content://info.mariegrap.dancejournal.provider/style_table/0");
Uri idUri = context.getContentResolver().insert(uri, cv);
//styleAdapter.notifyDataSetChanged();
fillData();
return true;
case R.id.style_delete_all:
Uri delete_uri = Uri.parse("content://info.mariegrap.dancejournal.provider/style_table");
context.getContentResolver().delete(delete_uri, null, null);
//styleAdapter.notifyDataSetChanged();
fillData();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
protected void fillData() {
this.getSherlockActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
this.getListView().setAdapter(styleAdapter);
}
protected void deleteStyle(long id) {
Uri uri = Uri.parse("content://info.mariegrap.dancejournal.provider/style_table/" + id);
this.getSherlockActivity().getContentResolver().delete(uri, null, null);
//styleAdapter.notifyDataSetChanged();
fillData();
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { StyleTable.STYLE_KEY_ID, StyleTable.STYLE_KEY_NAME };
return new CursorLoader(this.context,
DanceContentProvider.CONTENT_URI_STYLE, projection, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
styleAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
styleAdapter.swapCursor(null);
}
private void setActionModeCallback(){
mActionModeCallback = new Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.style_context_menu, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.style_delete:
deleteStyle(selectedId);
mode.finish();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
}
private void setLongClickListener(){
this.getListView().setOnItemLongClickListener (new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (mActionMode != null || position == 0) {
return false;
}
mActionMode = getSherlockActivity().startActionMode(mActionModeCallback);
selectedId = id;
return true;
}
});
}
}
カスタム CursorAdapter は次のとおりです。
import view.StyleListItem;
import info.mariegrap.model.Style;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
public class StyleCursorAdapter extends CursorAdapter {
public StyleCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
@Override
public void bindView(View arg0, Context arg1, Cursor arg2) {
int id = arg2.getInt(StyleTable.STYLE_COL_ID);
String name = arg2.getString(StyleTable.STYLE_COL_NAME);
Style style = new Style(name, id);
StyleListItem listItem = (StyleListItem) arg0;
listItem.setStyle(style);
}
@Override
public View newView(Context arg0, Cursor arg1, ViewGroup arg2) {
int id = arg1.getInt(StyleTable.STYLE_COL_ID);
String name = arg1.getString(StyleTable.STYLE_COL_NAME);
Style style = new Style(name, id);
StyleListItem listItem = new StyleListItem(arg0, style);
listItem.setStyle(style);
Log.d("mgrap", "Adapter view: " + listItem.getStyleId());
return listItem;
}
}
ListItem ビューは次のとおりです。
public class StyleListItem extends LinearLayout {
private TextView styleView;
private Style style;
public StyleListItem(Context context, Style style) {
super(context);
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.style_row, this, true);
styleView = (TextView)findViewById(R.id.style_label);
Log.d("mgrap", "Style View: " + styleView);
setStyle(style);
}
public void setStyle(Style style) {
this.style = style;
displayStyle(this.style);
}
public Style getStyle(){
return style;
}
private void displayStyle(Style style) {
if (style != null) {
styleView.setText(style.getName());
}
}
public int getStyleId() {
return styleView.getId();
}
}
リスト項目のレイアウト xml は次のとおりです。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="@+id/icon"
android:layout_width="30dp"
android:layout_height="24dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:src="@drawable/ic_launcher" >
</ImageView>
<TextView
android:id="@+id/style_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:lines="1"
android:text="@+id/TextView01"
android:textSize="24sp"
>
</TextView>
</LinearLayout>
リスト ヘッダーの xml レイアウトは次のとおりです。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/list_header_background_color"
android:layout_marginBottom="5dip" >
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
android:paddingTop="5dip"
android:paddingLeft="10dip"
android:paddingBottom="13dip"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true">
<TextView
android:id="@+id/listview_header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/list_header_style_title"
android:textSize="17sp"
android:textStyle="bold"
android:textColor="@color/list_header_title_color"
/>
<TextView
android:id="@+id/listview_header_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/list_header_style_subtitle"
android:textSize="13sp"
android:textColor="@color/list_header_subtitle_color"
/>
</LinearLayout>
<ImageView
android:id="@+id/list_header_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentRight="true"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="13dp"
android:visibility="gone"
android:contentDescription="@string/image_desc"
/>
</RelativeLayout>
スタイル データベース テーブルを定義するクラスは次のとおりです。
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class StyleTable {
/** Style table in the database. */
public static final String TABLE_STYLE = "style_table";
/** Style table column names and IDs for database access. */
public static final String STYLE_KEY_ID = "_id";
public static final int STYLE_COL_ID = 0;
public static final String STYLE_KEY_NAME = "name";
public static final int STYLE_COL_NAME = STYLE_COL_ID + 1;
/** SQLite database creation statement. Auto-increments IDs of inserted
* styles. Style IDs are set after insertion into the database. */
public static final String DATABASE_CREATE = "create table " +
TABLE_STYLE + " (" +
STYLE_KEY_ID + " integer primary key autoincrement, " +
STYLE_KEY_NAME + " text);";
/** SQLite database table removal statement. Only used if upgrading
* database. */
public static final String DATABASE_DROP = "drop table if exists " +
TABLE_STYLE;
/**
* Initializes the database.
*
* @param database
* The database to initialize.
*/
public static void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
/**
* Upgrades the database to a new version.
*
* @param database
* The database to upgrade.
* @param oldVersion
* The old version of the database.
* @param newVersion
* The new version of the database.
*/
public static void onUpgrade(SQLiteDatabase database, int oldVersion,
int newVersion)
{
Log.w("mgrap", "updating database...");
database.execSQL(DATABASE_DROP);
onCreate(database);
}
}