Skip to content
This repository has been archived by the owner on Feb 26, 2023. It is now read-only.

Adapters and lists

WonderCsabo edited this page Dec 28, 2014 · 9 revisions

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.

Using AndroidAnnotations

Questions?

Enjoying AndroidAnnotations

Improving AndroidAnnotations

Extending AndroidAnnotations

Clone this wiki locally