如何通过CursorLoader填充列表视图中的项目

时间:2015-07-04 08:40:41

标签: android listview loader android-cursorloader

由ViewPagerAdapter管理的片段实现了LoaderCallbacks。它查询数据库以获取Cursor,其中包含收藏字段的所有行等于1(true)。当用户在表更新中取消选择favorite-toggle_button favorite字段为0(false)时。我相信然后Loader调用onLoadFinished()来交换游标,因此列表视图应该在没有该特定项目的情况下进行更新。但这只发生在列表中的最后一项(最底部)。当我尝试删除最后一项时 - 它会立即移除并重新出现。我不明白它为什么会这样。

有什么想法?

public class SongsFavouriteFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

...

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_container_listview,container,false);

    mAmdmAdapter = new AmdmAdapter(getActivity(), null, 0, R.layout.song_art_item);
    ListView songsListView = (ListView)v.findViewById(R.id.container_listview);
    songsListView.setAdapter(mAmdmAdapter);
    songsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {

            ...
        }
    });
    return v;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    getLoaderManager().initLoader(LOADER_ID, null, this);
    super.onActivityCreated(savedInstanceState);
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    Uri songsUri = AmdmContract.SongEntry.buildSongJoinArtistUri();
    String[] projection = SONG_COLUMNS;
    String selection = TABLE_NAME + "." + COLUMN_FAVORITE + " = 1 "; 
    return new CursorLoader(getActivity(),
            songsUri,
            projection,
            selection,
            null,
            null);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    mAmdmAdapter.swapCursor(cursor);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mAmdmAdapter.swapCursor(null);
    }
}

和适配器

public class AmdmAdapter extends CursorAdapter {    

public AmdmAdapter (Context context, Cursor c, int flags, int layout) {
    super(context, c, flags);
    this.layout = layout;
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
    View view = LayoutInflater.from(context).inflate(layout, parent, false);
    return view;
}

@Override
public void bindView(View view, Context context, final Cursor cursor) {

    ViewHolder viewHolder = new ViewHolder(view);

...

    if (viewHolder.toggle_song != null) { // here is my favourite toggle button
        viewHolder.toggle_song.setChecked(cursor.getInt(COL_FAVOURITE) == 1);
        viewHolder.toggle_song.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                int favourite = isChecked ? 1 : 0;
                ContentValues value = new ContentValues();
                String where = AmdmContract.SongEntry.TABLE_NAME + "." +
                        AmdmContract.SongEntry._ID + " = " + tableId;
                value.put(AmdmContract.SongEntry.COLUMN_FAVORITE, favourite);
                mContext.getContentResolver()
                        .update(AmdmContract.SongEntry.CONTENT_URI,
                                value, where, null);

                if (songName != null)
                Log.d(DEBUG_TAG, "OnCheckedChanged: id =  " + tableId + ", songName = " + songName +
                        ", favourite =  " + favourite);
            }
            }
        });
    }
}

@Override
public Cursor swapCursor(Cursor newCursor) {
    if (newCursor != null) {
        COl_ID = newCursor.getColumnIndex("_id");
        COL_ARTIST_ID = newCursor.getColumnIndex("artist_id");
        COL_ARTIST_NAME = newCursor.getColumnIndex("artist_name");
        COL_SONG_NAME = newCursor.getColumnIndex("song_name");
        COL_DATE_ADD = newCursor.getColumnIndex("date_add");
        COL_FAVOURITE = newCursor.getColumnIndex("favourite");
        COL_PHOTO = newCursor.getColumnIndex("photo");
    }
    return super.swapCursor(newCursor);
}

    ...
}

更新:

@Override
public int update(
        Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    int rowsUpdated;

    switch (sUriMatcher.match(uri)) {
       ...
        case SONGS: {
            rowsUpdated = db.update(AmdmContract.SongEntry.TABLE_NAME,
                    values, selection, selectionArgs);
            break;
        }
    ...
                default:
                    throw new UnsupportedOperationException("Unknown uri: " + 

uri);
        }
        if (rowsUpdated != 0)
            getContext().getContentResolver().notifyChange(uri, null);

    Log.d(LOG_TAG,"update(): " + rowsUpdated  + " rows updated");
    return rowsUpdated;
}

2 个答案:

答案 0 :(得分:0)

假设您更新数据库的查询是正确的,那么您错过了将所有内容放在一起的难题。 ContentResolverLoaderManager / CursorLoader没有直接关联。 getContentResolver().update不会导致LoaderMananger重新启动并再次查询数据库。您需要注册一个ContentObserver来监听更新表后通知的Uri上的更改。调用onChange时,您必须重新启动LoaderManager

答案 1 :(得分:0)

我的错误是在setOnCheckedChangeListener()之前放置setChecked()。

列表重用项目,因此当光标更新时,旧项目将填充新数据。 setChecked()调用切换按钮,它导致调用已注册已从游标行中删除的onCheckedChangeListener。

当我在setChecked()之前放入setOnCheckedChangeListener()时,一切都按预期工作。 希望它会帮助别人。