2

我开发了一个待办事项应用程序,并希望拥有类似于 Google Tasks 的功能。这是我的 android 应用程序上的 Google Tasks 任务列表:

Google Tasks 安卓应用的屏幕截图

当我垂直拖放其中一项任务时,我的 Google Tasks Gmail Webapp 会立即将任务拖放到同一位置:

Google Tasks Gmail Web 应用的屏幕截图

这似乎是一个相当大的挑战,因为我没有找到任何代码示例或文档可以准确地解释/解释 Firestore 和 Firestore 回收器适配器。有一些关于 Firebase 实时数据库的代码示例,但它们并没有太大帮助。我无法想象没有人用 Firestore 编写过这种广泛使用的功能。

您是否知道(在 Github 上)发布的任何 android 应用程序代码或确实/解释了这种行为的文档?或者任何可以提供帮助的 Firestore 专家?

4

1 回答 1

2

这是我的解决方案,它允许用户对其 Firestore 集合中的文档进行排序。

我为任何 RecyclerAdapter / ViewHolder 组合创建了一些可重用的类来实现这一点。

首先,让你的 POJO 实现PositionAwareDocument

public interface PositionAwareDocument {
    public Double getPosition();

    public void setPosition(double position);
}
public class Experience implements Serializable, PositionAwareDocument {
    public String company;
    public String timeframe;
    public String jobTitle;
    public String jobDescription;
    public Double position;

    @ServerTimestamp
    public Date added;

    @Override
    public Double getPosition() {
        return position;
    }

    @Override
    public void setPosition(double position) {
        this.position = position;
    }
}

然后扩展FirestoreRecyclerAdapter(来自 FirebaseUI)。

public abstract class IgnoreChangesFirestoreRecyclerAdapter<T, VH extends RecyclerView.ViewHolder> extends FirestoreRecyclerAdapter<T, VH> {
    private boolean mIgnoreChanges = false;

    public IgnoreChangesFirestoreRecyclerAdapter(@NonNull FirestoreRecyclerOptions<T> options) {
        super(options);
    }

    public void setIgnoreChanges(boolean ignoreChanges) {
        mIgnoreChanges = ignoreChanges;
    }

    @Override
    public void onChildChanged(@NonNull ChangeEventType type, @NonNull DocumentSnapshot snapshot, int newIndex, int oldIndex) {
        if (!mIgnoreChanges) {
            super.onChildChanged(type, snapshot, newIndex, oldIndex);
        }
    }
}

创建 RecyclerAdapter 保持不变,只需扩展IgnoreChangesFirestoreRecyclerAdapter

public class ExperienceRecyclerAdapter extends IgnoreChangesFirestoreRecyclerAdapter<Experience, ExperienceViewHolder> {
    public ExperienceRecyclerAdapter(@NonNull FirestoreRecyclerOptions<Experience> options) {
        super(options);
    }

    @Override
    protected void onBindViewHolder(@NonNull ExperienceViewHolder holder, int position, @NonNull Experience model) {
        holder.setExperience(getSnapshots().getSnapshot(position).getId(), model);
    }

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

这是秘方 - ItemTouchHelper.Callback

import android.app.Activity;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

import com.google.firebase.firestore.CollectionReference;

public class FirestoreReorderableItemTouchHelperCallback<T extends PositionAwareDocument> extends ItemTouchHelper.SimpleCallback {
    private final Activity mContext;
    private final IgnoreChangesFirestoreRecyclerAdapter<T, ?> mRecyclerAdapter;
    private final CollectionReference mCollectionReference;

    private int dragFrom = -1;
    private int dragTo = -1;

    public FirestoreReorderableItemTouchHelperCallback(
            Activity context,
            IgnoreChangesFirestoreRecyclerAdapter<T, ?> recyclerAdapter,
            CollectionReference collectionReference) {
        super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0);
        mContext = context;
        mRecyclerAdapter = recyclerAdapter;
        mCollectionReference = collectionReference;
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) {
        if (dragFrom == -1) {
            dragFrom = source.getAdapterPosition();
        }
        dragTo = target.getAdapterPosition();
        mRecyclerAdapter.notifyItemMoved(source.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        if (dragFrom == -1) {
            return;
        }
        final PositionAwareDocument draggedItem = mRecyclerAdapter.getItem(dragFrom);
        final PositionAwareDocument draggedToItem = mRecyclerAdapter.getItem(dragTo);
        final String draggedId = mRecyclerAdapter.getSnapshots().getSnapshot(dragFrom).getId();
        final int itemCount = mRecyclerAdapter.getItemCount();

        mRecyclerAdapter.setIgnoreChanges(true);

        if (dragTo == itemCount - 1) {
            draggedItem.setPosition(draggedToItem.getPosition() + 100);
        } else if (dragTo == 0) {
            draggedItem.setPosition(draggedToItem.getPosition() / 2);
        } else {
            PositionAwareDocument draggedToNext = mRecyclerAdapter.getItem(dragTo > dragFrom ? dragTo + 1 : dragTo - 1);
            draggedItem.setPosition((draggedToItem.getPosition() + draggedToNext.getPosition()) / 2);
        }

        mCollectionReference.document(draggedId).update("position", draggedItem.getPosition()).addOnCompleteListener(mContext, task -> mRecyclerAdapter.setIgnoreChanges(false));
        dragFrom = dragTo = -1;
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {

    }

    @Override
    public boolean isLongPressDragEnabled() {
        return false;
    }

}

其余的一切都很顺利,这是我在我的 RecyclerView 和 Adapter 中制作的地方onCreate

final CollectionReference experienceReference = mResumeFirestore.collection("experience");
final FirestoreRecyclerOptions<Experience> experienceOptions = new FirestoreRecyclerOptions.Builder<Experience>()
        .setQuery(experienceReference.orderBy("position"), Experience.class)
        .build();

mExperienceList.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
mExperienceList.setAdapter(mExperienceAdapter = new ExperienceRecyclerAdapter(experienceOptions) {

    @Override
    public void onDataChanged() {
        super.onDataChanged();
        setupEmptyView(mExperienceList, mExperienceEmpty, getItemCount());
    }
});
mExperienceTouchHelper = new ItemTouchHelper(new FirestoreReorderableItemTouchHelperCallback<>(this, mExperienceAdapter, experienceReference));
mExperienceTouchHelper.attachToRecyclerView(mExperienceList);

如果您有任何问题,请告诉我!

于 2020-01-10T05:48:42.313 回答