Adapters and lists - WonderCsabo/androidannotations GitHub Wiki

This is just a simple demonstration of how you could handle Adapters and AdapterViews in a simple way with AndroidAnnotations.

Let's say you have a Person class:

public class Person {
    public final String firstName;
    public final String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

and a PersonFinder interface:

public interface PersonFinder {
    List<Person> findAll();
}

We want to create a PersonListActivity that lists all the available persons. For that, we'll need a PersonListAdapter that binds the data to the views, and a PersonItemView that is the view for one item in the list.

The PersonItemView will use one TextView for the first name, and one TextView for the last name:

@EViewGroup(R.layout.person_item)
public class PersonItemView extends LinearLayout {

    @ViewById
    TextView firstNameView;

    @ViewById
    TextView lastNameView;

    public PersonItemView(Context context) {
        super(context);
    }

    public void bind(Person person) {
        firstNameView.setText(person.firstName);
        lastNameView.setText(person.lastName);
    }
}

Notice that creating a custom view group that has its child views injected removes the need to use a View Holder Pattern.

There's a PersonFinder implementation, let's say InMemoryPersonFinder, that is annotated with @EBean. We won't describe this implementation.

The adapter directly manipulates it to bind its data and create the corresponding views:

@EBean
public class PersonListAdapter extends BaseAdapter {

    List<Person> persons;
    
    @Bean(InMemoryPersonFinder.class)
    PersonFinder personFinder;
    
    @RootContext
    Context context;

    @AfterInject
    void initAdapter() {
        persons = personFinder.findAll();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        
        PersonItemView personItemView;
        if (convertView == null) {
            personItemView = PersonItemView_.build(context);
        } else {
            personItemView = (PersonItemView) convertView;
        }
        
        personItemView.bind(getItem(position));

        return personItemView;
    }
    
    @Override
    public int getCount() {
        return persons.size();
    }

    @Override
    public Person getItem(int position) {
        return persons.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }
}

The PersonListActivity binds the PersonListAdapter to a ListView, and displays a toast when a PersonItemView is clicked.

@EActivity(R.layout.person_list)
public class PersonListActivity extends Activity {
    @ViewById
    ListView personList;

    @Bean
    PersonListAdapter adapter;

    @AfterViews
    void bindAdapter() {
        personList.setAdapter(adapter);
    }

    @ItemClick
    void personListItemClicked(Person person) {
        makeText(this, person.firstName + " " + person.lastName, LENGTH_SHORT).show();
    }
}

RecyclerView and ViewHolder

If you are using RecyclerView instead of a simple ListView, you should treat the situation a little bit differently. RecyclerView.Adapter creates ViewHolders, instead of Views. Unfortunately you cannot enhance and inject into ViewHolder classes, but you can easily use @EViewGroup as for ListViews with a help of a modified adapter.

Create a generic class which can be used to wrap all kind of Views into a ViewHolder:

public class ViewWrapper<V extends View> extends RecyclerView.ViewHolder {

    private V view;
    
    public ViewWrapper(V itemView) {
        super(itemView);
        view = itemView;
    }
    
    public V getView() {
        return view;
    }
}

Create a common base class for all RecyclerView adapters:

public abstract class RecyclerViewAdapterBase<T, V extends View> extends RecyclerView.Adapter<ViewWrapper<V>> {

    protected List<T> items = new ArrayList<T>();

    @Override
    public int getItemCount() {
        return items.size();
    }

    @Override
    public final ViewWrapper<V> onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewWrapper<V>(onCreateItemView(parent, viewType));
    }
    
    protected abstract V onCreateItemView(ViewGroup parent, int viewType);

    // additional methods to manipulate the items
}

You can utilize the two classes with our Person example as follows:

@EBean
public class PersonAdapter extends RecyclerViewAdapterBase<Person, PersonItemView> {

    @RootContext
    Context context;
    
    @Override
    protected PersonItemView onCreateItemView(ViewGroup parent, int viewType) {
        return PersonItemView_.build(context);
    }

    @Override
    public void onBindViewHolder(ViewWrapper<PersonItemView> viewHolder, int position) {
        PersonItemView view = viewHolder.getView();
        Person person = items.get(position);
        
        view.bind(person);
    }

}

If you create an interface for the bind method, you can even move up the implementation of onBindViewHolder to the base class.

⚠️ **GitHub.com Fallback** ⚠️