使用基于Realm的适配器拖放RecyclerView

时间:2016-11-09 01:16:08

标签: android android-recyclerview realm

我是Android新手,但在阅读了一些教程之后,我设法通过扩展RecyclerView来编写使用RealmRecyclerViewAdapter和Realm数据的简单应用。
我知道默认动画师需要稳定的ID,所以我为我的实体添加了一个唯一的id。除了拖放之外,一切似乎都能正常工作(滑动删除,添加,所有领域操作)。好吧,它的工作意味着数据和ui保持一致,但动画看起来很奇怪,我只能将项目向下移动一个位置。
一张图片胜过千言万语,所以这里是:
enter image description here

我想我也必须粘贴代码。我试图让它尽可能短。

public class MainActivity extends AppCompatActivity implements OnStartDragListener {
    private ItemTouchHelper touchHelper;
    private Realm realm;
    private TestRecyclerViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Irrelevant init stuff stripped

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));

        adapter = new TestRecyclerViewAdapter(this, realm.where(Item.class)
                                                         .findAll()
                                                         .sort(Item.INDEX));
        recyclerView.setAdapter(adapter);

        ItemTouchHelper.Callback callback = new ItemTouchHelperCallback(adapter);
        touchHelper = new ItemTouchHelper(callback);
        touchHelper.attachToRecyclerView(recyclerView);
    }

    @Override
    public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
        touchHelper.startDrag(viewHolder);
    }

    // not sure how this method should be implemented
    public void moveItem(Item item, final int fromPosition, final int toPosition) {
        final int index = item.index;
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                Item item = realm.where(Item.class).equalTo(Item.INDEX, index).findFirst();
                if (fromPosition < toPosition) {
                    RealmResults<Item> results = realm.where(Item.class)
                                                      .greaterThan(Item.INDEX, fromPosition)
                                                      .lessThanOrEqualTo(Item.INDEX, toPosition)
                                                      .findAll();
                    for (int i = 0; i < results.size(); i++) {
                        results.get(i).index -= 1;
                    }
                } else {
                    RealmResults<Item> results = realm.where(Item.class)
                                                      .greaterThanOrEqualTo(Item.INDEX, toPosition)
                                                      .lessThan(Item.INDEX, fromPosition)
                                                      .findAll();
                    for (int i = 0; i < results.size(); i++) {
                        results.get(i).index += 1;
                    }
                }
                item.index = toPosition;
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        realm.close();
    }
}

public class Item extends RealmObject {
    public static final String INDEX = "index";
    public static final String TEXT = "text";
    public static final String ID = "id";

    @PrimaryKey
    public long id;
    @Index
    public int index;
    public String text;
}

interface OnStartDragListener {
    void onStartDrag(RecyclerView.ViewHolder viewHolder);
}

class TestRecyclerViewAdapter extends RealmRecyclerViewAdapter<Item,
        TestRecyclerViewAdapter.EditInfoViewHolder> implements ItemTouchHelperAdapter {

    private final static String TAG = TestRecyclerViewAdapter.class.getName();
    private final MainActivity activity;

    public TestRecyclerViewAdapter(@NonNull MainActivity activity, @Nullable
            OrderedRealmCollection<Item> data) {
        super(activity, data, true);
        this.activity = activity;
        setHasStableIds(true);
    }

    @Override
    public long getItemId(int index) {
        return getData().get(index).id;
    }

    @Override
    public EditInfoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.item, parent, false);
        return new EditInfoViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final EditInfoViewHolder holder, int position) {
        Item obj = getData().get(position);
        holder.data = obj;
        holder.text.setText(obj.text);
        holder.handle.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
                    activity.onStartDrag(holder);
                }
                return false;
            }
        });
    }

    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        activity.moveItem(getData().get(fromPosition), fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    class EditInfoViewHolder extends RecyclerView.ViewHolder {
        final TextView text;
        final ImageView handle;

        public Item data;
        public EditInfoViewHolder(View itemView) {
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.text);
            handle = (ImageView) itemView.findViewById(R.id.handle);
        }

    }
}

interface ItemTouchHelperAdapter {
    boolean onItemMove(int fromPosition, int toPosition);
}

class ItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private final ItemTouchHelperAdapter adapter;

    public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
        this.adapter = adapter;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

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

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = 0;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
        if (source.getItemViewType() != target.getItemViewType()) {
            return false;
        }
        adapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

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

我对发布这么多代码感到非常内疚......

1 个答案:

答案 0 :(得分:1)

您的问题源于RealmRecyclerViewAdapter注册调用RealmChangeListener的{​​{1}},这会覆盖所有RecyclerView动画。

可能的解决方法是使用常规的RecyclerViewAdapter,当您使用UI线程事务操作Realm的元素时,您force a refresh immediately afterwards

adapter.notifyDataSetChanged()

class TestRecyclerViewAdapter extends RecyclerView.Adapter<...> { @Override public boolean onItemMove(int fromPosition, int toPosition) { activity.moveItem(getData().get(fromPosition), fromPosition, toPosition); // <-- refresh in there notifyItemMoved(fromPosition, toPosition); return true; } public void moveItem(Item item, final int fromPosition, final int toPosition) { final int index = item.index; realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { Item item = realm.where(Item.class).equalTo(Item.INDEX, index).findFirst(); if (fromPosition < toPosition) { RealmResults<Item> results = realm.where(Item.class) .greaterThan(Item.INDEX, fromPosition) .lessThanOrEqualTo(Item.INDEX, toPosition) .findAll(); for (int i = 0; i < results.size(); i++) { results.get(i).index -= 1; } } else { RealmResults<Item> results = realm.where(Item.class) .greaterThanOrEqualTo(Item.INDEX, toPosition) .lessThan(Item.INDEX, fromPosition) .findAll(); for (int i = 0; i < results.size(); i++) { results.get(i).index += 1; } } item.index = toPosition; } }); RealmRefresh.refreshRealm(realm); } 将强制所有异步查询同步执行,并且您也不会从其他地方执行的事务(如后台线程)中获得自动刷新。但我认为它会起作用!