Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Composable adapters for Android RecyclerViews and ListViews

License

NotificationsYou must be signed in to change notification settings

NextFaze/power-adapters

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Table of Contents

Power Adapters

Presenting large data sets efficiently can be a challenging part of Android development. It gets more complicated as webegin to handle edge cases and add additional decorations like headers. We also often find ourselves repeatingundesirable boilerplate as we write adapters for each data source. In addition, Android doesn't provide a cleanobject-oriented, reusable way of presenting collections of multiple types.

Feature Summary

This library provides the following features:

  • Presentmultiple data types within an adapter in atype-safe manner
  • Concatenate multiple adapters together
  • Showheaders andfooters
  • Show aloading indicator to indicate a loading state
  • Show anempty item to indicate an empty underlying data set
  • Adddividers in between items of an existing adapter
  • Show an adapter or item range only when acondition evaluates totrue
  • Presentnested adapters, a powerful substitute forExpandableListView without any limitation of nesting level
  • Load from remote or slow data sourcesasynchronously
  • Backed up byunit tests, verifying the correct notifications are issued and state maintained
  • Minimaldependencies; doesn't include any unnecessary transitive dependencies
  • All adapters issue the correct insertion/removal/change notifications needed for fullRecyclerView animation support
  • Kotlin extension modules, which add idiomatic Kotlin APIs
  • RxJava extension modules, adding easy integration withObservables, etc

Power adapters are compatible with the following collection view classes:

  • androidx.recyclerview.widget.RecyclerView
  • android.widget.ListView
  • android.widget.GridView
  • android.support.v4.view.ViewPager
  • Any other view that accepts aandroid.widget.Adapter

Usage

Get it from Maven Central, using Gradle:

implementation'com.nextfaze.poweradapters:power-adapters:0.26.0'implementation'com.nextfaze.poweradapters:power-adapters-recyclerview-v7:0.26.0'

Basic

// Declare a binder for your item typeclassTweetHolderextendsViewHolder {TextViewtextView;TweetHolder(Viewview) {super(view);textView = (TextView)view.findViewById(R.id.text);    }}Binder<Tweet,View>tweetBinder =ViewHolderBinder.create(R.layout.tweet,TweetHolder::new, (container,tweet,tweetHolder,holder) -> {tweetHolder.textView.setText(tweet.getText());});// Construct your "core" adapterListBindingAdapter<Tweet>tweetsAdapter =newListBindingAdapter<>(tweetBinder);// Assign to your RecyclerViewrecyclerView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(tweetsAdapter));

RxJava

RxJava modules are available. Simply append-rxjava2 to get the RxJava module:

implementation'com.nextfaze.poweradapters:power-adapters-rxjava2:0.26.0'implementation'com.nextfaze.poweradapters:power-adapters-data-rxjava2:0.26.0'

Kotlin

Kotlin modules are also provided for most modules. Append-kotlin to get the Kotlin module:

implementation'com.nextfaze.poweradapters:power-adapters-kotlin:0.26.0'implementation'com.nextfaze.poweradapters:power-adapters-data-kotlin:0.26.0'implementation'com.nextfaze.poweradapters:power-adapters-rxjava2-kotlin:0.26.0'implementation'com.nextfaze.poweradapters:power-adapters-data-rxjava2-kotlin:0.26.0'implementation'com.nextfaze.poweradapters:power-adapters-recyclerview-v7-kotlin:0.26.0'

Some of the Kotlin APIs include:

  • Top-level factory functions:
    val data= data { api.getPosts() }
    val data= cursorData({ db.getUsers() }, ::User)
    val header= viewFactory<TextView>(R.layout.header) {    text="News"}
  • Extension functions:
    recyclerView.adapter= myPowerAdapter.toRecyclerAdapter()
  • PowerAdapter andData Factory methods:adapterOf(),dataOf()
  • Binder factory methods:
    val binder= binder<Item,ItemView>(R.layout.item) { container, item, holder->    title= item.name    imageUri= item.imageUri}
  • Operator overloads:
    adapter.showOnlyWhile(emptyand!anotherThing)
    val adapter = itemsAdapter + anotherAdapter
    data+= dataObserver { updateViews() }
  • Property delegates:
    val condition=ValueCondition()var enabled by conditionval adapter= myAdapter.showOnlyWhile(condition)// Reassign property to control visibility of adapterenabled=false
  • Type-safe builder:
    adapter {    layoutResource(R.layout.header)+myItemsAdapter    layoutResource(R.layout.footer)}

