AutoAdapter

Additional

Language
Java
Version
2.5.1 (Aug 15, 2018)
Created
Dec 1, 2016
Updated
Aug 15, 2018
Owner
George Dzotsenidze (Zuluft)
Contributors
George Dzotsenidze (Zuluft)
m-akopov
2
Activity
Badge
Generate
Download
Source code
APK file

Advertisement

AutoAdapter

This Repository simplifies working with RecyclerView Adapter

Gradle:

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

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

Add dependency to app gradle:

implementation 'com.github.Zuluft:AutoAdapter:v2.4.1'
annotationProcessor 'com.github.Zuluft:AutoAdapter:v2.4.1'

Simple Sample:

Step 1:

Create layout xml file, for example item_footballer.xml which contains TextViews with ids tvName, tvNumber, tvClub and ImageView with id ivDelete

Step 2 (Optional):

create model, that you want to be drawn on above created layout:

public final class FootballerModel {
    private final String name;
    private final int number;
    private final String club;

    public FootballerModel(final String name,
                           final int number,
                           final String club) {
        this.name = name;
        this.number = number;
        this.club = club;
    }

    public String getName() {
        return name;
    }

    public int getNumber() {
        return number;
    }

    public String getClub() {
        return club;
    }
}

Step 3:

Create 'Renderer' class in the following way:

   @Render(layout = R.layout.item_footballer,
        views = {
                @ViewField(
                        id = R.id.tvName,
                        name = "tvName",
                        type = TextView.class
                ),
                @ViewField(
                        id = R.id.tvNumber,
                        name = "tvNumber",
                        type = TextView.class
                ),
                @ViewField(
                        id = R.id.tvClub,
                        name = "tvClub",
                        type = TextView.class
                )
        })
public class FootballerRenderer{
        
}

@Render annotation is needed to generate ViewHolder for this Renderer by annotation processor. Inside @Render annotation layout value is an itemView layout id and @ViewFields are containing information about the views in this layout. Name of the generated ViewHolder will be RendererClassName+'ViewHolder', in this case FootballerRendererViewHolder

Step 4:

Rebuild the project. Rebuilding generates ViewHolder class (FootballerRendererViewHolder), that we use in Step 5.

Step 5:

Extend your FootballerRenderer by Renderer and pass newly generated FootballerRendererViewHolder as a generic type:

@Render(layout = R.layout.item_footballer,
        views = {
                @ViewField(
                        id = R.id.tvName,
                        name = "tvName",
                        type = TextView.class
                ),
                @ViewField(
                        id = R.id.tvNumber,
                        name = "tvNumber",
                        type = TextView.class
                ),
                @ViewField(
                        id = R.id.tvClub,
                        name = "tvClub",
                        type = TextView.class
                )
        })
public class FootballerRenderer
        extends
        Renderer<FootballerRendererViewHolder> {

    public final FootballerModel footballerModel;

    public FootballerRenderer(final FootballerModel footballerModel) {
        this.footballerModel = footballerModel;
    }

    @Override
    public void apply(@NonNull final FootballerRendererViewHolder vh) {
        final Context context = vh.getContext();
        vh.tvName.setText(footballerModel.getName());
        vh.tvClub.setText(footballerModel.getClub());
        vh.tvNumber.setText(context.getString(R.string.footballer_number_template,
                footballerModel.getNumber()));
    }
}

As you see generated FootballerRendererViewHolder has tvName, tvClub, tvNumber fields.

Step 6:

  ...
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
 ...
 mAutoAdapter = AutoAdapterFactory.createAutoAdapter();
        mAutoAdapter.addAll(Stream.of(getFootballers()).map(FootballerRenderer::new)
                .collect(Collectors.toList()));
        mRecyclerView.setAdapter(mAutoAdapter);
    }


    private List<FootballerModel> getFootballers() {
        return Arrays.asList(
                new FootballerModel("Luis Suarez", 9, "Barcelona"),
                new FootballerModel("Leo Messi", 10, "Barcelona"),
                new FootballerModel("Ousmane Dembele", 11, "Barcelona"),
                new FootballerModel("Harry Kane", 9, "Tottenham Hotspur"),
                new FootballerModel("Dele Alli", 20, "Tottenham Hotspur"),
                new FootballerModel("Alexis Sanchez", 7, "Arsenal")
        );
    }

This line Stream.of(getFootballers()).map(FootballerRenderer::new).collect(Collectors.toList()) converts FootballerModel to FootballerRenderer using Stream

Mmm... What If I want to have heterogeneous items and layouts inside RecyclerView ?

AutoAdapter's working perfectly with heterogeneous items. You can add any descedent of Renderer to AutoAdapter, for example it's not a problem to write the following:

...
mAutoAdapter.add(new FootballerRenderer());
mAutoAdapter.add(new BasketballerRenderer());
mAutoAdapter.add(new BoxerRenderer());
....

They all will draw their own layout.

How to add OnClickListener to itemView ?

AutoAdapter has clicks method, it has one required argument Renderer class, one optional argument child view id and returns Rx2 Observable with ItemInfo as generic type. ItemInfo has 3 public final fields: position, renderer, viewHolder.

...
mAutoAdapter.clicks(FootballerRenderer.class)
                .map(itemInfo -> itemInfo.renderer)
                .map(renderer -> renderer.footballerModel)
                .subscribe(footballerModel ->
                        Toast.makeText(this,
                                footballerModel.getName(), Toast.LENGTH_LONG)
                                .show());
...
...
mAutoAdapter.clicks(FootballerRenderer.class, R.id.ivDelete)
                .map(itemInfo -> itemInfo.position)
                .subscribe(position -> {
                    mAutoAdapter.remove(position);
                    mAutoAdapter.notifyItemRemoved(position);
                });
...

SortedAutoAdapter Sample:

@Render(layout = R.layout.item_footballer,
        views = {
                @ViewField(
                        id = R.id.tvName,
                        name = "tvName",
                        type = TextView.class
                ),
                @ViewField(
                        id = R.id.tvNumber,
                        name = "tvNumber",
                        type = TextView.class
                ),
                @ViewField(
                        id = R.id.tvClub,
                        name = "tvClub",
                        type = TextView.class
                )
        })
public class FootballerOrderableRenderer
        extends
        OrderableRenderer<FootballerOrderableRendererViewHolder> {

    public final FootballerModel footballerModel;

    public FootballerOrderableRenderer(final FootballerModel footballerModel) {
        this.footballerModel = footballerModel;
    }

    @Override
    public void apply(final FootballerOrderableRendererViewHolder vh) {
        final Context context = vh.getContext();
        vh.tvName.setText(footballerModel.getName());
        vh.tvClub.setText(footballerModel.getClub());
        vh.tvNumber.setText(context.getString(R.string.footballer_number_template,
                footballerModel.getNumber()));
    }

    @Override
    public int compareTo(@NonNull OrderableRenderer item) {
        return Integer.valueOf(footballerModel.getNumber())
                .compareTo(getFootballerModel(item).getNumber());
    }

    private FootballerModel getFootballerModel(@NonNull final OrderableRenderer orderableRenderer) {
        return ((FootballerOrderableRenderer) orderableRenderer).footballerModel;
    }

    @Override
    public boolean areContentsTheSame(@NonNull OrderableRenderer item) {
        final FootballerModel otherFootballer = getFootballerModel(item);
        return footballerModel.getClub().equals(otherFootballer.getClub())
                && footballerModel.getNumber() == otherFootballer.getNumber();
    }

    @Override
    public boolean areItemsTheSame(@NonNull OrderableRenderer item) {
        return footballerModel.getName().equals(getFootballerModel(item).getName());
    }
}
public class SortedAutoAdapterSampleActivity
        extends
        AppCompatActivity {

    private RecyclerView mRecyclerView;
    private SortedAutoAdapter mSortedAutoAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRecyclerView = findViewById(R.id.recyclerView);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this,
                LinearLayoutManager.VERTICAL));
        mSortedAutoAdapter = AutoAdapterFactory.createSortedAutoAdapter();
        mSortedAutoAdapter.clicks(FootballerOrderableRenderer.class)
                .map(itemInfo -> itemInfo.renderer)
                .map(renderer -> renderer.footballerModel)
                .subscribe(footballerModel ->
                        Toast.makeText(this,
                                footballerModel.getName(), Toast.LENGTH_LONG)
                                .show());
        mSortedAutoAdapter.clicks(FootballerOrderableRenderer.class, R.id.ivDelete)
                .map(itemInfo -> itemInfo.position)
                .subscribe(position ->
                        mSortedAutoAdapter.remove(position));
        mSortedAutoAdapter.updateAll(Stream.of(getFootballers())
                .map(FootballerOrderableRenderer::new)
                .collect(Collectors.toList()));
        mRecyclerView.setAdapter(mSortedAutoAdapter);
    }

    private List<FootballerModel> getFootballers() {
        return Arrays.asList(
                new FootballerModel("Luis Suarez", 9, "Barcelona"),
                new FootballerModel("Leo Messi", 10, "Barcelona"),
                new FootballerModel("Ousmane Dembele", 11, "FC Barcelona"),
                new FootballerModel("Harry Kane", 9, "Tottenham Hotspur"),
                new FootballerModel("Dele Alli", 20, "Tottenham Hotspur"),
                new FootballerModel("Alexis Sanchez", 7, "Arsenal")
        );
    }
}