LazyRecycler-Flow

Indistinct artifact id, use lazyrecycler-flow instead

License

License

GroupId

GroupId

io.github.dokar3
ArtifactId

ArtifactId

flow
Last Version

Last Version

0.1.1
Release Date

Release Date

Type

Type

aar
Description

Description

LazyRecycler-Flow
Indistinct artifact id, use lazyrecycler-flow instead
Project URL

Project URL

https://github.com/dokar3/LazyRecycler
Source Code Management

Source Code Management

https://github.com/dokar3/LazyRecycler

Download flow

How to add to project

<!-- https://jarcasting.com/artifacts/io.github.dokar3/flow/ -->
<dependency>
    <groupId>io.github.dokar3</groupId>
    <artifactId>flow</artifactId>
    <version>0.1.1</version>
    <type>aar</type>
</dependency>
// https://jarcasting.com/artifacts/io.github.dokar3/flow/
implementation 'io.github.dokar3:flow:0.1.1'
// https://jarcasting.com/artifacts/io.github.dokar3/flow/
implementation ("io.github.dokar3:flow:0.1.1")
'io.github.dokar3:flow:aar:0.1.1'
<dependency org="io.github.dokar3" name="flow" rev="0.1.1">
  <artifact name="flow" type="aar" />
</dependency>
@Grapes(
@Grab(group='io.github.dokar3', module='flow', version='0.1.1')
)
libraryDependencies += "io.github.dokar3" % "flow" % "0.1.1"
[io.github.dokar3/flow "0.1.1"]

Dependencies

compile (3)

Group / Artifact Type Version
org.jetbrains.kotlinx : kotlinx-coroutines-core jar 1.4.2
org.jetbrains.kotlin : kotlin-stdlib jar 1.4.31
io.github.dokar3 : lazyrecycler jar 0.1.1

Project Modules

There are no modules declared in this project.

RecyclerView,Compose like

Maven Central

LazyRecycler is a library that provides LazyColumn (from Jetpack Compose) like apis to build lists with RecyclerView.

Usage

implementation 'io.github.dokar3:lazyrecycler:0.1.4'

With LazyRecycler, a few dozen lines of code can do almost all things for RecyclerView. Adapter, LayoutManager, DiffUtil, OnItemClickListener and more, these are all in one block:

LazyRecycler(recyclerView, spanCount = 3) {
    item(R.layout.header, Unit) {}

    items(listOfNews) { binding: ItemNewsBinding, news ->
        // bind
    }.clicks { view, item ->
        // handle item clicks
    }.spanSize { position ->
        // map to SpanSizeLookup.getSpanSize()
        if (position % 3 == 0) 3 else 1
    }.differ {
        areItemsTheSame { oldItem, newItem ->
            // map to DiffUtil.ItemCallback.areItemsTheSame()
            oldItem.id == newItem.id
        }
        areContentsTheSame { oldItem, newItem ->
            // map to DiffUtil.ItemCallback.areContentsTheSame()
            oldItem.title == newItem.title && ...
        }
    }
    ...
    item(R.layout.footer, Unit) {}
}

Basic

Sections

LazyRecycler uses sections to build lists, A section will contain id, items, view creator, view binder, click listeners and other needed properties, it represents a view type in adapter. To create a new section, use the item and items function:

item(...) { ... }

items(...) { ... }

Sections should only be used in LazyRecycler / newSections block, so it's necessary to pass a unique id when creating a dynamic section (Require doing some operations after list is created, like update, remove, hide and show). Or use observable data sources (see the section below).

val lazyRecycler = LazyRecycler {
    items<News>(..., sectionId = SOME_ID) { ... } 
}

// update
lazyRecycler.updateSection(SOME_ID, items)
// remove
lazyRecycler.removeSection(SOME_ID)
// hide and show
lazyRecycler.setSectionVisible(SOME_ID, false)

Layout

layout id and ViewBinding are supported:

items(R.layout.item_news, news) { ... }
items<ItemNewsBinding, News>(news) { ... }

Providing an item view in the bind scope is also supported:

items<String>(...) {
	val tv = TextView(context)
    bind { text ->
    	tv.text = text
    }
    return@item tv
}

Bind

For ViewBinding item/items:

items<ItemNewsBinding, News>(...) { binding, news -> 
	binding.title.text = news.title
    binding.image.load(news.cover)
    ...
}

For other item/items:

items(...) { ...
    ...
    val tv: TextView = view.findViewById(R.id.title)
	bind { item ->
    	tv.text = item.title
    }
    ...
}

Generics

  • I for item type
  • V for the View Binding
items<I>(layoutId, I) { ... }
items<V, I>(items) { ... }

Overloads

There are a lots of item/items functions to support various layouts, for helping compiler to pick correct one, lambdas may require explicit argument(s):

// item
item(R.layout.item, Any()) {}
item(Any()) { binding: ItemBinding, item -> }
// Require -> to distinguish from view binding one
item(Any()) { -> TextView(context) }

