I'm new to Android but after reading some tutorials I've managed to write simple app that's using RecyclerView
with Realm data by extending RealmRecyclerViewAdapter
.
I know that default animator needs stable ids so I've added an unique id to my entity. Everything seems to work fine (swipe to delete, adding, all realm operations) except drag and drop. Well, it kind of works meaning that data and ui stay consistent but animation looks weird and I can move items down only by one position.
A picture is worth a thousand words so here it is:
I guess I will also have to paste the code. I've tried to make it as short as possible.
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) {
}
}
I feel so guilty for posting this amount of code...