public class MainActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
setUpRecyclerView();
}
private void setUpRecyclerView() {
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(new TestAdapter());
mRecyclerView.setHasFixedSize(true);
setUpItemTouchHelper();
setUpAnimationDecoratorHelper();
}
private void setUpItemTouchHelper() {
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
Drawable background;
Drawable xMark;
int xMarkMargin;
boolean initiated;
private void init() {
background = new ColorDrawable(Color.WHITE);
xMark = ContextCompat.getDrawable(MainActivity.this, R.drawable.ic_clear_24dp);
xMark.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
xMarkMargin = (int) MainActivity.this.getResources().getDimension(R.dimen.ic_clear_margin);
initiated = true;
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
{
int position = viewHolder.getAdapterPosition();
TestAdapter testAdapter = (TestAdapter)recyclerView.getAdapter();
if (testAdapter.isUndoOn() && testAdapter.isPendingRemoval(position))
{
return 0;
}
return super.getSwipeDirs(recyclerView, viewHolder);
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int swipedPosition = viewHolder.getAdapterPosition();
TestAdapter adapter = (TestAdapter)mRecyclerView.getAdapter();
boolean undoOn = adapter.isUndoOn();
if (undoOn) {
adapter.pendingRemoval(swipedPosition);
} else {
adapter.remove(swipedPosition);
}
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
View itemView = viewHolder.itemView;
if (viewHolder.getAdapterPosition() == -1) {
return;
}
if (!initiated) {
init();
}
background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
background.draw(c);
int itemHeight = itemView.getBottom() - itemView.getTop();
int intrinsicWidth = xMark.getIntrinsicWidth();
int intrinsicHeight = xMark.getIntrinsicWidth();
int xMarkLeft = itemView.getRight() - xMarkMargin - intrinsicWidth;
int xMarkRight = itemView.getRight() - xMarkMargin;
int xMarkTop = itemView.getTop() + (itemHeight - intrinsicHeight)/2;
int xMarkBottom = xMarkTop + intrinsicHeight;
xMark.setBounds(xMarkLeft, xMarkTop, xMarkRight, xMarkBottom);
xMark.draw(c);
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
};
ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
mItemTouchHelper.attachToRecyclerView(mRecyclerView);
}
private void setUpAnimationDecoratorHelper() {
mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
Drawable background;
boolean initiated;
private void init() {
background = new ColorDrawable(Color.RED);
initiated = true;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (!initiated) {
init();
}
if (parent.getItemAnimator().isRunning())
{
View lastViewComingDown = null;
View firstViewComingUp = null;
int left = parent.getHeight();
int right = parent.getWidth();
int top = 0;
int bottom = 0;
int childCount = parent.getLayoutManager().getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getLayoutManager().getChildAt(i);
if (child.getTranslationY() < 0) {
// view is coming down
lastViewComingDown = child;
} else if (child.getTranslationY() > 0) {
// view is coming up
if (firstViewComingUp == null) {
firstViewComingUp = child;
}
}
}
if (lastViewComingDown != null && firstViewComingUp != null) {
// views are coming down AND going up to fill the void
top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
} else if (lastViewComingDown != null) {
// views are going down to fill the void
top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
bottom = lastViewComingDown.getBottom();
} else if (firstViewComingUp != null) {
// views are coming up to fill the void
top = firstViewComingUp.getTop();
bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
}
background.setBounds(left, top, right, bottom);
background.draw(c);
}
super.onDraw(c, parent, state);
}
});
}
class TestAdapter extends RecyclerView.Adapter {
private static final int PENDING_REMOVAL_TIMEOUT = 3000; // 3sec
List<String> items;
List<String> itemsPendingRemoval;
int lastInsertedIndex; // so we can add some more items for testing purposes
boolean undoOn; // is undo on, you can turn it on from the toolbar menu
private Handler handler = new Handler(); // hanlder for running delayed runnables
HashMap<String, Runnable> pendingRunnables = new HashMap<>(); // map of items to pending runnables, so we can cancel a removal if need be
public TestAdapter() {
items = new ArrayList<>();
itemsPendingRemoval = new ArrayList<>();
// let's generate some items
lastInsertedIndex = 15;
// this should give us a couple of screens worth
for (int i=1; i<= lastInsertedIndex; i++) {
items.add("Item " + i);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new TestViewHolder(parent);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
TestViewHolder viewHolder = (TestViewHolder)holder;
final String item = items.get(position);
if (itemsPendingRemoval.contains(item)) {
// we need to show the "undo" state of the row
//viewHolder.itemView.setBackgroundColor(Color.RED);
viewHolder.itemView.setBackgroundColor(Color.WHITE);
viewHolder.titleTextView.setVisibility(View.GONE);
//viewHolder.undoButton.setVisibility(View.VISIBLE);
viewHolder.undoButton.setVisibility(View.GONE);
viewHolder.undoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// user wants to undo the removal, let's cancel the pending task
Runnable pendingRemovalRunnable = pendingRunnables.get(item);
pendingRunnables.remove(item);
if (pendingRemovalRunnable != null) handler.removeCallbacks(pendingRemovalRunnable);
itemsPendingRemoval.remove(item);
// this will rebind the row in "normal" state
notifyItemChanged(items.indexOf(item));
}
});
} else {
// we need to show the "normal" state
viewHolder.itemView.setBackgroundColor(Color.WHITE);
viewHolder.titleTextView.setVisibility(View.VISIBLE);
viewHolder.titleTextView.setText(item);
viewHolder.undoButton.setVisibility(View.GONE);
viewHolder.undoButton.setOnClickListener(null);
}
}
@Override
public int getItemCount() {
return items.size();
}
public void addItems(int howMany){
if (howMany > 0) {
for (int i = lastInsertedIndex + 1; i <= lastInsertedIndex + howMany; i++) {
items.add("Item " + i);
notifyItemInserted(items.size() - 1);
}
lastInsertedIndex = lastInsertedIndex + howMany;
}
}
public void setUndoOn(boolean undoOn) {
this.undoOn = undoOn;
}
public boolean isUndoOn() {
return undoOn;
}
public void pendingRemoval(int position) {
final String item = items.get(position);
if (!itemsPendingRemoval.contains(item)) {
itemsPendingRemoval.add(item);
// this will redraw row in "undo" state
notifyItemChanged(position);
// let's create, store and post a runnable to remove the item
Runnable pendingRemovalRunnable = new Runnable() {
@Override
public void run() {
remove(items.indexOf(item));
}
};
handler.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT);
pendingRunnables.put(item, pendingRemovalRunnable);
}
}
public void remove(int position) {
String item = items.get(position);
if (itemsPendingRemoval.contains(item)) {
itemsPendingRemoval.remove(item);
}
if (items.contains(item)) {
items.remove(position);
notifyItemRemoved(position);
}
}
public boolean isPendingRemoval(int position) {
String item = items.get(position);
return itemsPendingRemoval.contains(item);
}
}
static class TestViewHolder extends RecyclerView.ViewHolder
{
TextView titleTextView;
Button undoButton;
public TestViewHolder(ViewGroup parent)
{
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.row_view, parent, false));
titleTextView = (TextView) itemView.findViewById(R.id.title_text_view);
undoButton = (Button) itemView.findViewById(R.id.undo_button);
}
}
}