Giter VIP home page Giter VIP logo

ibrahimyilmaz / kiel Goto Github PK

View Code? Open in Web Editor NEW
370.0 10.0 30.0 323 KB

(Published to MavenCentral) Kotlin way of building RecyclerView Adapter ๐Ÿงฉ. You do not have to write RecyclerView Adapters again and again and suffer from handling of different view types. Kiel will help you.

License: Apache License 2.0

Kotlin 100.00%
recyclerview recyclerview-adapter viewholder visitor-pattern kotlin kotlin-android android-library kiel

kiel's Introduction

build Maven Central Android Arsenal GitHub license GitHub issues GitHub stars

Kiel

Kiel is a RecyclerView.Adapter with a minimalistic and convenient Kotlin DSL which provides utility on top of Android's normal RecyclerView.Adapter.

kiel_icon

Most of the time:

  • We found ourselves repeating same boilerplate code for RecyclerView.Adapter.
  • We have difficulty in handling RecyclerView.Adapter when there are many viewTypes.

But now, Kiel may help us to get rid of these problems. You may read the detailed story in this blog post.

Usage:

Basic Usage:

adapterOf
 val recyclerViewAdapter = adapterOf<Text> {
                register(
                    layoutResource = R.layout.adapter_message_text_item,
                    viewHolder = ::TextMessageViewHolder,
                    onViewHolderCreated = { vh->
                       //you may handle your on click listener
                       vh.itemView.setOnClickListener {

                       }
                    },
                    onBindViewHolder = { vh, _, it ->
                        vh.messageText.text = it.text
                        vh.sentAt.text = it.sentAt
                    }
                )
     }

 recyclerView.adapter = recyclerViewAdapter
pagingDataAdapterOf
 val pagingDataAdapterOf = pagingDataAdapterOf<Text> {
                register(
                    layoutResource = R.layout.adapter_message_text_item,
                    viewHolder = ::TextMessageViewHolder,
                    onViewHolderCreated = { vh->
                       //you may handle your on click listener
                       vh.itemView.setOnClickListener {

                       }
                    },
                    onBindViewHolder = { vh, _, it ->
                        vh.messageText.text = it.text
                        vh.sentAt.text = it.sentAt
                    }
                )
     }

 recyclerView.adapter = recyclerViewAdapter

Different View Types:

You may register different ViewHolders to your adapters.

              register(
                    layoutResource = R.layout.adapter_message_text_item,
                    viewHolder = ::TextMessageViewHolder,
                    onBindViewHolder = { vh, _, it ->
                        vh.messageText.text = it.text
                        vh.sentAt.text = it.sentAt
                    }
                )

                register(
                    layoutResource = R.layout.adapter_message_image_item,
                    viewHolder = ::ImageMessageViewHolder,
                    onBindViewHolder = { vh, _, item ->
                        vh.messageText.text = item.text
                        vh.sentAt.text = item.sentAt

                        Glide.with(vh.messageImage)
                            .load(item.imageUrl)
                            .into(vh.messageImage)
                    }
                )

Handling Events:

As ViewHolder instance is accessible in:

  • onViewHolderCreated
  • onBindViewHolder
  • onBindViewHolderWithPayload

You can handle the events in the same way how you did it before.

 val recyclerViewAdapter = adapterOf<Text> {
                register(
                    layoutResource = R.layout.adapter_message_text_it,
                    viewHolder = ::TextMessageViewHolder,
                    onViewHolderCreated = { vh->
                       vh.itemView.setOnClickListener {

                       }
                       vh.messageText.addTextChangedListener{text ->

                       }
                    },
                    onBindViewHolder = { vh, _, it ->
                        vh.messageText.text = it.text
                        vh.sentAt.text = it.sentAt
                    }
                )
 }

recyclerView.adapter = recyclerViewAdapter

View Binding:

As ViewHolder instance is accessible in:

  • onViewHolderCreated
  • onBindViewHolder
  • onBindViewHolderWithPayload

You may define your ViewBinding in your ViewHolder class and you can easily reach it:

class TextMessageViewHolder(view: View) : RecyclerViewHolder<Text>(view) {
    val binding = AdapterTextItemBinding.bind(view)
}

val recyclerViewAdapter = adapterOf<Text> {
                register(
                    layoutResource = R.layout.adapter_message_text_it,
                    viewHolder = ::TextMessageViewHolder,
                    onViewHolderCreated = { vh->
                       vh.binding.
                    },
                    onBindViewHolder = { vh, _, it ->
                       vh.binding.messageText.text = it.text
                       vh.binding.sentAt.text = it.sentAt
                    }
                )
 }

DiffUtil:

