-
-
Save Kishanjvaghela/67c42f8f32efaa2fadb682bc980e9280 to your computer and use it in GitHub Desktop.
import android.databinding.DataBindingUtil; | |
import android.support.v7.widget.RecyclerView; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import com.app.wanna.android.R; | |
import com.app.wanna.android.data.User; | |
import com.app.wanna.android.databinding.LayoutItemPeopleBinding; | |
import com.app.wanna.android.utils.firebaseadapter.FirebaseRecyclerAdapter; | |
import com.firebase.ui.common.ChangeEventType; | |
import com.firebase.ui.database.FirebaseRecyclerOptions; | |
import com.google.firebase.database.DataSnapshot; | |
import com.squareup.picasso.Picasso; | |
public class ExamplePeopleAdapter extends FirebaseRecyclerAdapter<User, PeopleListAdapter.PeopleViewHolder> { | |
private RecycleItemClick recycleItemClick; | |
private static final String TAG = "PeopleListAdapter"; | |
public ExamplePeopleAdapter(FirebaseRecyclerOptions<User> options) { | |
super(options, true); | |
} | |
public interface RecycleItemClick { | |
void onItemClick(String userId, User user, int position); | |
} | |
public void setRecycleItemClick(RecycleItemClick recycleItemClick) { | |
this.recycleItemClick = recycleItemClick; | |
} | |
@Override | |
public PeopleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | |
View view = LayoutInflater.from(parent.getContext()) | |
.inflate(R.layout.layout_item_people, parent, false); | |
return new PeopleViewHolder(view); | |
} | |
@Override | |
protected void onBindViewHolder(PeopleViewHolder holder, int position, User model) { | |
holder.bind(model); | |
} | |
@Override | |
protected void onChildUpdate(User model, | |
ChangeEventType type, | |
DataSnapshot snapshot, | |
int newIndex, | |
int oldIndex) { | |
model.setUserId(snapshot.getKey()); | |
super.onChildUpdate(model, type, snapshot, newIndex, oldIndex); | |
} | |
@Override | |
protected boolean filterCondition(User model, String filterPattern) { | |
return model.getFirstName().toLowerCase().contains(filterPattern) || | |
model.getLastName().toLowerCase().contains(filterPattern); | |
} | |
public class PeopleViewHolder extends RecyclerView.ViewHolder { | |
LayoutItemPeopleBinding mBinding; | |
PeopleViewHolder(View view) { | |
super(view); | |
mBinding = DataBindingUtil.bind(view); | |
} | |
public void bind(User user) { | |
Picasso.with(mBinding.peopleImage.getContext()) | |
.load(user.getImage()) | |
.placeholder(R.drawable.place_holder_user) | |
.into(mBinding.peopleImage); | |
mBinding.peopleName.setText(String.format("%s %s", user.getFirstName(), user.getLastName())); | |
itemView.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
int pos = getAdapterPosition(); | |
User user = getItem(pos); | |
recycleItemClick.onItemClick(user.getUserId(), user, pos); | |
} | |
}); | |
} | |
} | |
} |
import android.arch.lifecycle.LifecycleObserver; | |
import android.support.annotation.RestrictTo; | |
import com.firebase.ui.database.ChangeEventListener; | |
import com.firebase.ui.database.FirebaseArray; | |
import com.firebase.ui.database.ObservableSnapshotArray; | |
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) | |
interface FirebaseAdapter<T> extends ChangeEventListener, LifecycleObserver { | |
/** | |
* If you need to do some setup before the adapter starts listening for change events in the | |
* database, do so it here and then call {@code super.startListening()}. | |
*/ | |
void startListening(); | |
/** | |
* Removes listeners and clears all items in the backing {@link FirebaseArray}. | |
*/ | |
void stopListening(); | |
ObservableSnapshotArray<T> getSnapshots(); | |
T getItem(int position); | |
} |
import android.arch.lifecycle.Lifecycle; | |
import android.arch.lifecycle.LifecycleOwner; | |
import android.arch.lifecycle.OnLifecycleEvent; | |
import android.support.v7.widget.RecyclerView; | |
import android.util.Log; | |
import android.widget.Filter; | |
import android.widget.Filterable; | |
import com.firebase.ui.common.ChangeEventType; | |
import com.firebase.ui.database.FirebaseRecyclerOptions; | |
import com.firebase.ui.database.ObservableSnapshotArray; | |
import com.google.firebase.database.DataSnapshot; | |
import com.google.firebase.database.DatabaseError; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.List; | |
/** | |
* This class is a generic way of backing a {@link RecyclerView} with a Firebase location. It | |
* handles all of the child events at the given Firebase location and marshals received data into | |
* the given class type. | |
* <p> | |
* See the <a href="https://github.com/firebase/FirebaseUI-Android/blob/master/database/README.md">README</a> | |
* for an in-depth tutorial on how to set up the FirebaseRecyclerAdapter. | |
* | |
* @param <T> The Java class that maps to the type of objects stored in the Firebase location. | |
* @param <VH> The {@link RecyclerView.ViewHolder} class that contains the Views in the layout that | |
* is shown for each object. | |
*/ | |
public abstract class FirebaseRecyclerAdapter<T, VH extends RecyclerView.ViewHolder> | |
extends RecyclerView.Adapter<VH> implements FirebaseAdapter<T>, Filterable { | |
private static final String TAG = "FirebaseRecyclerAdapter"; | |
private final ObservableSnapshotArray<T> mSnapshots; | |
private final List<T> list, backupList; | |
private CustomFilter mCustomFilter; | |
private boolean isFiltarable; | |
/** | |
* Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See | |
* {@link FirebaseRecyclerOptions} for configuration options. | |
*/ | |
public FirebaseRecyclerAdapter(FirebaseRecyclerOptions<T> options, boolean isFiltarable) { | |
mSnapshots = options.getSnapshots(); | |
list = new ArrayList<>(); | |
backupList = new ArrayList<>(); | |
if (options.getOwner() != null) { | |
options.getOwner().getLifecycle().addObserver(this); | |
} | |
this.isFiltarable = isFiltarable; | |
} | |
@OnLifecycleEvent(Lifecycle.Event.ON_START) | |
public void startListening() { | |
if (!mSnapshots.isListening(this)) { | |
mSnapshots.addChangeEventListener(this); | |
} | |
} | |
@OnLifecycleEvent(Lifecycle.Event.ON_STOP) | |
public void stopListening() { | |
mSnapshots.removeChangeEventListener(this); | |
notifyDataSetChanged(); | |
} | |
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) | |
void cleanup(LifecycleOwner source) { | |
source.getLifecycle().removeObserver(this); | |
} | |
@Override | |
public void onChildChanged(ChangeEventType type, | |
DataSnapshot snapshot, | |
int newIndex, | |
int oldIndex) { | |
T model = mSnapshots.get(newIndex); | |
onChildUpdate(model, type, snapshot, newIndex, oldIndex); | |
} | |
protected void onChildUpdate(T model, ChangeEventType type, | |
DataSnapshot snapshot, | |
int newIndex, | |
int oldIndex) { | |
switch (type) { | |
case ADDED: | |
addItem(snapshot.getKey(), model); | |
notifyItemInserted(newIndex); | |
break; | |
case CHANGED: | |
addItem(snapshot.getKey(), model, newIndex); | |
notifyItemChanged(newIndex); | |
break; | |
case REMOVED: | |
removeItem(newIndex); | |
notifyItemRemoved(newIndex); | |
break; | |
case MOVED: | |
moveItem(snapshot.getKey(), model, newIndex, oldIndex); | |
notifyItemMoved(oldIndex, newIndex); | |
break; | |
default: | |
throw new IllegalStateException("Incomplete case statement"); | |
} | |
} | |
private void moveItem(String key, T t, int newIndex, int oldIndex) { | |
list.remove(oldIndex); | |
list.add(newIndex, t); | |
if (isFiltarable) { | |
backupList.remove(oldIndex); | |
backupList.add(newIndex, t); | |
} | |
} | |
private void removeItem(int newIndex) { | |
list.remove(newIndex); | |
if (isFiltarable) | |
backupList.remove(newIndex); | |
} | |
private void addItem(String key, T t, int newIndex) { | |
list.remove(newIndex); | |
list.add(newIndex, t); | |
if (isFiltarable) { | |
backupList.remove(newIndex); | |
backupList.add(newIndex, t); | |
} | |
} | |
private void addItem(String id, T t) { | |
list.add(t); | |
if (isFiltarable) | |
backupList.add(t); | |
} | |
@Override | |
public void onDataChanged() { | |
} | |
@Override | |
public void onError(DatabaseError error) { | |
Log.w(TAG, error.toException()); | |
} | |
@Override | |
public ObservableSnapshotArray<T> getSnapshots() { | |
return mSnapshots; | |
} | |
@Override | |
public T getItem(int position) { | |
return list.get(position); | |
} | |
@Override | |
public int getItemCount() { | |
return list.size(); | |
} | |
@Override | |
public void onBindViewHolder(VH holder, int position) { | |
onBindViewHolder(holder, position, getItem(position)); | |
} | |
/** | |
* @param model the model object containing the data that should be used to populate the view. | |
* @see #onBindViewHolder(RecyclerView.ViewHolder, int) | |
*/ | |
protected abstract void onBindViewHolder(VH holder, int position, T model); | |
/** | |
* filter condition for Filter | |
* | |
* @param model model T | |
* @param filterPattern filter pattern with Lower Case | |
*/ | |
protected boolean filterCondition(T model, String filterPattern) { | |
return true; | |
} | |
@Override | |
public Filter getFilter() { | |
if (mCustomFilter == null) { | |
mCustomFilter = new CustomFilter(); | |
} | |
return mCustomFilter; | |
} | |
public class CustomFilter extends Filter { | |
@Override | |
protected FilterResults performFiltering(CharSequence constraint) { | |
final FilterResults results = new FilterResults(); | |
if (constraint.length() == 0) { | |
results.values = backupList; | |
results.count = backupList.size(); | |
} else { | |
List<T> filteredList = new ArrayList<>(); | |
final String filterPattern = constraint.toString().toLowerCase().trim(); | |
for (T t : backupList) { | |
if (filterCondition(t, filterPattern)) { | |
filteredList.add(t); | |
} | |
} | |
results.values = filteredList; | |
results.count = filteredList.size(); | |
} | |
return results; | |
} | |
@Override | |
protected void publishResults(CharSequence constraint, FilterResults results) { | |
list.clear(); | |
list.addAll((Collection<? extends T>) results.values); | |
notifyDataSetChanged(); | |
} | |
} | |
} |
I also have the same error as @essowe-agnek on the onChildChanged method when i remove an item from the recyclerView.
The error log shows me that:
Process: io.beanario.ocho_sidekick, PID: 22924
java.lang.ArrayIndexOutOfBoundsException: length=33; index=-1
at java.util.ArrayList.remove(ArrayList.java:506)
The error is pointed
private void removeItem(int newIndex) {
>>>> list.remove(newIndex); <<<<Error pointed
if (isFiltarable)
backupList.remove(newIndex);
}
Help me, please!
@vito8916 have you tried using my solution above? and also can you change list.remove(newIndex)
to list.remove(oldIndex)
?
Thank you, you made my day sir
Items get doubled when I minimizing the app, so add list.clear()
like this:-
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
itemsList.getSnapshots().removeChangeEventListener(this);
list.clear();
notifyDataSetChanged();
}
@Akash-Shukla123 I am just curious about it. 'list.clear()
' should be on ON_STOP
or ON_DESTROY
Items get doubled when I minimizing the app, so add list.clear()
like this:-
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
itemsList.getSnapshots().removeChangeEventListener(this);
list.clear();
notifyDataSetChanged();
}
@janglapuk, @yagneshshinde This may work for you. Thanks a lot @Akash-Shukla123
I added
public void startListening() {
if (list.size() > 0) list.clear(); // Add this
if (!mSnapshots.isListening(this)) {
mSnapshots.addChangeEventListener(this);
}
}
and also
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
itemsList.getSnapshots().removeChangeEventListener(this);
list.clear(); // add this
notifyDataSetChanged();
}
but it still duplicated everytime the editText was cleared.
and then I added
backupList.clear()
with list.clear()
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
mSnapshots.removeChangeEventListener(this);
list.clear();
backupList.clear(); //add this
notifyDataSetChanged();
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void startListening() {
if (list.size() > 0) list.clear();
if (backupList.size() > 0) backupList.clear(); //add this
if (!mSnapshots.isListening(this)) {
mSnapshots.addChangeEventListener(this);
}
}
and all duplicate problem are gone. this worked for me
@100race THanks :-)
Anyway I also have the same error as @essowe-agnek on the onChildChanged method. I found that the get value in the method modified to this works.
T model = mSnapshots.get(type!=ChangeEventType.REMOVED ? newIndex : oldIndex);
Also I found that the recyclerview is prone to duplicate items. I was able to mitigate it using the band-aid code below.