{"id":22043145,"url":"https://github.com/eranboudjnah/solid","last_synced_at":"2025-05-08T01:23:16.572Z","repository":{"id":57726705,"uuid":"223144820","full_name":"EranBoudjnah/solid","owner":"EranBoudjnah","description":"Solid Android components","archived":false,"fork":false,"pushed_at":"2021-04-12T16:52:34.000Z","size":438,"stargazers_count":35,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-31T16:11:13.444Z","etag":null,"topics":["activity","android","android-library","dependency-injection","dry","fragment","kotlin","recyclerview","solid","solid-components"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EranBoudjnah.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-21T10:09:16.000Z","updated_at":"2024-08-31T12:59:32.000Z","dependencies_parsed_at":"2022-09-26T21:51:00.107Z","dependency_job_id":null,"html_url":"https://github.com/EranBoudjnah/solid","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranBoudjnah%2Fsolid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranBoudjnah%2Fsolid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranBoudjnah%2Fsolid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranBoudjnah%2Fsolid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EranBoudjnah","download_url":"https://codeload.github.com/EranBoudjnah/solid/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252979623,"owners_count":21835091,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activity","android","android-library","dependency-injection","dry","fragment","kotlin","recyclerview","solid","solid-components"],"created_at":"2024-11-30T12:15:11.581Z","updated_at":"2025-05-08T01:23:16.552Z","avatar_url":"https://github.com/EranBoudjnah.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SOLID Components\n\n[![Version - Application](https://img.shields.io/maven-central/v/com.mitteloupe.solid/solidapplication?label=application+|+MavenCentral)](https://mvnrepository.com/artifact/com.mitteloupe.solid/solidapplication)\n\n[![Version - Activity](https://img.shields.io/maven-central/v/com.mitteloupe.solid/solidactivity?label=activity+|+MavenCentral)](https://mvnrepository.com/artifact/com.mitteloupe.solid/solidactivity)\n[![Version - Fragment](https://img.shields.io/maven-central/v/com.mitteloupe.solid/solidfragment?label=fragment+|+MavenCentral)](https://mvnrepository.com/artifact/com.mitteloupe.solid/solidfragment)\n[![Version - Service](https://img.shields.io/maven-central/v/com.mitteloupe.solid/solidservice?label=service+|+MavenCentral)](https://mvnrepository.com/artifact/com.mitteloupe.solid/solidservice)\n\n[![Version - RecyclerView](https://img.shields.io/maven-central/v/com.mitteloupe.solid/solidrecyclerview?label=recyclerview+|+MavenCentral)](https://mvnrepository.com/artifact/com.mitteloupe.solid/solidrecyclerview)\n\n[![Build Status](https://img.shields.io/travis/EranBoudjnah/Solid)](https://travis-ci.com/EranBoudjnah/solid)\n[![License](https://img.shields.io/github/license/EranBoudjnah/Solid)](https://github.com/EranBoudjnah/solid/blob/master/LICENSE)\n[![Platform](https://img.shields.io/badge/platform-android-lightgrey)](https://developer.android.com/reference)\n\nSOLID Components are an attempt at following good engineering standards and best practices such as [SOLID](https://en.wikipedia.org/wiki/SOLID) and [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) where Google neglected to.\n\n![Icon](https://github.com/EranBoudjnah/solid/raw/master/assets/RockSolid.png)\n\n## Installation\n\nAnd the implementation dependencies below. You can add just the components you need.\n\n```groovy\ndependencies {\n    implementation \"com.mitteloupe.solid:solidapplication:1.0.8\"\n\n    implementation \"com.mitteloupe.solid:solidactivity:1.0.8\"\n    implementation \"com.mitteloupe.solid:solidfragment:1.0.7\"\n    implementation \"com.mitteloupe.solid:solidservice:1.0.8\"\n\n    implementation \"com.mitteloupe.solid:solidrecyclerview:1.0.5\"\n}\n```\n\n\n## Usage\n\n### Application\n\nMake your app Application instance extend `SolidApplication`. To implement functionality, simply override\n`lifecycleHandlers`, `configurationChangeHandlers` or `memoryHandlers`, providing relevant handlers.\n\n\n### Activity\n\nUse `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.\n\nCommon use cases can include dependency injection, analytics, logging, setting up of ViewHolders. \n\nA `Koin` injection handler will look as follows:\n\n```kotlin\nclass KoinActivityScopeHandler(\n    private val activity: Activity,\n    private val currentScope: Scope\n) : LifecycleHandler {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        currentScope.declare(activity)\n    }\n}\n```\n\nImplement an activity using the handler as follows:\n\n```kotlin\nclass MainActivity : SolidActivity() {\n    override val lifecycleHandlers = listOf(\n        KoinActivityScopeHandler(this, currentScope),\n        ...\n    )\n\n    ...\n}\n```\n\n### Fragment\n\nUse `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.\n\nCommon use cases can include dependency injection, analytics, logging, setting up of ViewHolders. \n\nA `Koin` injection handler will look as follows:\n\n```kotlin\nclass KoinFragmentScopeHandler(\n    private val fragment: Fragment,\n    private val currentScope: Scope\n) : LifecycleHandler {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        currentScope.declare(fragment)\n    }\n}\n```\n\nImplement an fragment using the handler as follows:\n\n```kotlin\nclass MainFragment : SolidFragment() {\n    override val lifecycleHandlers = listOf(\n        KoinFragmentScopeHandler(this, currentScope),\n        ...\n    )\n\n    ...\n}\n```\n\n### Service\n\n`SolidService` allows composing services instead of inheriting from base services.\n\nAs an example, an `IntentService` using `SolidService` would look like this:\n\n```kotlin\nclass SolidIntentService : SolidService() {\n    override val lifecycleHandlers = listOf(\n        IntentHandler(this, { intent -\u003e handleIntent(intent) })\n    )\n\n    private fun handleIntent(intent: Intent?) {\n        ...\n    }\n}\n```\n\n### Adapter\n\nInstead of setting a `RecyclerView.Adapter` to your RecyclerView, simply set a `SolidAdapter`.\n\n`SolidAdapter` has a few constructor-injected dependencies that define its behaviour:\n\n1. `ViewProvider` - this will provide child Views for your RecyclerView. A handy `InflatedViewProvider` is available for simple layout inflation.\n\n2. 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 calling `findViewById()`). This is their sole responsibility.\n\n3. `ViewBinder` - this will bind a data item to views provided by a ViewHolder.\n\n4. itemsSynchronizerProvider - this is a lambda that, given a `RecyclerView.Adapter`, returns an `ItemsSynchronizer`. The responsibility of `ItemsSynchronizer` is to hold the data items and synchronize changes with the `RecyclerView.Adapter`. If not provided, the `SolidAdapter` uses a default `SimpleItemsSynchronizer`, which provides most common functionality.\n\n5. positionToType - this is a lambda that, given an `ItemsSynchronizer` instance and a position, returns the view type for that position. By default, it always returns `ITEM_TYPE_DEFAULT`.\n\n#### Comparison\n\nLet's take a look at a simple, common RecyclerView.Adapter implementation:\n\nWithout SolidAdapter:\n\n```kotlin\nclass MoodViewHolder(\n    override val containerView: View\n) : RecyclerView.ViewHolder(containerView), LayoutContainer {\n    val iconView: ImageView = layoutIconView\n    val titleView: TextView = layoutTitleView\n\n    override fun bindData(moodItem: MoodUiModel) {\n        iconView.setImageDrawable(\n            AppCompatResources.getDrawable(context, data.iconResourceId)\n        )\n        indexView.text = data.title\n    }\n}\n\nclass ListItemsAdapter(\n    private val layoutInflater: LayoutInflater\n) : RecyclerView.Adapter\u003cMoodViewHolder\u003e() {\n    private val listData = mutableListOf\u003cMoodUiModel\u003e()\n\n    fun setData(listData: List\u003cMoodUiModel\u003e) {\n        this.listData.clear()\n        this.listData.addAll(listData)\n        notifyDataSetChanged()\n    }\n\n    fun removeItem(position: Int) {\n        listData.removeAt(position)\n        notifyItemRemoved(position)\n    }\n\n    fun addItem(position: Int, item: ListItemUiModel) {\n        listData.add(position, item)\n        notifyItemInserted(position)\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoodViewHolder {\n        val view = layoutInflater.inflate(R.layout.item_mood, parent, false)\n        return MoodViewHolder(view)\n    }\n\n    override fun getItemCount() = listData.size\n\n    override fun onBindViewHolder(holder: ListItemViewHolder, position: Int) {\n        holder.bindData(listData[position])\n    }\n}\n\nval adapter = ListItemsAdapter(layoutInflater)\n```\n\nWith SolidAdapter:\n\n```kotlin\nclass MoodViewProvider(\n    layoutInflater: LayoutInflater\n) : InflatedViewProvider(layoutInflater, R.layout.item_mood)\n\nclass MoodViewHolder(\n    override val containerView: View\n) : RecyclerView.ViewHolder(containerView), LayoutContainer {\n    val iconView: ImageView = layoutIconView\n    val titleView: TextView = layoutTitleView\n}\n\nclass MoodViewBinder(\n    private val context: Context\n) : SimpleViewBinder\u003cMoodViewHolder, MoodUiModel\u003e() {\n    override fun bindView(viewHolder: MoodViewHolder, data: MoodUiModel) {\n        viewHolder.iconView.setImageDrawable(\n            AppCompatResources.getDrawable(context, data.iconResourceId)\n        )\n        viewHolder.indexView.text = data.title\n    }\n}\n\nval adapter = SolidAdapter(\n    MoodViewProvider(layoutInflater),\n    { view, _ -\u003e MoodViewHolder(view) },\n    MoodViewBinder(this)\n)\n```\n\n## Contributing\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\nPlease make sure to update tests as appropriate.\n\n## License\n[MIT](https://choosealicense.com/licenses/mit/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feranboudjnah%2Fsolid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feranboudjnah%2Fsolid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feranboudjnah%2Fsolid/lists"}