SOLID Components are an attempt at following good engineering standards and best practices such as SOLID and DRY where Google neglected to.
And the implementation dependencies below. You can add just the components you need.
dependencies {
implementation "com.mitteloupe.solid:solidapplication:1.0.8"
implementation "com.mitteloupe.solid:solidactivity:1.0.8"
implementation "com.mitteloupe.solid:solidfragment:1.0.7"
implementation "com.mitteloupe.solid:solidservice:1.0.8"
implementation "com.mitteloupe.solid:solidrecyclerview:1.0.5"
}Make your app Application instance extend SolidApplication. To implement functionality, simply override
lifecycleHandlers, configurationChangeHandlers or memoryHandlers, providing relevant handlers.
Use SolidActivity as the parent activity of any activity in your app. Instead of having a BaseActivity, you can now provide common activity code by overriding one or more of the handler lists, providing a list of handlers.
Common use cases can include dependency injection, analytics, logging, setting up of ViewHolders.
A Koin injection handler will look as follows:
class KoinActivityScopeHandler(
private val activity: Activity,
private val currentScope: Scope
) : LifecycleHandler {
override fun onCreate(savedInstanceState: Bundle?) {
currentScope.declare(activity)
}
}Implement an activity using the handler as follows:
class MainActivity : SolidActivity() {
override val lifecycleHandlers = listOf(
KoinActivityScopeHandler(this, currentScope),
...
)
...
}Use SolidFragment as the parent fragment of any fragment in your app. Instead of having a BaseFragment, you can now provide common fragment code by overriding one or more of the handler lists, providing a list of handlers.
Common use cases can include dependency injection, analytics, logging, setting up of ViewHolders.
A Koin injection handler will look as follows:
class KoinFragmentScopeHandler(
private val fragment: Fragment,
private val currentScope: Scope
) : LifecycleHandler {
override fun onCreate(savedInstanceState: Bundle?) {
currentScope.declare(fragment)
}
}Implement an fragment using the handler as follows:
class MainFragment : SolidFragment() {
override val lifecycleHandlers = listOf(
KoinFragmentScopeHandler(this, currentScope),
...
)
...
}SolidService allows composing services instead of inheriting from base services.
As an example, an IntentService using SolidService would look like this:
class SolidIntentService : SolidService() {
override val lifecycleHandlers = listOf(
IntentHandler(this, { intent -> handleIntent(intent) })
)
private fun handleIntent(intent: Intent?) {
...
}
}Instead of setting a RecyclerView.Adapter to your RecyclerView, simply set a SolidAdapter.
SolidAdapter has a few constructor-injected dependencies that define its behaviour:
-
ViewProvider- this will provide child Views for your RecyclerView. A handyInflatedViewProvideris available for simple layout inflation. -
viewHolderProvider - this is a lambda that, given a View, returns a ViewHolder. It is worth noting that when using a
SolidAdapter, ViewHolder do just that. They hold references to Views (commonly obtained by callingfindViewById()). This is their sole responsibility. -
ViewBinder- this will bind a data item to views provided by a ViewHolder. -
itemsSynchronizerProvider - this is a lambda that, given a
RecyclerView.Adapter, returns anItemsSynchronizer. The responsibility ofItemsSynchronizeris to hold the data items and synchronize changes with theRecyclerView.Adapter. If not provided, theSolidAdapteruses a defaultSimpleItemsSynchronizer, which provides most common functionality. -
positionToType - this is a lambda that, given an
ItemsSynchronizerinstance and a position, returns the view type for that position. By default, it always returnsITEM_TYPE_DEFAULT.
Let's take a look at a simple, common RecyclerView.Adapter implementation:
Without SolidAdapter:
class MoodViewHolder(
override val containerView: View
) : RecyclerView.ViewHolder(containerView), LayoutContainer {
val iconView: ImageView = layoutIconView
val titleView: TextView = layoutTitleView
override fun bindData(moodItem: MoodUiModel) {
iconView.setImageDrawable(
AppCompatResources.getDrawable(context, data.iconResourceId)
)
indexView.text = data.title
}
}
class ListItemsAdapter(
private val layoutInflater: LayoutInflater
) : RecyclerView.Adapter<MoodViewHolder>() {
private val listData = mutableListOf<MoodUiModel>()
fun setData(listData: List<MoodUiModel>) {
this.listData.clear()
this.listData.addAll(listData)
notifyDataSetChanged()
}
fun removeItem(position: Int) {
listData.removeAt(position)
notifyItemRemoved(position)
}
fun addItem(position: Int, item: ListItemUiModel) {
listData.add(position, item)
notifyItemInserted(position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoodViewHolder {
val view = layoutInflater.inflate(R.layout.item_mood, parent, false)
return MoodViewHolder(view)
}
override fun getItemCount() = listData.size
override fun onBindViewHolder(holder: ListItemViewHolder, position: Int) {
holder.bindData(listData[position])
}
}
val adapter = ListItemsAdapter(layoutInflater)With SolidAdapter:
class MoodViewProvider(
layoutInflater: LayoutInflater
) : InflatedViewProvider(layoutInflater, R.layout.item_mood)
class MoodViewHolder(
override val containerView: View
) : RecyclerView.ViewHolder(containerView), LayoutContainer {
val iconView: ImageView = layoutIconView
val titleView: TextView = layoutTitleView
}
class MoodViewBinder(
private val context: Context
) : SimpleViewBinder<MoodViewHolder, MoodUiModel>() {
override fun bindView(viewHolder: MoodViewHolder, data: MoodUiModel) {
viewHolder.iconView.setImageDrawable(
AppCompatResources.getDrawable(context, data.iconResourceId)
)
viewHolder.indexView.text = data.title
}
}
val adapter = SolidAdapter(
MoodViewProvider(layoutInflater),
{ view, _ -> MoodViewHolder(view) },
MoodViewBinder(this)
)Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
