1

我有一个带有连接到数据库的自定义 CursorAdapter 的 ListFragment。数据库中的项目没有出现在 ListView 中,包括 ListHeader,而是我只看到一个旋转的轮子。

之前,我的应用程序使用 ListFragment 中的 SimpleCursorAdapter 运行良好。我已经在非空数据库上对此进行了测试。关于为什么这不起作用的唯一线索是,当我尝试 ContextActionMenu 中的“全部删除”选项时,我在 LogCat 中收到以下错误。

ViewRootImpl sendUserActionEvent() == null

这是 ListFragment:

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);
    }
}
4

1 回答 1

3

如果没有更多的日志记录,很难说,但有几件事会跳出来:

  1. 适配器直接在列表视图上设置,而不是调用setAdapterListFragment。这通常会导致问题。
  2. 您在初始化加载程序后设置列表视图的适配器。这可能不是竞争条件,因为在您的调用完成后可能会在主循环中调用回调,但我不会指望它。
于 2013-07-18T03:54:52.363 回答