• Stars
    star
    195
  • Rank 199,374 (Top 4 %)
  • Language
    Java
  • License
    Apache License 2.0
  • Created about 10 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Write less code with AnnotatedAdapter, an annotation processor for generating RecyclerView and AbsListView adapters

AnnotatedAdapter

This Project is no longer maintained.

Sick of writing ViewHolder classes, inflate xml and distinguish ViewTypes in your adapters?
Write less code with AnnotatedAdapter, an annotation processor for generating RecyclerView and AbsListView adapters. So you no longer have to write boilerplate code like ViewHolder classes, inflate xml layouts and lots of if-else or switch-case to determine how to bind the data to the view holder by hand. AnnotatedAdpater generates that boilerplate code for you at compile time by annotation processing (not using reflections, so performance will be the same as handwritten).

Check this blog post or this sample adapter and you will se how much lesser code you have to write and how much cleaner your adapters code looks like.

I'm working on a solution for automatically detecting ViewHolder classes by scanning the xml layouts. A possible solution is discussed here

Best Practice

An interface called Binder (see Usage) will be generated for each AnnotatedAdapter. Hence the following workflow is considered as best practice in Android Studio:

  1. Create your adapter class and make this class extends from SupportAnnotatedAdapter
  2. Define at least one @ViewType
  3. In the main menu bar: Build -> Rebuild Project. This will force to generate the Binder interface
  4. Make your adapter class implementing the Binder interface and implement the required methods

Note that the manually triggered rebuild is normally required only on the very first time you create a new adapter class.

Dependency

Check GradlePlease to get the latest version number.

To run annotation processing you need to apply Hugo Visser's awesome android-apt gradle plugin.

  • Use SupportAnnotatedAdapter as base class and the following dependencies for RecyclerView from support library
dependencies {
	compile 'com.hannesdorfmann.annotatedadapter:annotation:1.1.1'
	compile 'com.hannesdorfmann.annotatedadapter:support-recyclerview:1.1.1'
	apt 'com.hannesdorfmann.annotatedadapter:processor:1.1.1'
}
  • Use AbsListAnnotatedAdapter as base class and the following dependencies for AbsListView widgets like ListView or GridView:
dependencies {
	compile 'com.hannesdorfmann.annotatedadapter:annotation:1.1.1'
	apt 'com.hannesdorfmann.annotatedadapter:processor:1.1.1'
}

Usage

Check out the sample folder, but basically you have to create an adapter class like this and annotate the view types with @ViewType and provide some more information in its annotation:

public class SampleAdapter extends SupportAnnotatedAdapter 
                            implements SampleAdapterBinder {

  /**
   * Specify a view type by annotating a public final int with @ViewType.
   * Like for any other adapter the view types must be start with an integer = 0
   */
  @ViewType(
      layout = R.layout.row_medium,   // The layout that will be inflated for this view type 
      views = {                      // The fields of the view holder
        @ViewField(
            id = R.id.textView,       // The id of this view
            name = "text",            // The name of this field in the generated ViewHolder
            type = TextView.class)    // The type (class) of view in the generated view holder
      }
   )
  public final int mediumRow = 0;     // The annotated ViewType constant


   @ViewType(
        layout = R.layout.row_with_pic,
        views = {
            @ViewField(id = R.id.textView, name = "text", type = TextView.class),
            @ViewField(id = R.id.imageView, name = "image", type = ImageView.class)
        }
    )
  public final int rowWithPic = 1;

  List<String> items;

  public SampleAdapter(Context context, List<String> items) {
    super(context);
    this.items = items;
  }

  /**
   * Get the number of items like in any other adapter
   */
  @Override public int getItemCount() {
    return items == null ? 0 : items.size();
  }

  /**
   * Determine the view type for the cell at position (like you would do in any other adpater)
   */
  @Override public int getItemViewType(int position) {
    if (position % 2 == 0)
        return mediumRow;
    else
        return rowWithPic;
    }
  
  /**
   * Bind the data to this view type mediumRow; MediumRowViewHolder was generated
   */
  @Override public void bindViewHolder(SampleAdapterHolders.MediumRowViewHolder vh,
        int position) {
  
      String str = items.get(position);
      vh.text.setText(str);
    }
  
    /**
     * Bind the data to this view type rowWithPic; RowWithPicViewHolder was generated
     */
    @Override public void bindViewHolder(SampleAdapterHolders.RowWithPicViewHolder vh,
        int position) {
  
      String str = items.get(position);
      vh.text.setText(str);
      vh.image.setImageResource(R.drawable.ic_launcher);
    }
  
}

Even if there are already some comments in the code shown above, let's review the code step by step:

  1. Create an adapter class that extends from SupportAnnotatedAdapter for android.support.v7.widget.RecyclerView or AbsListAnnotatedAdapter for AbsListView (like ListView or GridView)
  2. Set view types like you would do in any normal adapter by specifying integer constants. Remember those constants must start with zero.
  3. Annotate this view types with @ViewType. Specify the layout that should be inflated for this view type and declare the fields that should be generated for the corresponding view holder. The following anntated view type:
@ViewType(
        layout = R.layout.row_with_pic,
        views = {  // UI View Fields
            @ViewField(id = R.id.textView, name = "fooText", type = TextView.class),
            @ViewField(id = R.id.imageView, name = "image", type = ImageView.class)
        },
        fields = { // other fields (not Views)
           @Field(name="listener", type = MyClickListener.class )
        }

    )
  public final int rowWithPic = 0;

will generate the following view holder class:

public static class RowWithPicViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {

    // UI View fields
    public TextView fooText;
    public ImageView image;

    // Not View fields
    public MyClickListener listener;

    public RowWithPicViewHolder(android.view.View view) {

      super(view);

      fooText = (android.widget.TextView) view.findViewById(R.id.textView);
      image = (android.widget.ImageView) view.findViewById(R.id.imageView);
    }
  }
  1. Like in any other adapter you have to specify which view type should be displayed for the given position by overriding public int getItemViewType(int position) and you of course you have to say how many items are displayed in the RecyclerView / ListView by overriding public int getItemCount()
  2. An interface will be generated (if adapter class contains at least one @ViewType) with the name AdapterClassName + Binder.
  3. Let your adapter class implement this interface. For each view type you have to implement the corresponding bindViewHolder() method where you bind the data to the generated view holder.

Lifecycle and methods call

Internally views and ViewHolders are created and are recycled like you expect from your own handwritten adapter implementation. Basically the following steps are executed for each cell (view):

  1. Call int viewType = getItemViewType(position) to determine the view type
  2. If there is a cell (view) that can be recycled then continue in step 4.
  3. If no cell (view) can be recycled instantiate a new one:
    1. Inflate the xml layout specified in @ViewType( layout = R.layout.id )
    2. Create a new instance of the corresponding ViewHolder class. findViewById()will be used for each field in @ViewHolder ( views = { @ViewField ( ... ) } ). If you need additional fields (not view fields that are bound by findViewById()) i.e. for having a field for OnClickListener you can do so by using @ViewHolder ( fields = { @Field ( ... ) } )
    3. If you want to do additional initialization of the inflated View (like setting the width or height of a subview) in code then you have to set @ViewHolder( initMethod = true). This will force to create a method called initViewHolder(viewHolderClass, view, parent) in the Binder interface which you have to implement afterwards
  4. Call bindViewHolder(viewHolder, position) to bind the data to the cell (view)

Inheritance

AnnotatedAdapter supports inheritance. The only thing you have to keep in mind, like for any other handwritten adapter, is that the view holders constant integer value must be unique along the inheritance tree.

Example:

public class BaseAdapter extends SupportAnnotatedAdapter implements BaseAdapterHolder {

    @ViewHolder (...)
    public final int simpleRow = 0;

}

public class OtherAdapter extends BaseAdpter implements BaseAdapterHolder {

    @ViewHolder (...)
    public final int otherRow = 0;  // Cause problems, because BaseAdapter.simpleRow == 1 && OtherAdapter.otherRow == 1 

}

In this case are @ViewType simpleRow = 0 and @ViewType otherRow = 0 which will cause unexpected behaviour. To avoid this kind of problems AnnotatedAdapter will throw a compile time error that states that there are two view types with the same value. However, you can disable this check by setting @ViewType( checkValue = false ). Do that only if you have a very good reason for. Usually it should be enough to override the bindViewHolder() method in your subclass instead of setting @ViewType( checkValue = false ). The only good reason I can see right now is to "override" the xml layout that should be inflated. Notice that at this point the subclass @ViewType definition will be used instead of the base class @ViewType definition.

More Repositories

1

mosby

A Model-View-Presenter / Model-View-Intent library for modern Android apps
Java
5,489
star
2

AdapterDelegates

"Favor composition over inheritance" for RecyclerView Adapters
Java
2,934
star
3

fragmentargs

Annotation Processor for setting arguments in android fragments
Java
1,077
star
4

SwipeBack

SwipeBack for Android Activities to do pretty the same as the android "back-button" will do, but in a really intuitive way by using a swipe gesture
Java
695
star
5

annotationprocessing101

Java
431
star
6

ParcelablePlease

Annotation Processor for generating Parcelable code
Java
259
star
7

sqlbrite-dao

DAO for SQLBrite
Java
182
star
8

debugoverlay

A tiny window overlay to log app internal on top of your android app
Java
151
star
9

mosby-conductor

Plugin for conductor to integrate Mosby
Java
131
star
10

CoordinatorsAndroid

Sample that shows how to apply the Coordinator Pattern on Android
Kotlin
121
star
11

Model-View-Intent-Android

A demo that shows how to apply Model-View-Intent on Android
Kotlin
86
star
12

AdapterCommands

Drop in solution to animate RecyclerView's dataset changes by using command pattern
Java
74
star
13

Instantiator

Tired of manually setup test data of Kotlin data classes or POJOs? Instantiator creates Instances of any class for you so that you can focus on writing tests instead of spending time and effort to setup test data
Kotlin
60
star
14

Vaadin-MVP-Lite

This is a Vaadin Addon that provides a Model-View-Presenter Framework
Java
51
star
15

CircleProgressView

Fork from SmoothProgressbar for internal development of another UI library ...
Java
38
star
16

mvi-timing

Just a simple demo app for blog post about MVI and Timing
Java
29
star
17

Todo-Testing-By-Design

Kotlin
13
star
18

rxworkshop

Kotlin
12
star
19

conductor-shared-element-transition

Kotlin
9
star
20

Vaadin-MVP-Lite-MailExample

This is an example project that shows how to use the VaadinMVP framework
JavaScript
9
star
21

appkit

Develop android application in a modern way. Write less code: appkit uses many popular Annotation Processing libraries like Butterknife, IcePick, Dependency Injection with Dagger and achieve a very clean software architecture with Model-View-Presenter (MVP)
Java
9
star
22

SecureBitcoinWallet

Java
8
star
23

SealedSubclassInstantiator

Instantiates instances of subclasses of a sealed class (Kotlin)
Kotlin
8
star
24

website-old

Personal blog, please read README
CSS
7
star
25

MosbyDagger

Example that shows best practices for Dagger2 + Mosby
4
star
26

AndroidCollections

Some usefu collection implementation like List, Map, Set (and combinations of all) etc. that can be useful in Android Projects
Java
4
star
27

OkHttpCertificate

Kotlin
3
star
28

hdlib

Java
2
star
29

fragmentargs-samples

Java
2
star
30

AndroidDesignPatterns

A Collection of Software Design Patterns for Android Applications
Java
2
star
31

ColorProgressBar

A circular (loading spinner) ProgressBar for Android that let you customize the colors of the loading spinner
Java
2
star
32

MosbyViewPagerDemo

Demo of viewpager + mosby
Java
1
star
33

RobolectircTest

Java
1
star
34

DexUtil

How many mehtods are used in your androids .dex file?
Java
1
star
35

annotationprocessing

Some utilities for writing annotation processors
Java
1
star
36

AndroidCollectionsTest

The eclipcse Android Test Project to run unit tests from eclipse
1
star
37

ason

Java
1
star
38

Vaadin-LoadingPanel

A simple LoadingPanel component, where you can switch beetween showing a loading animation and the "normal" content
Java
1
star
39

HippoHappa

Java
1
star
40

stdlib-android-test

Units test for the stdlib-android library
Java
1
star