Adapter Composition

Power Adapters can be composed by using the fluent chaining methods.For example, say you want to present a list of tweets, with a loading indicator, but show an empty message when thereare no tweets, you can write the following:

PowerAdapteradapter =tweetsAdapter    .limit(10)// Only show up to 10 tweets    .append(// Show empty item while no tweets have loadedasAdapter(R.layout.tweets_empty_item).showOnlyWhile(noTweets()),// Show loading indicator while loadingasAdapter(R.layout.loading_indicator).showOnlyWhile(tweetsAreLoading())    )recyclerView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(adapter));

This lets you write a simpleTweetAdapter class, the only responsibility of which is to present tweets. By usingPowerAdapter.append as such, theTweetAdapter need not be modified, and can be potentially reused elsewhere moreeasily. The use ofshowOnlyWhile applies a condition to the empty footer item, so it remains hidden unless theunderlying list of tweets is empty.

Headers and Footers

Headers and footers can be added usingprepend andappend:

// Prepend a header view.PowerAdapteradapter =tweetAdapter.prepend(R.layout.header);
// Append a footer view.PowerAdapteradapter =tweetAdapter.append(R.layout.footer);

Data Type Binding

Included in Power Adapters is the ability to bind elements in your data set to views in a reusable, readable, andtype-safe manner.

Binder

The primary class needed to achieve this is aBinder. The responsibilities of aBinder include:

  • Construct aView to be bound, and re-used by the adapter/recycler view
  • Bind an object and/or data set index to theView

Multiple types of commonly required binders are supplied. If you prefer the widely used view holder pattern, useaViewHolderBinder:

Binder<BlogPost,View>blogPostBinder =ViewHolderBinder.create(R.layout.post,BlogPostHolder::new, (container,blogPost,blogPostHolder,holder) -> {blogPostHolder.labelView.setText("Blog: " +blogPost.getTitle());});classBlogPostHolderextendsViewHolder {TextViewlabelView;BlogPostHolder(Viewview) {super(view);labelView = (TextView)view.findViewById(android.R.id.text1);    }}

If you use custom views for each of your data models, useBinder.create. It takes a layout resource or aViewFactory.The view returned by theViewFactory is passed to subsequentbindView calls, saving you from writing a separateViewHolder.

For example:

Binder<Tweet,TweetView>tweetBinder =Binder.create(R.layout.tweet_item, ((container,sample,v,holder) -> {v.setTweet(tweet);v.setOnClickListener(v ->onTweetClick(tweet));}))

Mapper

The examples above have all dealt with a single item type, and so there has only been a singleBinder. When you want your list to contain multiple items, aMapper is consulted to determine whichBinder to use for presenting each particular item. Typically you'll useMapperBuilder to declaratively assign your model classes tobinders:

Mappermapper =newMapperBuilder()    .bind(Tweet.class,newTweetBinder())    .bind(Ad.class,newAdBinder())    .bind(Video.class,newVideoBinder())    .build();ListBindingAdapter<Object>adapter =newListBindingAdapter<>(mapper);adapter.add(newTweet());adapter.add(newAd());adapter.add(newVideo());

Conversion

PowerAdapter is designed to be used with different collection view implementations, so a final step is converting it to implement the expected adapter interface. This would usually be done as soon as the collection view is created, say inonViewCreated:

recyclerView.setAdapter(toRecyclerAdapter(powerAdapter));

The following conversion methods are provided:

Collection ViewConverterExtension Module
ListViewPowerAdapters.toListAdapter()None
RecyclerViewRecyclerPowerAdapters.toRecyclerAdapter()power-adapters-recyclerview-v7
ViewPagerSupportPowerAdapters.toPagerAdapter()power-adapters-support-v4

Nested Adapters

TheTreeAdapter class allows you to present hierarchical data structures with no intrinsic depth limit. Each layer iscomprised of just another adapter - your children can themselves can beTreeAdapters!