// items
items(R.layout.item, listOf(Any()) {}
items(listOf(Any()) { binding: ItemBinding, item -> }
// Require -> to distinguish from view binding one
items(listOf(Any()) { -> TextView(context) }

LayoutManager

LazyRecycler create a LinearLayoutManager by default, if spanCount is defined, GridLayoutManager will be picked. To skip the LayoutManager setup, set setupLayoutManager to false:

LazyRecycler(
    recyclerView,
	setupLayoutManager = false,
    isHorizontal = false, // ignored
    spanCount = 3, // ignored
    reverseLayout = false, // ignored
    stackFromEnd = false, // ignored
) { ... }
....
recyclerView.layoutManager = ...

spanSize() is used to define SpanSizeLookup for GridLayoutManager:

item(...) {
}.spanSize {
	3
}

items<V, I>(...) {
	...
}.spanSize { position ->
	if (position == 0) 3 else 1
}

DiffUtil

Use differ function, then LazyRecycler will do the all works after section items changed.

items<V, I>(...) {
	...
}.differ {
    areItemsTheSame { oldItem, newItem ->
        ...
    }
    areContentsTheSame { oldItem, newItem ->
    	...
    }
}

Clicks

clicks for the OnItemClickListener, longClicks for the OnItemLongClickListener.

items(...) {
	...
}.clicks { view, item -> 
	...
}.longClicks { view, item -> 
    ...
}

Observable data sources

To support Flow, LiveData or RxJava, add the dependencies first:

// Flow
implementation 'io.github.dokar3:lazyrecycler-flow:0.1.4'

// LiveData
implementation 'io.github.dokar3:lazyrecycler-livedata:0.1.4'

// RxJava3
implementation 'io.github.dokar3:lazyrecycler-rxjava3:0.1.4'

Flow

  1. import:

    import com.dokar.lazyrecycler.flow.item
    import com.dokar.lazyrecycler.flow.items
  2. Replace the data sources:

    val entry: Flow<I> = ...
    item<V, I>(entry) { ... }
    
    val source: Flow<List<I>> = ...
    items<V, I>(source) { ... }
  3. Register/unregister observers:

    val lazyRecycler = LazyRecycler { ... }
    ...
    // Observe data changes
    lazyRecycler.observeChanges(coroutineScope)
    // Stop observing, if the register scope is lifecycleScope 
    // then there is no need to unregister it manually
    lazyRecycler.stopObserving(coroutineScope)

LiveData

  1. import:

    import com.dokar.lazyrecycler.livedata.item
    import com.dokar.lazyrecycler.livedata.items
  2. Replace the data sources:

    val entry: LiveData<I> = ...
    item<V, I>(entry) { ... }
    
    val source: LiveData<List<I>> = ...
    items<V, I>(source) { ... }
  3. Register/unregister observers:

    val lazyRecycler = LazyRecycler { ... }
    ...
    // Observe data changes
    lazyRecycler.observeChanges(lifecycleOwner)
    // Stop observing, no need to unregister it manually
    lazyRecycler.stopObserving(lifecycleOwner)

RxJava

  1. import:

    import com.dokar.lazyrecycler.rxjava3.item
    import com.dokar.lazyrecycler.rxjava3.items
  2. Replace the data sources:

    val entry: Observable<I> = ...
    item<V, I>(entry) { ... }
    
    val source: Observable<List<I>> = ...
    items<V, I>(source) { ... }
  3. Register/unregister observers:

    val lazyRecycler = LazyRecycler { ... }
    ...
    // Observe data changes
    lazyRecycler.observeChanges()
    // Required: Stop observing
    lazyRecycler.stopObserving()

Advanced

Alternate sections

template() + items(section, ...): A section can be created from a Template:

LazyRecycler {
	// layout id definition
    val sectionHeader = template<String>(R.layout.section_header) {
    	// bind
    }
    // ViewBinding definition
    val normalItem = template<ItemViewBinding, Item> { binding, item ->
    	// bind
    }
    
    item(sectionHeader, "section 1")
    items(normalItem, someItems)
    
    item(sectionHeader, "section 2")
    items(normalItem, otherItems)
    ...
}

Single items with multiple view types

template() + subSection(): Multiple view types for single data source is supported:

LazyRecycler {
	val bubbleFromMe = template<ItemMsgFromMeBinding, Message> { binding, msg ->
    	// bind
    }
    
    items<ItemMsgBinding, Message>(messages) { binding, msg ->
    	// bind
    }.subSection(bubbleFromMe) { msg, position ->
    	// condition
        msg.isFromMe == true
    }
    ...
}

Add new sections dynamically

LazyRecycler.newSections():

val lazyRecycler = ...
...
lazyRecycler.newSections {
    item(...) { ... }
    items(...) { ... }
    ...
}
// If new sections contain any observable data source
lazyRecycler.registerObservers(...)

Build list in background

LazyRecycler.attchTo():

backgroundThread {
	val recycler = LazyRecycler {
    	...
    }
    uiThread {
		lazyRecycler.attchTo(recyclerView)
    }
}

ProGuard

If ViewBinding item/items are used, add this rule to proguard file:

# keep ViewBinding.inflate for LazyRecycler
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
    inflate(android.view.LayoutInflater, android.view.ViewGroup, boolean);
}

Demos

1. Multiple sections

Day Night

2. Chat screen

Day Night

3. Tetris game (Just for fun)

Day Night

Links

License

Apache License 2.0

Versions

Version
0.1.1
0.1.0