val recyclerViewAdapter = adapterOf<MessageViewState> {
                diff(
                    areContentsTheSame = { old, new -> old == new },
                    areItemsTheSame = { old, new -> old.message.id == new.message.id },
                    getChangePayload = { oldItem, newItem ->
                        val diffBundle = Bundle()

                        if (oldItem.selectionState != newItem.selectionState) {
                            diffBundle.putParcelable(
                                TextMessageViewHolder.KEY_SELECTION,
                                newItem.selectionState
                            )
                        }

                        if (diffBundle.isEmpty) null else diffBundle
                    }
                )
                register (
                    layoutResource = R.layout.adapter_message_text_item,
                    viewHolder = ::TextMessageViewHolder,
                    onBindViewHolder = { vh, _, it ->
                        vh.messageText.text = it.message.text
                        vh.sentAt.text = it.message.sentAt
                    }
                )

Download

implementation 'io.github.ibrahimyilmaz:kiel:latestVersion'

latestVersion = 1.2.1

License

Copyright 2020 Ibrahim Yilmaz

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

kiel's People

Contributors

ibrahimyilmaz avatar kuruchy avatar mahmoudmabrok avatar raquezha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kiel's Issues

[kiel] Check and make internal non-public entities

As a feedback by Gabor Varadi,

Most classes and typealiases seem to be
 default visibility, which is public. But the library
 should try to minimize this and make 
non public things internal

We should make internal kiel's non public entities.

[kiel] : Support HasStableId in adapterOf

In order to set hasStableId, we may pass lambda or interface to RecyclerViewAdapter to override getItemId.

When it is passed, RecyclerViewAdapter use it,
When it is not passed, RecyclerViewAdapter default super.getItemId()

Aim is to make it composable also:

syntax would be:

val adapter =adapterOf<Type>{
         itemId{ t.getId() }
         ....
}

Add "import" information in README

Hi,
I just tried out your library and I have to say: Awesome!
I WILL use it in my projects from now on.

But it seems like adapterOf does not get imported automatically, so I had to search for the import in the sample app.
Maybe you should add the note to the README that you need the import import me.ibrahimyilmaz.kiel.adapter.RecyclerViewAdapter.Companion.adapterOf like in the Download section.

Greetings!

[kiel] - Consider new syntax alternatives.

consider the syntax alternatives like that(there can be some alternatives like kluent!):

adapterOf<Something> {
    register {
        type = Text::class.java
        layoutResource { R.layout.adapter_message_text_item }
        // ..
    }
}

And could look like this:

adapterOf<Something> {
    register(Text::class.java) {
        layoutResource { R.layout.adapter_message_text_item }
        // ..
    }
}
adapterOf<Something> {
    Text::class.java.register {
        layoutResource { R.layout.adapter_message_text_item }
        // ..
    }
}

[kiel] Implement Unit Test for `RecyclerViewAdapter`

During the implementation of RecyclerViewListAdapter, implementing getViewType method somehow was missed.
Robolectric or Android Instrumentation test it does not matter, it will be nice to have unit tests for Adapters.

[question] Use setHasFixedSize list will not be displayed

@ibrahimyilmaz If the RecyclerView layout uses wrap_content instead of match_parent and setHasFixedSize = true, the list will not be displayed.

activity_country_indonesia.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" / "wrap_content"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        tools:listitem="@layout/item_task_detail" />

</LinearLayout>

CountryIndonesiaActivity.kt

class CountryIndonesiaActivity : BaseActivity() {
    private val viewModel by viewModel<CountryIndonesiaViewModel>()
    private lateinit var binding: ActivityCountryIndonesiaBinding
    private val viewAdapter by lazy { createAdapter(::onItemClicked) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCountryIndonesiaBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupActionBarWithBackButton(binding.toolbar)
        initView()

        viewModel.loadData()
    }

    private fun initView() {
        with(binding.recyclerView) {
            adapter = viewAdapter
            setHasFixedSize(true)
        }
    }

    override fun observeChange() {
        observe(viewModel.items, ::onDataLoaded)
        observe(viewModel.toastMessage, ::showSnackbarMessage)
    }

    private fun onDataLoaded(items: List<BaseViewItem>) {
        viewAdapter.submitList(items)
    }


    private fun onItemClicked(viewItem: BaseViewItem, view: View) {
        when (viewItem) {
            is DailyItem -> {

            }
            is TextItem -> {
                DailyGraphActivity.startActivity(this)
            }
        }
    }

    companion object {
        @JvmStatic
        fun startActivity(context: Context?) =
            context?.startActivity(Intent(context, CountryIndonesiaActivity::class.java))
    }
}

This question can be through kotlin-mvvm-covid19 project test.

[kiel] Create alternative methods for register , where layoutResource is passed as a value, and then wrapped into a function type.

Create alternative methods for register , where layoutResource is passed as a value, and then wrapped into a function type.

This

register(
    type = Text::class.java
    layoutResource = { R.layout.adapter_message_text_item }
    viewHolder = { ::TextMessageViewHolder }
    onViewHolderBound = { vh, _, it ->
        vh.messageText.text = it.text
        vh.sentAt.text = it.sentAt
    }
)

would become this:
Kotlin

register(
    type = Text::class.java
    layoutResource = R.layout.adapter_message_text_item
    viewHolder = { ::TextMessageViewHolder }
    onViewHolderBound = { vh, _, it ->
        vh.messageText.text = it.text
        vh.sentAt.text = it.sentAt
    }
)

With:

fun register(layoutResourceProvider: () -> Int) {
    // use layoutResourceProvider
}

fun register(layoutResource: Int) = register(
    layoutResourceProvider = { layoutResource }
)

[kiel] Drop Jetifier

FEATURE REQUEST

Hi! Thank you for making Kiel! I've played with it for some time now and I want to use it in our project but this issue is we are trying to reduce build time by dropping jetifier.

We found out by dropping jetifier we can reduce a significant amount of build time. thank you!

Describe the solution you'd like

I've forked your project and dropped jetifier, test it and it works! I just disabled the app module because of some conflicts. Anyway it's working fine in my test project.

 implementation 'com.github.raquezha:kiel:1.2.1-beta'

Describe alternatives you've considered

I hope you consider this feature request

Additional context

  1. Adam Bennet's Article about droping jetifier: https://adambennett.dev/2020/08/disabling-jetifier/
  2. Can I Drop Jetifier Repo: https://github.com/plnice/can-i-drop-jetifier

[samples] : Create example with long/normal click listeners

I got this feedback from reddit:

Need example with long/normal click listeners + per-view-id click tracking 
(in case if item has "favorite" and "delete" buttons for example).

It will be great to show an example how to deal with click events. With an example, README may also be improved.

Composable Diff improvements

#40 added syntax to define Diff callback for whole adapter. But you still have no ability to define diff callback for register blocks separately.

There is example of how it may look like:

adapterOf<Animal> {
    register(
        layoutResource = R.layout.item_cat,
        viewHolder = ::CatHolder,
        diff = diff(
            areItemsTheSame = { cat1, cat2 -> cat1.name == cat2.name }
        )
    )

    register(
        layoutResource = R.layout.item_dog,
        viewHolder = ::DogHolder,
        diff = diff(
            areItemsTheSame = { dog1, dog2 -> dog1.bark == dog2.bark }
        )
    )
}

This builder under the hood should create single DiffUtil.Callback with multiple "delegates": one for Cat, one for Dog. Then, when DiffUtil.Callback methods are called, that Callback must pick appropriate "delegate" and call it

how to define viewTypes with conditions

now we can register viewTypes but README has no way to determine which viewType is created at spefici position i.e
how to make views has two types Head, Sub

  • Head with odd positions
  • Sub with even positions

[kiel] : Make DiffUtil usage composable too

I got this comment from Reddit:

Suggestion: make DiffUtil usage composable too :)
Look: RecyclerView.Adapter and DiffUtil.Callback are very similar! Adapter is responsible for converting item of ANY type into view, Callback is responsible for comparing items of ANY types with each other.
You noticed, that Adapter become much easier to use if you separate each recycler type and allow to compose them together inside one adapter. But DiffUtil.Callback is the same โ€” if you make it composable, it will reduce much of boilerplate code.
I suggest you to add areItemsTheSame and areContentsTheSame methods right inside your register DSL. I have written very similar library a year or two ago - and it works great in our team 

syntax would be :

recyclerView.adapter = adapterOf<Type>{
   diffUtil{
          areItemsTheSame{ old, new -> old.id == new.id }
          content{ old, new -> old == new }
          changePayload{ old, new -> }
     }
    ....
 }

This is

Improve Readme to make Kiel more accessible.

As a developer, we would like to understand :

  • How we can use Kiel better
  • How we can use DataBinding with Kiel
  • How we can use ViewBinding with Kiel
    So Readme can be improved.

Any idea or support is welcome

[kiel] Support for load more functions

Hi there,
RecyclerView usually have to handle a large amount of data, I think it really need to add support for load more functions.
What do you think?

[kiel]: Make `PagingDataAdapter` composable

As an Android Developer, we would like to able to use Kiel with PagingDataAdapter . With this we may utilize the power of Paging 3.

https://developer.android.com/topic/libraries/architecture/paging/v3-overview
https://developer.android.com/reference/kotlin/androidx/paging/PagingDataAdapter

syntax would be:

val adapter =pagingAdapter<Type>{
diffUtil{...}
register{...}
}

adapter.submitData(...)

In addition to this, it will be nice to have an example in the samples project.

[kiel] Avoid using Builder Classes.

in general we try to avoid builder classes in Kotlin, as they're not needed and introduce nullability.
So considernig this:
Kotlin

register {
    type { Text::class.java }
    layoutResource { R.layout.adapter_message_text_item }
    viewHolder { ::TextMessageViewHolder }
    onViewHolderBound<Text, TextMessageViewHolder> { vh, _, it ->
        vh.messageText.text = it.text
        vh.sentAt.text = it.sentAt
    }
}

Could look like this:
Kotlin

register(
    type = Text::class.java
    layoutResource = { R.layout.adapter_message_text_item }
    viewHolder = { ::TextMessageViewHolder }
    onViewHolderBound = { vh, _, it ->
        vh.messageText.text = it.text
        vh.sentAt.text = it.sentAt
    }
)

[kiel] : Make ListAdapter Composable

As an Android Developer, we would like to able to use Kiel with ListAdapter .

syntax would be:

val adapter =listAdapter<...>{
diffUtil{...}
register{...}
}

In addition to this, it will be nice to have an example in the samples project.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.