PowerAdapterrootAdapter =newFileAdapter(newFile("/"));TreeAdaptertreeAdapter =newTreeAdapter(rootAdapter,position -> {// Create a child adapter for this position in the root data set.// Can be another TreeAdapter!returncreateChildAdapter(position);});treeAdapter.setExpanded(15,true);

Asynchronous Data Loading

Implementing a UI for presenting the contents of a remote collection, like a list of comments or products, requiresseveral different mechanics. Among them are:

  • Perform requests asynchronously to avoid blocking the UI thread
  • Presenting a loading indicator to give the user feedback on progress
  • Allow the user to page through results
  • Handle and present errors as they occur
  • Dispatch change notifications to your adapter so yourRecyclerView orListView can react to content changes

Thepower-adapters-data extension module aims to simplify this by encapsulating the above concerns into a singleobject:Data<T>. In doing so, it allows you to retain one object when a config change occurs, like an orientationchange. This way you don't need to reload or parcel/unparcel all of your list results when that occurs. TheData<T>object comprises much of the repetitive asynchronous UI "glue" code you'd otherwise have to write (and debug) yourself.

implementation'com.nextfaze.poweradapters:power-adapters-data:0.26.0'

Basic Data Usage

The recommended usage pattern is to instantiate aData<T> object in your retainedFragment. TheData.fromListfactory method supports the simplest use case, fetching a list of items asynchronously:

publicfinalclassProductListFragmentextendsFragment {privatefinalData<Product>products =Data.fromList(() ->api.getProducts());@OverridepublicvoidonCreate(BundlesavedInstanceState) {super.onCreate(savedInstanceState);// Retain this fragment so we don't need to reload the products after a config changesetRetainInstance(true);    }}

Now hook up yourData<Product> instance and aBinder with yourRecyclerView:

@OverridepublicvoidonViewCreated(Viewview,BundlesavedInstanceState) {super.onViewCreated(view,savedInstanceState);PowerAdapteradapter =newDataBindingAdapter(products,productBinder);recyclerView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(adapter));}

Invalidating and Reloading

At some stage you'll want to request a reload of the elements from the remote source. You can do this usingreload(),refresh(), orinvalidate(). The behaviour of these methods differ slightly, but ultimately they all result in youritems being reloaded from the source. See theData javadoc for how they differ.

DataLayout

DataLayout aids in presenting the various states of aData instance, by hiding and showing contents, empty, error,and loading child views.It's aRelativeLayout subclass, and it works by accepting aData instance, then registering to receive changenotifications. If the contents are empty, your marked empty view will be shown instead of the list view. If an erroroccurs, the error view will be shown until a reload is triggered.DataLayout has several extension points to customizethis behaviour to suite the needs of your application.

Here's an example of how to declare aDataLayout in XML. Notice thelayout_component attributes:

<com.nextfaze.poweradapters.data.widget.DataLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/news_fragment_data_layout"android:layout_width="match_parent"android:layout_height="match_parent">    <ListViewandroid:id="@+id/list"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_component="content"/>    <ProgressBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"app:layout_component="loading"/>    <TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"app:layout_component="empty"android:text="No items!"/>    <TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"app:layout_component="error"android:textColor="#ffff0000"/></com.nextfaze.poweradapters.data.widget.DataLayout>

TheDataLayout observes state changes of theData to know when to update the view visibility. Connecting to yourDataLayout andRecyclerView in Java code:

@OverridepublicvoidonViewCreated(Viewview,BundlesavedInstanceState) {super.onViewCreated(view,savedInstanceState);PowerAdapteradapter =newDataBindingAdapter(products,productBinder);listView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(adapter));dataLayout.setData(products);}

RxJava Module

An RxJava module is provided:power-adapters-data-rxjava2. This is a simple adapter library that providesObservablesfor properties ofData:

RxData.inserts(products).subscribe(event ->handleProductInsert(event));

Data Views

Data has fluent chaining methods for providing filtered, transformed, or sorted views of its contents:

Data<String>names = ...Data<Integer>lengths =names.transform(name ->name.length);
Data<Post>allPosts = ...Data<Post>todaysPosts =names.filter(post ->isToday(post.getDate()));

Samples

Check the included sample project for a range of usage pattern examples.

Build

Building instructions:

$ git clone git@github.com:NextFaze/power-adapters.git$cd power-adapters$ ./gradlew clean build

License

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.0Unless required by applicable law or agreed to in writing, softwaredistributed 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 andlimitations under the License.

Packages

No packages published

Contributors3

  •  
  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp