Skip to content

Instantly share code, notes, and snippets.

@Judas
Last active January 21, 2016 10:29
Show Gist options
  • Save Judas/53b44c3166c4f03348f2 to your computer and use it in GitHub Desktop.
Save Judas/53b44c3166c4f03348f2 to your computer and use it in GitHub Desktop.
Animated Adapter for RecyclerView with model use-case
package com.judas.protos.recyclerview.adapters;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public abstract class AnimatedAdapter<VH extends RecyclerView.ViewHolder, T> extends RecyclerView.Adapter<VH> {
protected List<T> mOldItems;
protected List<T> mNewItems;
// Comparator for notifyItemChanged (return something other than 0 to update the item)
protected Comparator<T> mUpdateComparator;
public void notifyUpdate() {
final List<T> tmpItems = new ArrayList<>(mOldItems);
// 1 - Deleted items must be removed
for (int i = mOldItems.size() - 1; i >= 0; i--) {
if (!mNewItems.contains(mOldItems.get(i))) {
notifyItemRemoved(i);
tmpItems.remove(i);
}
}
// 2 - Added items must be inserted
for (int i = 0; i < mNewItems.size(); i++) {
final T newItem = mNewItems.get(i);
if (!mOldItems.contains(newItem)) {
notifyItemInserted(i);
tmpItems.add(i, newItem);
}
}
// 3 - Items that switched place must be moved
T misplacedItem = getMisplacedItem(tmpItems);
while (misplacedItem != null) {
final int oldPosition = tmpItems.indexOf(misplacedItem);
final int newPosition = mNewItems.indexOf(misplacedItem);
notifyItemMoved(oldPosition, newPosition);
final T misplaced = tmpItems.remove(oldPosition);
tmpItems.add(newPosition, misplaced);
misplacedItem = getMisplacedItem(tmpItems);
}
// 4 - Items with new content must be updated
if (mUpdateComparator != null) {
for (int i = mNewItems.size() - 1; i >= 0; i--) {
final T newItem = mNewItems.get(i);
final T oldItem = tmpItems.get(i);
if (mUpdateComparator.compare(oldItem, newItem) != 0) {
notifyItemChanged(i); // Item at i has changed
}
}
}
}
private T getMisplacedItem(final List<T> tmpItems) {
for (int i = 0; i < tmpItems.size(); i++) {
final T oldItem = tmpItems.get(i);
final int newPosition = mNewItems.indexOf(oldItem);
if (i != newPosition)
return oldItem;
}
return null;
}
}
package com.judas.protos.recyclerview.activities;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SimpleItemAnimator;
import com.judas.protos.recyclerview.R;
import com.judas.protos.recyclerview.adapters.ItemAdapter;
import com.judas.protos.recyclerview.model.Item;
import java.util.ArrayList;
import java.util.List;
public class HomeActivity extends AppCompatActivity {
private ItemAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAdapter = new ItemAdapter();
final RecyclerView rv = (RecyclerView) findViewById(R.id.recycler);
rv.setHasFixedSize(true);
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
rv.setAdapter(mAdapter);
final SimpleItemAnimator animator = new DefaultItemAnimator();
animator.setAddDuration(250);
animator.setRemoveDuration(250);
animator.setMoveDuration(500);
animator.setChangeDuration(500);
rv.setItemAnimator(animator);
}
private void reset() {
final List<Item> items = new ArrayList<>();
items.add(new Item("Item n°1", Color.RED, 1));
items.add(new Item("Item n°2", Color.GREEN, 2));
items.add(new Item("Item n°3", Color.BLUE, 3));
items.add(new Item("Item n°4", Color.YELLOW, 4));
items.add(new Item("Item n°5", Color.CYAN, 5));
mAdapter.update(items, false);
}
private void shuffle() {
final List<Item> items = new ArrayList<>();
items.add(new Item("Item n°6", Color.MAGENTA, 6));
items.add(new Item("Item n°2", Color.GREEN, 2));
items.add(new Item("Item n°3", Color.BLUE, 3));
items.add(new Item("Item n°1 UPDATED", Color.RED, 1));
items.add(new Item("Item n°4", Color.YELLOW, 4));
mAdapter.update(items, true);
}
@Override
protected void onPostResume() {
super.onPostResume();
// Reset to original position
reset();
// Shuffle items after a delay
new Handler().postDelayed(() -> shuffle(), 2000);
}
}
package com.judas.protos.recyclerview.model;
public class Item {
private String mName;
private int mColor;
private int mId;
public Item(final String name, final int color, final int id) {
mName = name;
mColor = color;
mId = id;
}
public void setName(final String name) {
mName = name;
}
public String getName() {
return mName;
}
public int getColor() {
return mColor;
}
@Override
public boolean equals(final Object o) {
if (o == null || !(o instanceof Item))
return false;
return mId == ((Item) o).mId;
}
}
package com.judas.protos.recyclerview.adapters;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.judas.protos.recyclerview.R;
import com.judas.protos.recyclerview.model.Item;
import java.util.ArrayList;
import java.util.List;
public class ItemAdapter extends AnimatedAdapter<ItemViewHolder, Item> {
public ItemAdapter() {
// Set comparator for animation update (compare name fields)
mUpdateComparator = (lhs, rhs) -> lhs.getName().compareTo(rhs.getName());
}
public ItemAdapter(final List<Item> items) {
this();
mNewItems = items;
}
@Override
public ItemViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View v = View.inflate(parent.getContext(), R.layout.item, null);
final ItemViewHolder holder = new ItemViewHolder(v);
v.setOnClickListener(v1 -> Toast.makeText(parent.getContext(), mNewItems.get(holder.getAdapterPosition()).getName(), Toast.LENGTH_SHORT).show());
return holder;
}
@Override
public void onBindViewHolder(final ItemViewHolder holder, final int position) {
holder.bind(getItem(position));
}
@Override
public int getItemCount() {
return mNewItems == null ? 0 : mNewItems.size();
}
private Item getItem(final int position) {
if (mNewItems == null || mNewItems.size() < position)
return null;
return mNewItems.get(position);
}
public void update(final List<Item> newItems, final boolean animated) {
if (mNewItems == null || !animated) {
mNewItems = newItems;
notifyDataSetChanged();
} else {
mOldItems = new ArrayList<>(mNewItems);
mNewItems = newItems;
notifyUpdate();
}
}
}
package com.judas.protos.recyclerview.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.judas.protos.recyclerview.R;
import com.judas.protos.recyclerview.model.Item;
public class ItemViewHolder extends RecyclerView.ViewHolder {
private View mView;
public ItemViewHolder(final View v) {
super(v);
mView = v;
}
public void bind(final Item item) {
((TextView) mView.findViewById(R.id.item_text)).setText(item.getName());
mView.findViewById(R.id.item_avatar).setBackgroundColor(item.getColor());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment