Coder Social home page Coder Social logo

liveadapter's Introduction

LiveAdapter

JitPack Android Arsenal License

LiveAdapter

Don't write a RecyclerView adapter again. Not even a ViewHolder!

  • Based on Android Data Binding
  • Written in Kotlin
  • Supports LiveData
  • No need to write the adapter
  • No need to write the ViewHolders
  • No need to modify your model classes
  • No need to notify the adapter when data set changes
  • Supports multiple item view types
  • Optional Callbacks/Listeners
  • Very fast — no reflection
  • Super easy API
  • Minimum Android SDK: 19

Setup

Gradle

Add it in your root build.gradle at the end of repositories:

allprojects {
	repositories {
		...
		maven { url 'https://jitpack.io' }
	}
}

Add the dependency to module build.gradle

// apply plugin: 'kotlin-kapt' // this line only for Kotlin projects

android {
    ...
    dataBinding.enabled true 
}

dependencies {
    implementation 'com.github.RaviKoradiya:LiveAdapter:1.3.4'
    // kapt 'com.android.databinding:compiler:GRADLE_PLUGIN_VERSION' // this line only for Kotlin projects
}

Usage

Create your item layouts with <layout> as root:

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable name="item" type="com.github.RaviKoradiya.LiveAdapter.item.Header"/>
    </data>
    
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{item.text}"/>
        
</layout>

It is important for all the item types to have the same variable name, in this case "item". This name is passed to the adapter builder as BR.variableName, in this case BR.item:

ObservableList or simple List
// Java
new LiveAdapter(listOfItems, BR.item)
           .map(Header.class, R.layout.item_header)
           .map(Point.class, R.layout.item_point)
           .into(recyclerView);
// Kotlin
LiveAdapter(listOfItems, BR.item)
           .map<Header>(R.layout.item_header)
           .map<Point>(R.layout.item_point)
           .into(recyclerView)

The list of items can be an ObservableList or LiveData<List> if you want to get the adapter automatically updated when its content changes, or a simple List if you don't need to use this feature.

LiveData<List<*>>
// Kotlin sample
LiveAdapter(
            data = liveListOfItems,
            lifecycleOwner = this@MainActivity,
            variable = BR.item )
           .map<Header, ItemHeaderBinding>(R.layout.item_header) {
               areContentsTheSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
               areItemSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
           }
           .map<Point, ItemPointBinding>(R.layout.item_point) {
               areContentsTheSame { old: Point, new: Point ->
                   return@areContentsTheSame old.id == new.id
               }
               areItemSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
           }
           .into(recyclerview)

I suggest to implement DiffUtil ItemCallback while using LiveData.

NoDataCallback

A callback to manage "no data text or image view" visiblity.

// Kotlin sample
LiveAdapter(listOfItems, BR.item)
           .map<Header> ...
           .onNoData { noData ->
               textNoDataFound.visibility =
                   if (noData)
                       View.VISIBLE
                   else
                       View.GONE
           }
           ...
           .into(recyclerView)

LayoutHandler

The LayoutHandler interface allows you to use different layouts based on more complex criteria. Its one single method receives the item and the position and returns the layout resource id.

// Java sample
new LiveAdapter(listOfItems, BR.item)
           .handler(handler)
           .into(recyclerView);

private LayoutHandler handler = new LayoutHandler() {
    @Override public int getItemLayout(@NotNull Object item, int position) {
        if (item instanceof Header) {
            return (position == 0) ? R.layout.item_header_first : R.layout.item_header;
        } else {
            return R.layout.item_point;
        }
    }
};
// Kotlin sample
LiveAdapter(listOfItems, BR.item).layout { item, position ->
    when (item) {
        is Header -> if (position == 0) R.layout.item_header_first else R.layout.item_header
        else -> R.layout.item_point 
    }
}.into(recyclerView)

Acknowledgments

Thanks to Miguel Ángel Moreno for this library.

Author

Ravi Koradiya

I'm open to new challenging work - Hire me!

Email Upwork LinkedIn Facebook Twitter

License

Copyright 2020 Ravi Koradiya

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.

liveadapter's People

Contributors

ravikoradiya 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

Watchers

 avatar  avatar

liveadapter's Issues

Help me, recyleview not display data

class MessageManager : ViewModel() {

private val _messages = MutableLiveData<List>()
val messages: LiveData<List> = _messages

init {
_messages.value = mutableListOf()
}

fun addMessage(message: Message) {
val currentList = _messages.value?.toMutableList() ?: mutableListOf()
currentList.add(message)
currentList.sortBy { it.timestamp }
_messages.value = currentList
}

fun getMessages(): List {
return messages.value.orEmpty()
}
}

/////////////////////////////
private fun initLiveAdapter(){
LiveAdapter(messageManager.getMessages())
.map(R.layout.item_rcv_message) {
onBind {
if(it.binding.data?.sender == DatabaseHelper.SENDER_CLIENT){
it.binding.getViewById(R.id.img_sender)?.setImageResource(R.drawable.user)
it.binding.getViewById(R.id.ll_background_message)?.setBackgroundColor(ContextCompat.getColor(this@ChatActivity, R.color.colorSecondary))
}else{
it.binding.getViewById(R.id.img_sender)?.setImageResource(R.drawable.openai)
it.binding.getViewById(R.id.ll_background_message)?.setBackgroundColor(ContextCompat.getColor(this@ChatActivity, R.color.colorPrimary))
}
it.binding.getViewById(R.id.tv_response_text)?.text = it.binding.data?.content
}
}
.into(binding.rcvChatConversation)
}

DiffUtilCallback wrong items for compare leads to not updating of the RecyclerItem

There is an issue with DiffUtil callback

  Timber.d("XXX input: $liveDataEvent")
    LiveAdapter(customLiveData, lifecycleOwner, BR.viewModel)
        .map<StatusBarNotification, LayoutNotificationBarItemBinding>(R.layout.layout_notification_bar_item) {
            areItemSame { old: StatusBarNotification, new: StatusBarNotification ->
                Timber.d("XXX areItemSame old: ${old.id == new.id} old:$old , new:$new")
                return@areItemSame old.id == new.id
            }
            areContentsTheSame { old: StatusBarNotification, new: StatusBarNotification ->
                Timber.d("XXX areContentsTheSame: ${old == new} old:$old , new:$new")
                return@areContentsTheSame old == new
            }
        }
        .into(this)

First live data event (isProgress = true)

XXX input: [StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=true, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)]
2021-12-07 18:56:11.209 30820-31122/com.globalpayments.atom D/StatusBarNotificationView$setUpWithLiveData: XXX areItemSame old: true old:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=true, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428) , new:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=true, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)
2021-12-07 18:56:11.209 30820-31122/com.globalpayments.atom D/StatusBarNotificationView$setUpWithLiveData: XXX areItemSame old: true old:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=true, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428) , new:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=true, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)
2021-12-07 18:56:11.210 30820-31122/com.globalpayments.atom D/StatusBarNotificationView$setUpWithLiveData: XXX areContentsTheSame: true old:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=true, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428) , new:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=true, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)
2021-12-07 18:56:18.260 30820-30820/com.globalpayments.atom D/StatusBarNotificationView$setUpWithLiveData$$inlined$map: 

Second live data event (isProgress = false)

XXX input: [StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=false, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)]
2021-12-07 18:56:18.262 30820-31090/com.globalpayments.atom D/StatusBarNotificationView$setUpWithLiveData: XXX areItemSame old: true old:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=false, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428) , new:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=false, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)
2021-12-07 18:56:18.262 30820-31090/com.globalpayments.atom D/StatusBarNotificationView$setUpWithLiveData: XXX areItemSame old: true old:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=false, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428) , new:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=false, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)
2021-12-07 18:56:18.262 30820-31090/com.globalpayments.atom D/StatusBarNotificationView$setUpWithLiveData: XXX areContentsTheSame: true old:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=false, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428) , new:StatusBarNotification(id=1, status=WARNING, text=Transaction not synchronized., isProgress=false, progressText=Sync in progress…, actionButtonText=sync now, actionButtonClickListener=com.globalpayments.atom.ui.home.MainActivity$observeNotificationBar$2$1$1@7b38428)

As you can see DiffUtil always sends same items for comparing and this leads to always returning true from both methods areItemSame and areContentsTheSame.

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.