Last active
March 13, 2019 14:51
-
-
Save IMoHaMeDHaMdYI/e977d8ab516dab622e5c832dd94a917a to your computer and use it in GitHub Desktop.
BaseAdapter is a RecyclerView adapter that contains the main operations happen within the adapter,
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
interface Displayable { | |
fun getType(): Int | |
} | |
// ---------------------------------- | |
abstract class ViewRenderer<in D : Displayable, VH : RecyclerView.ViewHolder>(val type: Int) { | |
abstract fun bindView(model: D, holder: VH) | |
abstract fun createViewHolder(parent: ViewGroup): VH | |
} | |
// ---------------------------------- | |
abstract class BaseAdapter(private val context: Context, private val dataList: ArrayList<Displayable>) : | |
RecyclerView.Adapter<RecyclerView.ViewHolder>() { | |
private val STATE_START = 0 | |
// This is used when the list is all empty. | |
private val STATE_LOADING_START = 1 | |
// This is used when there is scroll to load more. | |
private val STATE_LOADING = 2 | |
private val STATE_NETWORK_ERROR = 3 | |
private val STATE_EMPTY = 4 | |
// When true indicates that there is no previous error, loading, or empty states. | |
private var mReady = false | |
private val mRendererList = SparseArray<ViewRenderer<Displayable, RecyclerView.ViewHolder>>() | |
private val mState = BehaviorSubject.create<Int>().apply { | |
onNext(STATE_LOADING) | |
} | |
private val compositeDisplayable = CompositeDisposable() | |
init { | |
// TODO see if it actually needs to be reactive because I feel like this is overkilling it | |
compositeDisplayable.add(mState.observeOn(AndroidSchedulers.mainThread()) | |
.subscribe { | |
when (mState.value) { | |
STATE_START -> onLoaded() | |
STATE_LOADING, STATE_LOADING_START -> onLoading() | |
STATE_EMPTY -> onEmpty() | |
STATE_NETWORK_ERROR -> onNetworkError() | |
} | |
} | |
) | |
} | |
fun setState(state: Int) { | |
if (state != STATE_START) mReady = false | |
mState.onNext(state) | |
} | |
private fun onLoading() { | |
dataList.add(object : Displayable { | |
override fun getType(): Int { | |
return if (dataList.size == 0) STATE_LOADING_START | |
else STATE_LOADING | |
} | |
}) | |
} | |
private fun onNetworkError() { | |
dataList.add(object : Displayable { | |
override fun getType(): Int { | |
return STATE_NETWORK_ERROR | |
} | |
}) | |
} | |
private fun onEmpty() { | |
dataList.add(object : Displayable { | |
override fun getType(): Int { | |
return STATE_EMPTY | |
} | |
}) | |
} | |
private fun onLoaded() { | |
if (!mReady) dataList.removeAt(dataList.size - 1) | |
mReady = true | |
} | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { | |
val viewRenderer = mRendererList.get(viewType) | |
viewRenderer?.let { return it.createViewHolder(parent) } | |
throw RuntimeException("Not Supported Item View Type: $viewType") | |
} | |
fun registerRenderer(renderer: ViewRenderer<Displayable, RecyclerView.ViewHolder>) { | |
val type = renderer.type | |
if (mRendererList.get(type) == null) { | |
mRendererList.put(type, renderer) | |
} | |
} | |
override fun getItemCount(): Int { | |
return dataList.size | |
} | |
fun replace(list: ArrayList<Displayable>) { | |
dataList.clear() | |
dataList.addAll(list) | |
notifyDataSetChanged() | |
} | |
fun add(item: Displayable) { | |
dataList.add(item) | |
notifyItemInserted(dataList.size - 1) | |
} | |
fun add(itemList: ArrayList<Displayable>) { | |
val oldSz = itemList.size | |
dataList.addAll(itemList) | |
notifyItemRangeInserted(oldSz, itemList.size) | |
} | |
// Must be called in the lifecycle destroyer function. | |
fun onDestroy() { | |
compositeDisplayable.dispose() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment