Created
August 13, 2016 12:09
-
-
Save talosdev/1e6533743b1062aafccfb188494e6c9f 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
package com.listhi.features.discos; | |
import android.support.v7.widget.RecyclerView; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import com.google.firebase.database.Query; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
import java.util.ArrayList; | |
import java.util.List; | |
import app.we.go.firebase.rx.SingleChildEventObservable; | |
import rx.Observable; | |
import rx.functions.Action0; | |
import rx.functions.Func1; | |
/** | |
* Created by Aristides Papadopoulos (github:talosdev). | |
*/ | |
public abstract class FirebaseAdapter<FM, VM, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { | |
private final Class<FM> firebaseModelClass; | |
private final Class<VM> viewModelClass; | |
private final int modelLayout; | |
private final Class<VH> viewHolderClass; | |
private final Query ref; | |
private List<VM> data; | |
/** | |
* @param firebaseModelClass 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 firebaseModelClass. | |
* @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 FirebaseAdapter(Class<FM> firebaseModelClass, Class<VM> viewModelClass, int modelLayout, Class<VH> viewHolderClass, Query ref) { | |
this.firebaseModelClass = firebaseModelClass; | |
this.viewModelClass = viewModelClass; | |
this.modelLayout = modelLayout; | |
this.viewHolderClass = viewHolderClass; | |
this.ref = ref; | |
data = new ArrayList<>(); | |
} | |
@Override | |
public VH onCreateViewHolder(ViewGroup parent, int viewType) { | |
LayoutInflater inflater = LayoutInflater.from(parent.getContext()); | |
View view = inflater.inflate(viewType, parent, false); | |
try { | |
Constructor<VH> constructor = viewHolderClass.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); | |
} | |
} | |
/** | |
* Initialization call that triggers the adapter to load its data. Calls | |
* {@link Observable#doOnCompleted(Action0)} when done. | |
* @return | |
*/ | |
public Observable<Void> loadData() { | |
return loadFirebaseModelData() | |
.flatMap(transformToViewModelData()) | |
.toList() | |
.map(new Func1<List<VM>, Void>() { | |
@Override | |
public Void call(List<VM> vms) { | |
data.addAll(vms); | |
notifyDataSetChanged(); | |
return null; | |
} | |
}); | |
} | |
public List<VM> getData() { | |
return data; | |
} | |
/** | |
* Loads the data from firebase. | |
* @return | |
*/ | |
protected Observable<FM> loadFirebaseModelData() { | |
return new SingleChildEventObservable(ref) | |
.get(firebaseModelClass); | |
} | |
/** | |
* Transforms the firebase model data to view model data. This is to compensate for the cases | |
* when the data fetched by the firebase reference is not complete (due to normalization/duplication) | |
* and therefore more queries must be made. Subclasses can override this method and perform | |
* all the additional data loading necessary. | |
* This implementation will simply try to cast firebase model object to the view model object | |
* and will only work if the view model class is the same as (or assignable from) the firebase | |
* model class. | |
* | |
* @return | |
* | |
*/ | |
protected Func1<FM, Observable<VM>> transformToViewModelData() { | |
return new Func1<FM, Observable<VM>>() { | |
@Override | |
public Observable<VM> call(FM fm) { | |
try { | |
return Observable.just((VM) fm); | |
} catch (ClassCastException e) { | |
throw new RuntimeException("Firebase model class is not castable to View model " + | |
"class. You must provide a transform function.", e); | |
} | |
} | |
}; | |
} | |
@Override | |
public void onBindViewHolder(VH viewHolder, int position) { | |
VM model = getItem(position); | |
populateViewHolder(viewHolder, model, position); | |
} | |
public VM getItem(int position) { | |
return data.get(position); | |
} | |
@Override | |
public int getItemCount() { | |
return data.size(); | |
} | |
@Override | |
public int getItemViewType(int position) { | |
return modelLayout; | |
} | |
/** | |
* 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, VM model, int position); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment