Created
June 12, 2016 09:04
-
-
Save ndefeijter/d98eb5f643b5faf5476b8a611de912c1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Firebase UI Bindings Android Library | |
* | |
* Copyright © 2015 Firebase - All Rights Reserved | |
* https://www.firebase.com | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, this | |
* list of conditions and the following disclaimer. | |
* | |
* 2. Redistributions in binaryform must reproduce the above copyright notice, | |
* this list of conditions and the following disclaimer in the documentation | |
* and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
* EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, | |
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
package com.firebase.ui; | |
import android.support.v7.widget.RecyclerView; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import com.firebase.client.DataSnapshot; | |
import com.firebase.client.Firebase; | |
import com.firebase.client.Query; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
/** | |
* This class is a generic way of backing an RecyclerView with a Firebase location. | |
* It handles all of the child events at the given Firebase location. It marshals received data into the given | |
* class type. | |
* | |
* To use this class in your app, subclass it passing in all required parameters and implement the | |
* populateViewHolder method. | |
* | |
* <blockquote><pre> | |
* {@code | |
* private static class ChatMessageViewHolder extends RecyclerView.ViewHolder { | |
* TextView messageText; | |
* TextView nameText; | |
* | |
* public ChatMessageViewHolder(View itemView) { | |
* super(itemView); | |
* nameText = (TextView)itemView.findViewById(android.R.id.text1); | |
* messageText = (TextView) itemView.findViewById(android.R.id.text2); | |
* } | |
* } | |
* | |
* FirebaseRecyclerAdapter<ChatMessage, ChatMessageViewHolder> adapter; | |
* ref = new Firebase("https://<yourapp>.firebaseio.com"); | |
* | |
* RecyclerView recycler = (RecyclerView) findViewById(R.id.messages_recycler); | |
* recycler.setHasFixedSize(true); | |
* recycler.setLayoutManager(new LinearLayoutManager(this)); | |
* | |
* adapter = new FirebaseRecyclerAdapter<ChatMessage, ChatMessageViewHolder>(ChatMessage.class, android.R.layout.two_line_list_item, ChatMessageViewHolder.class, mRef) { | |
* public void populateViewHolder(ChatMessageViewHolder chatMessageViewHolder, ChatMessage chatMessage, int position) { | |
* chatMessageViewHolder.nameText.setText(chatMessage.getName()); | |
* chatMessageViewHolder.messageText.setText(chatMessage.getMessage()); | |
* } | |
* }; | |
* recycler.setAdapter(mAdapter); | |
* } | |
* </pre></blockquote> | |
* | |
* @param <T> The Java class that maps to the type of objects stored in the Firebase location. | |
* @param <VH> The ViewHolder class that contains the Views in the layout that is shown for each object. | |
*/ | |
public abstract class FirebaseRecyclerAdapter2<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { | |
Class<T> mModelClass; | |
protected int mModelLayout; | |
Class<VH> mViewHolderClass; | |
FirebaseArrays mArrays = new FirebaseArrays(); | |
protected boolean notifyPrevious; | |
protected boolean notifyNext; | |
/** | |
* @param modelClass Firebase will marshall the data at a location into an instance of a class that you provide | |
* @param modelLayout This is the layout used to represent a single item in the list. You will be responsible for populating an | |
* instance of the corresponding view with the data from an instance of modelClass. | |
* @param viewHolderClass The class that hold references to all sub-views in an instance modelLayout. | |
*/ | |
public FirebaseRecyclerAdapter2(Class<T> modelClass, int modelLayout, Class<VH> viewHolderClass) { | |
mModelClass = modelClass; | |
mModelLayout = modelLayout; | |
mViewHolderClass = viewHolderClass; | |
mArrays.setOnChangedListener(new FirebaseArray.OnChangedListener() { | |
@Override | |
public void onChanged(EventType type, int index, int oldIndex) { | |
switch (type) { | |
case Added: | |
notifyItemInserted(index); | |
notifyOthersAddedOrChanged(index); | |
break; | |
case Changed: | |
notifyItemChanged(index); | |
notifyOthersAddedOrChanged(index); | |
break; | |
case Removed: | |
notifyItemRemoved(index); | |
notifyOthersRemoved(index); | |
break; | |
case Moved: | |
notifyItemMoved(oldIndex, index); | |
notifyOthersRemoved(oldIndex); | |
notifyOthersAddedOrChanged(index); | |
break; | |
default: | |
throw new IllegalStateException("Incomplete case statement"); | |
} | |
} | |
private void notifyOthersAddedOrChanged(final int index) { | |
if (notifyPrevious && isIndexValid(index - 1)) { | |
notifyItemChanged(index - 1); | |
} | |
if (notifyNext && isIndexValid(index + 1)) { | |
notifyItemChanged(index + 1); | |
} | |
} | |
private void notifyOthersRemoved(final int index) { | |
if (notifyPrevious && isIndexValid(index - 1)) { | |
notifyItemChanged(index - 1); | |
} | |
if (notifyNext && isIndexValid(index)) { | |
notifyItemChanged(index); | |
} | |
} | |
private boolean isIndexValid(final int index) { | |
return index >= 0 && index < getItemCount(); | |
} | |
}); | |
} | |
/** | |
* @param modelClass Firebase will marshall the data at a location into an instance of a class that you provide | |
* @param modelLayout This is the layout used to represent a single item in the list. You will be responsible for populating an | |
* instance of the corresponding view with the data from an instance of modelClass. | |
* @param viewHolderClass The class that hold references to all sub-views in an instance modelLayout. | |
* @param ref The Firebase location to watch for data changes. Can also be a slice of a location, using some | |
* combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code> | |
*/ | |
public FirebaseRecyclerAdapter2(Class<T> modelClass, int modelLayout, Class<VH> viewHolderClass, Query ref) { | |
this(modelClass, modelLayout, viewHolderClass); | |
addQuery(ref); | |
} | |
/** | |
* @param modelClass Firebase will marshall the data at a location into an instance of a class that you provide | |
* @param modelLayout This is the layout used to represent a single item in the list. You will be responsible for populating an | |
* instance of the corresponding view with the data from an instance of modelClass. | |
* @param viewHolderClass The class that hold references to all sub-views in an instance modelLayout. | |
* @param ref The Firebase location to watch for data changes. Can also be a slice of a location, using some | |
* combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code> | |
*/ | |
public FirebaseRecyclerAdapter2(Class<T> modelClass, int modelLayout, Class<VH> viewHolderClass, Firebase ref) { | |
this(modelClass, modelLayout, viewHolderClass, (Query) ref); | |
} | |
public void cleanup() { | |
mArrays.cleanup(); | |
} | |
public void addQuery(final Query ref) { | |
if (null != ref) { | |
mArrays.add(ref); | |
} | |
} | |
public void removeQuery(final Query ref) { | |
if (null != ref) { | |
mArrays.remove(ref); | |
} | |
} | |
@Override | |
public int getItemCount() { | |
return mArrays.getCount(); | |
} | |
public T getItem(int position) { | |
return parseSnapshot(mArrays.getItem(position)); | |
} | |
/** | |
* This method parses the DataSnapshot into the requested type. You can override it in subclasses | |
* to do custom parsing. | |
* | |
* @param snapshot the DataSnapshot to extract the model from | |
* @return the model extracted from the DataSnapshot | |
*/ | |
protected T parseSnapshot(DataSnapshot snapshot) { | |
return snapshot.getValue(mModelClass); | |
} | |
public Firebase getRef(int position) { return mArrays.getItem(position).getRef(); } | |
@Override | |
public long getItemId(int position) { | |
// http://stackoverflow.com/questions/5100071/whats-the-purpose-of-item-ids-in-android-listview-adapter | |
return mArrays.getItem(position).getKey().hashCode(); | |
} | |
@Override | |
public VH onCreateViewHolder(ViewGroup parent, int viewType) { | |
ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false); | |
try { | |
Constructor<VH> constructor = mViewHolderClass.getConstructor(View.class); | |
return constructor.newInstance(view); | |
} catch (NoSuchMethodException e) { | |
throw new RuntimeException(e); | |
} catch (InvocationTargetException e) { | |
throw new RuntimeException(e); | |
} catch (InstantiationException e) { | |
throw new RuntimeException(e); | |
} catch (IllegalAccessException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public void onBindViewHolder(VH viewHolder, int position) { | |
T model = getItem(position); | |
populateViewHolder(viewHolder, model, position); | |
} | |
@Override | |
public int getItemViewType(int position) { | |
return mModelLayout; | |
} | |
/** | |
* Each time the data at the given Firebase location changes, this method will be called for each item that needs | |
* to be displayed. The first two arguments correspond to the mLayout and mModelClass given to the constructor of | |
* this class. The third argument is the item's position in the list. | |
* <p> | |
* Your implementation should populate the view using the data contained in the model. | |
* | |
* @param viewHolder The view to populate | |
* @param model The object containing the data used to populate the view | |
* @param position The position in the list of the view being populated | |
*/ | |
abstract protected void populateViewHolder(VH viewHolder, T model, int position); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment