在发布这个答案的那一刻,我已经尝试了这个线程中列出的所有方法。CursorWrapper 从性能的角度来看是最有效的,但不幸的是不安全,因为不能保证已关闭项目的位置是稳定的(如果可以从其他来源更改数据,例如通过后台同步)。或者,您可以尝试我的基本光标适配器的简单实现:
/*
* Copyright (C) 2014. Victor Kosenko (http://qip-blog.eu.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// your package here
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import com.google.api.client.util.Sets;
import java.util.Set;
/**
* This is basic implementation of swipable cursor adapter that allows to skip displaying dismissed
* items by replacing them with empty view. This adapter overrides default implementation of
* {@link #getView(int, android.view.View, android.view.ViewGroup)}, so if you have custom
* implementation of this method you should review it according to logic of this adapter.
*
* @author Victor Kosenko
*/
public abstract class BaseSwipableCursorAdapter extends CursorAdapter {
protected static final int VIEW_ITEM_NORMAL = 0;
protected static final int VIEW_ITEM_EMPTY = 1;
protected Set<Long> pendingDismissItems;
protected View emptyView;
protected LayoutInflater inflater;
/**
* If {@code true} all pending items will be removed on cursor swap
*/
protected boolean flushPendingItemsOnSwap = true;
/**
* @see android.widget.CursorAdapter#CursorAdapter(android.content.Context, android.database.Cursor, boolean)
*/
public BaseSwipableCursorAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
init(context);
}
/**
* @see android.widget.CursorAdapter#CursorAdapter(android.content.Context, android.database.Cursor, int)
*/
protected BaseSwipableCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
init(context);
}
/**
* Constructor with {@code null} cursor and enabled autoRequery
*
* @param context The context
*/
protected BaseSwipableCursorAdapter(Context context) {
super(context, null, true);
init(context);
}
/**
* @param context The context
* @param flushPendingItemsOnSwap If {@code true} all pending items will be removed on cursor swap
* @see #BaseSwipableCursorAdapter(android.content.Context)
*/
protected BaseSwipableCursorAdapter(Context context, boolean flushPendingItemsOnSwap) {
super(context, null, true);
init(context);
this.flushPendingItemsOnSwap = flushPendingItemsOnSwap;
}
protected void init(Context context) {
inflater = LayoutInflater.from(context);
pendingDismissItems = Sets.newHashSet();
emptyView = new View(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (!getCursor().moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
if (isPendingDismiss(position)) {
return emptyView;
} else {
return super.getView(position, convertView, parent);
}
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
return pendingDismissItems.contains(getItemId(position)) ? VIEW_ITEM_EMPTY : VIEW_ITEM_NORMAL;
}
/**
* Add item to pending dismiss. This item will be ignored in
* {@link #getView(int, android.view.View, android.view.ViewGroup)} when displaying list of items
*
* @param id Id of item that needs to be added to pending for dismiss
* @return {@code true} if this item already in collection if pending items, {@code false} otherwise
*/
public boolean putPendingDismiss(Long id) {
return pendingDismissItems.add(id);
}
/**
* Confirm that specified item is no longer present in underlying cursor. This method should be
* called after the fact of removing this item from result set of underlying cursor.
* If you're using flushPendingItemsOnSwap flag there is no need to call this method.
*
* @param id Id of item
* @return {@code true} if this item successfully removed from pending to dismiss, {@code false}
* if it's not present in pending items collection
*/
public boolean commitDismiss(Long id) {
return pendingDismissItems.remove(id);
}
/**
* Check if this item should be ignored
*
* @param position Cursor position
* @return {@code true} if this item should be ignored, {@code false} otherwise
*/
public boolean isPendingDismiss(int position) {
return getItemViewType(position) == VIEW_ITEM_EMPTY;
}
public boolean isFlushPendingItemsOnSwap() {
return flushPendingItemsOnSwap;
}
/**
* Automatically flush pending items when calling {@link #swapCursor(android.database.Cursor)}
*
* @param flushPendingItemsOnSwap If {@code true} all pending items will be removed on cursor swap
*/
public void setFlushPendingItemsOnSwap(boolean flushPendingItemsOnSwap) {
this.flushPendingItemsOnSwap = flushPendingItemsOnSwap;
}
@Override
public Cursor swapCursor(Cursor newCursor) {
if (flushPendingItemsOnSwap) {
pendingDismissItems.clear();
}
return super.swapCursor(newCursor);
}
}
它基于HashSet和默认项目 ID (getItemId()),因此性能不应该成为问题,因为 contains() 方法具有 O(1) 时间复杂度,并且实际设置大部分时间将包含零或一个项目。这也取决于番石榴。如果您不使用 Guava,只需替换第 91 行的集合构造即可。
要在您的项目中使用它,您只需扩展此类而不是 CursorAdapter 并在 onDismiss() 中添加几行代码(如果您使用的是 EnhancedListView或类似库):
@Override
public EnhancedListView.Undoable onDismiss(EnhancedListView enhancedListView, int i) {
adapter.putPendingDismiss(id);
adapter.notifyDataSetChanged();
...
}
如果您使用列表分隔符,此解决方案将不起作用(因为此适配器显示空视图而不是关闭的项目)。您应该在项目布局中添加边距以在项目之间设置间距并在项目布局中包含分隔线。
这段代码以后可以更新,所以我把它贴在了github gist上:https ://gist.github.com/q1p/0b95633ab9367fb86785
另外,我想建议您不要像您的示例中那样在主线程中使用 I/O 操作:)