Improvements in Smart Adapters 1.2
The 1.2.x version is the multi mapper release. And what does that mean?
After receiving some feedback and dealing with some user problems, there was an issue that was coming up again and again: what if we wanted one object class to be mapped to to more than one view class at the same time? It is a very typical use case if you depend on the contents of what's inside the object to render some view or other.
For example, we might want to do this:
SmartAdapter.items(myObjectList) .listener(myViewListener) .map(Tweet.class, TweetView.class) .map(Tweet.class, TweetGalleryView.class) .map(Tweet.class, TweetImageView.class) .map(Tweet.class, TweetEmbedView.class) .map(OtherThing.class, OtherThingView.class) .into(myListView);
My idea with the last version was to just ignore the mapper and do it on your own. But with the RecyclerView.Adapter it's not that simple, because we don't have the info needed at all times.
So it was better to change the BindableLayoutBuilder interface to have different methods: one for view class decision viewType
and another for view class creation build
.
public interface BindableLayoutBuilder<T, Q extends BindableLayout<T>> { Q build(@NonNull ViewGroup parent, int viewType, T item, @NonNull Mapper mapper); Class<? extends BindableLayout> viewType(@NonNull T item, int position, @NonNull Mapper mapper); }
That way, most of the cases of multiple mapping can be solved by only overriding the viewType
method of the DefaultBindableLayoutBuilder to include the checks.
This would be an example based in the previous mapping.
public class TweetBindableLayoutBuilder extends DefaultBindableLayoutBuilder { @Override public Class<? extends BindableLayout> viewType(@NonNull Object item, int position, Mapper mapper) { if (item instanceof Tweet) { // All the multiple bindings must be dealt with here and NOT get into the fallback Tweet tweet = (Tweet) item; if (tweet.hasGallery()) { return TweetGalleryView.class; } else if (tweet.hasImage()) { return TweetImageView.class; } else if (tweet.hasEmbeds()) { return TweetEmbedView.class; } else { return TweetView.class; } } // With this fallback we return control for all the other cases to be handled as the default use. return super.viewType(item, position, mapper); } }
Of course, we would have to assign the builder to the adapter.
SmartAdapter.items(myObjectList) .listener(myViewListener) .map(Tweet.class, TweetView.class) .map(Tweet.class, TweetGalleryView.class) .map(Tweet.class, TweetImageView.class) .map(Tweet.class, TweetEmbedView.class) .map(OtherThing.class, OtherThingView.class) .builder(new TweetBindableLayoutBuilder()) .into(myListView);
For a future release I'm thinking about making this use case, the multiple mapping of models to views, to be simpler by using a list of classes plus a callback for each one so the user doesn't have to subclass the builders. But first, let's see how this works.