【问题标题】:How to persist items position in firestore after vertically drag & drop them on a RecyclerView将项目垂直拖放到 RecyclerView 后,如何在 Firestore 中保留项目位置
【发布时间】:2019-07-09 21:28:33
【问题描述】:

在我垂直拖放待办事项项目后,我尝试保持待办事项列表应用程序的项目位置。我使用 RecyclerView/FirestoreRecylerAdapter/ItemTouchHelper/Firestore。在 ItemTouchHelper.Callback 的 onMove() 方法中,我猜想通过异步调用访问 Firestore。这会导致我的 RecyclerView 中出现不需要的内容更改。想象一下,我有一个包含标题 A、B、C、D 的 4 个项目的列表。现在我将标题为 A 的项目从位置 0(在顶部)拖动到位置 1。就像这个项目 B 的标题也更改为 A。但这仅在 UI 上而不在数据库中。当我更改为另一个活动并返回时,标题再次正确。我想这与异步调用有关。

//part of MainActivity onCreate()
mFirestore = FirebaseFirestore.getInstance();
    FirebaseFirestore.setLoggingEnabled(true);

    FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
            .setPersistenceEnabled(true)
            .build();
    mFirestore.setFirestoreSettings(settings);

    mRecyclerView = findViewById(R.id.recycler_view);
    mUser = FirebaseAuth.getInstance().getCurrentUser();
    if(mUser == null){
        startSignIn();
    }else{
        Query mQuery = mFirestore.collection("todos").whereEqualTo("author", mUser.getUid()).whereEqualTo("done", false).orderBy("position", Query.Direction.ASCENDING);

        FirestoreRecyclerOptions<Todo> options = new FirestoreRecyclerOptions.Builder<Todo>()
                .setQuery(mQuery, Todo.class)
                .build();
        mAdapter = new TodoAdapter(options);

        initRecyclerView();

//ItemTouchHelper.Callback method as part of initRecyclerView()
@Override
        public boolean onMove(final RecyclerView recyclerView, final RecyclerView.ViewHolder source, final RecyclerView.ViewHolder target) {
            if (source.getItemViewType() != target.getItemViewType()) {
                return false;
            }

            int fromPos = source.getAdapterPosition();
            int toPos = target.getAdapterPosition();

            //Change items position when an item was draged & droped vertically downwards
            if(toPos > fromPos) {
                DocumentReference docTo = mAdapter.getSnapshots().getSnapshot(fromPos).getReference();
                docTo.update("position", toPos);
                int current = fromPos+1;
                while(current <= toPos){
                    DocumentReference docCurrent = mAdapter.getSnapshots().getSnapshot(current).getReference();
                    docCurrent.update("position", current-1);
                    current ++;
                }

                //Change items position when an item was draged & droped vertically upwards
            }else if (toPos < fromPos){
                //TODO implement
            } else {
                return false;
            }

            mAdapter.notifyItemMoved(fromPos, toPos);

            return true;
        }

//adapter class
public class TodoAdapter extends FirestoreRecyclerAdapter<Todo, TodoAdapter.TodoHolder> {

private static final String TAG = "DOIT_DEBUG: ";

private OnItemClickListener listener;
private View viewItem;

public TodoAdapter(@NonNull FirestoreRecyclerOptions<Todo> options) {
    super(options);
}

@Override
protected void onBindViewHolder(@NonNull TodoHolder holder, int position, @NonNull Todo model) {
    holder.textViewTitle.setText(model.getTitle());
    holder.textViewDescription.setText(model.getDescription());
}

@NonNull
@Override
public TodoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    viewItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_todo,
            parent, false);
    return new TodoHolder(viewItem);
}

public void deleteItem(final int position) {
    getSnapshots().getSnapshot(position).getReference().delete();
}

public void setDoneItem(int position){
    getSnapshots().getSnapshot(position).getReference().update("done", true);
    viewItem.setEnabled(false);
}

public void setOnItemClickListener(OnItemClickListener listener){
    this.listener = listener;
}

public interface OnItemClickListener{
    void onItemClick (DocumentSnapshot documentSnapshot, int position);
}

public class TodoHolder extends RecyclerView.ViewHolder {
    private TextView textViewTitle;
    private TextView textViewDescription;
    private String documentID;

    public TodoHolder(View itemView) {
        super(itemView);
        textViewTitle = itemView.findViewById(R.id.todo_title);
        textViewDescription = itemView.findViewById(R.id.todo_description);


        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int position = getAdapterPosition();
                if(position != RecyclerView.NO_POSITION && listener != null){
                    listener.onItemClick(getSnapshots().getSnapshot(position), position);
                }
            }
        });
    }

}


//model class
public class Todo {

private String title;
private String description;
private String author;
private boolean done;
private int position;
private Date created;

public Todo(){} // Needed for Firebase

public Todo(String title, String description, String author, boolean done, int position, Date created) {
    this.title = title;
    this.description = description;
    this.author = author;
    this.done = done;
    this.position = position;
    this.created = created;
}

public String getTitle() {
    return title;
}

public String getDescription() {
    return description;
}

public String getAuthor() {
    return author;
}

public boolean getDone() {
    return done;
}

public void setTitle(String title) {
    this.title = title;
}

public void setDescription(String description) {
    this.description = description;
}

public void setAuthor(String author) {
    this.author = author;
}

public void setDone(boolean done) {
    this.done = done;
}

public int getPosition() {
    return position;
}

public void setPosition(int position) {
    this.position = position;
}

@ServerTimestamp
public Date getCreated() {
    return created;
}

public void setCreated(Date created) {
    this.created = created;
}

//数据库结构

集合“待办事项” 文档字段:作者字符串、创建时间戳、描述字符串、完成布尔值、位置编号、标题字符串

我希望拖放位置更改在 Firestore 中保持不变,并且 RecyclerView 仅显示项目的正确移动,而没有其他更改。

【问题讨论】:

    标签: java android google-cloud-firestore android-recyclerview


    【解决方案1】:

    我能够做到这一点的方法是存储位置并在必要时更新它。如果你给每个item一个排名,你只需要在item受到影响的时候写一个item。

    我只有一个重新排序功能,每次移动项目时都会写入新排名,如果我将项目 15 移动到位置 1,我需要更改 1-15 的所有位置。根据功能的频率,您可以变得聪明,例如存储排名并仅在没有其他更改的几秒钟后更新它(以避免用户非常快速地进行 50 次更改并每次更新)。

    这不是最佳的,但通过数据库持久化似乎需要一个专用字段,因此这种方法。

    【讨论】:

    • 你的意思是,我需要先在上面的 onMove() 方法中写入新的项目位置,例如在一个数组中,然后在 Firestore 中的另一个 Java 线程中保留一段时间?示例代码是什么样子的?
    • 但是如果我在拖放后立即想在 Firestore 中保留新位置怎么办?这实际上是我的要求。但这不适用于上面的 onMove() 代码。那么我需要如何更改方法代码呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-25
    • 2013-09-17
    • 2017-06-23
    • 1970-01-01
    • 2019-11-30
    • 2012-01-26
    相关资源
    最近更新 更多