使用数据库对 RecyclerView 项目进行排序

Posted

技术标签:

【中文标题】使用数据库对 RecyclerView 项目进行排序【英文标题】:Sorting RecyclerView Items With A DATABASE 【发布时间】:2018-04-08 09:51:02 【问题描述】:

我目前正在开发一款带有“收藏歌曲”标签的音乐应用。 我正在尝试实现一个工具,该工具允许用户使用拖放对存储在数据库中的他们喜欢的歌曲进行排序。

我在这种情况下使用ItemTouchHelper,但我的搜索都没有产生适用于数据库的解决方案。

** 我的列表是从数据库中填充的 **

FavoritesPresenter.class

        ItemTouchHelper.SimpleCallback simpleCallback =
                new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) 

                    @Override
                    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) 
                        mRecyclerView.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
                        return true;
                    

                    @Override
                    public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) 
                        super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);

                    

DataBase.class

int updateOrder(long id, int newPos) 
    ContentValues cv = new ContentValues();
    cv.put(DataBaseOpenHelper.COLUMN_ORDER, newPos);

    String where = DataBaseOpenHelper.COLUMN_ID + "=?";
    String[] whereArgs = String.valueOf(id);

    return getWritable().update(DataBaseOpenHelper.TABLE_NAME_FAVORITE, cv, where, whereArgs);

我已经尝试过这个“解决方案”here,但显然它对我不起作用。有什么建议吗?

【问题讨论】:

【参考方案1】:

使用android recyclerview拖放排序很简单,无需使用第三方库

private final OnStartDragListener mDragStartListener;

public RecyclerListAdapter(OnStartDragListener dragStartListener) 
mDragStartListener = dragStartListener;
// ...
@Override
public void onBindViewHolder(final ItemViewHolder holder, 
int position) 
// ... holder.handleView.setOnTouchListener(new OnTouchListener() 
@Override
public boolean onTouch(View v, MotionEvent event) 
if (MotionEventCompat.getActionMasked(event) == 
MotionEvent.ACTION_DOWN) 
mDragStartListener.onStartDrag(holder);

return false;

);

更多详情请查看下方帖子

https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd

【讨论】:

我想我不够清楚......我试图让用户能够通过拖放对他最喜欢的歌曲进行排序,然后将当前列表保存在数据库中...... 排序列表效果很好。只是它不会保存在数据库中,这意味着在我对列表进行排序并播放歌曲后,会播放上一首歌曲。 从表中删除所有数据,将数据保存在表中的新排序列表中【参考方案2】:

使用本教程(Fazel 提到): https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd

我创建了一个适合我的解决方案。我不知道这是否是正确的方法,也许有人可以改进这个答案。

onItemMove() 和 onMoved() 都被调用,而我仍在拖动项目。 clearView() 只被调用一次。因为我使用 Firestore 作为我的数据库,所以我真的只想在拖动完成后更新项目。

我正在更新类别,所以我有一个扩展 FirestoreAdapter 的 CategoryFirestoreAdapter。我保存在数据库中的排序顺序是从 1(不是 0)开始的。

ItemTouchHelperAdapter

public interface ItemTouchHelperAdapter 

    boolean onItemMove(int fromPosition, int toPosition);

    void onItemDismiss(int position);

    void clearView(); //added this

SimpleItemTouchHelperCallback

@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 

    super.clearView(recyclerView, viewHolder);

    viewHolder.itemView.setAlpha(ALPHA_FULL);

    if (viewHolder instanceof ItemTouchHelperViewHolder) 
        // Tell the view holder it's time to restore the idle state
        ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
        itemViewHolder.onItemClear();
    
    mAdapter.clearView(); //add this so the method gets called in the CategoryFirestoreAdapter

CategoryFirestoreAdapter

public class CategoryFirestoreAdapter
    extends FirestoreAdapter<CategoryFirestoreAdapter.ViewHolder>
    implements ItemTouchHelperAdapter 

@Override
public void clearView() 

    //Update the database here for the items that have moved.

    FirebaseFirestore db = FirebaseFirestore.getInstance();
    ArrayList<DocumentSnapshot> snapshots = getAllItems();

    db.runTransaction(new Transaction.Function<Object>() 

        @Nullable
        @Override
        public Object apply(@NonNull Transaction transaction) 

            for (int i = 0; i < snapshots.size(); i++) 

                DocumentSnapshot snapshot = snapshots.get(i);
                DocumentReference docRef = db.collection("categories").document(snapshot.getId());
                Category category = snapshot.toObject(Category.class);

                Integer sortOrder = category.getSortOrder();
                Integer newSortOrder = i+1;

                if ((i + 1) != sortOrder) 
                    //only update items that have changed.
                    transaction.update(docRef, "sortOrder", newSortOrder); //sortOrder hardcoded - fix this
                
            
            return null;
        
    );
 ....

FirestoreAdapter

protected void onDocumentModified(DocumentChange change) 

//    I changed this as it was moving things around again after updating the db.  
//    I am not sure why, and this may need improving, but this is working for me.

//    Dont handle position changes, just updates.
//    if (change.getOldIndex() == change.getNewIndex()) 
//        // Item changed but remained in same position
//        mSnapshots.set(change.getOldIndex(), change.getDocument());
//        notifyItemChanged(change.getOldIndex());
//     else 
//        // Item changed and changed position
        mSnapshots.remove(change.getOldIndex());
        mSnapshots.add(change.getNewIndex(), change.getDocument());
//        notifyItemMoved(change.getOldIndex(), change.getNewIndex());
        notifyItemChanged(change.getNewIndex()); //added
        notifyItemChanged(change.getOldIndex()); //added
//    


protected ArrayList<DocumentSnapshot> getAllItems() 
    return mSnapshots;

【讨论】:

以上是关于使用数据库对 RecyclerView 项目进行排序的主要内容,如果未能解决你的问题,请参考以下文章

禁用整个RecyclerView的点击次数

如何在android的recyclerview中将最新项目标记为“新”?

RecyclerView的行项目视图隐藏/显示在Recyclerview的滚动上搞砸了

更改recyclerview中最后几个项目的跨度计数

RecyclerView可扩展的cardView

如何使用 AsyncTask 更新 RecyclerView 项目