Developing a new BaseAdapter template in Android

UPDATED: No more bugs! Comment on the sample itself. Advantages and disadvantages. What do you like, what you do not do. What can be fixed. Still don't understand why I did this ... let me know (but read my post below)

I am working on creating a new design pattern for BaseAdapters on Android, and so far I really like how it works!


I have a data structure that can contain all the necessary data for each object in a particular type of collection. I am creating specific implementations of the BaseAdapter with my own user interface layout that will be displayed to the user on the screen. Pretty simple and great idea ... nothing new.


OK, so WHY do I want this?

The general idea is: 1) to ignore the implementation of the getView method and allow more amateur developers to easily create their own custom adapters and 2) to simplify and abstract as much as possible.

NOTE: in the last two parts of the code, I have only 3 things to implement (this makes sense). The inner class that is present in the current Holder template is the SetLayoutResource (...) method and the ExtractLayoutResources (...) method.

3 main reasons why I want this and think it’s good ... abstraction, abstraction, abstraction!


ConcreteCustomAdapter.java

(A specific code example ... this is basically how everything will look !!! This is basically all you need to enter to create a new custom base adapter !!!)

public class ConcreteCustomAdapter extends BaseDataAdapter<Song, SongHolder> { public ConcreteCustomAdapter(Context context, int resource, Song[] data) { super(context, resource, data); // Give the base class a reference to the actual type of Holder class to use this.setViewHolder(new SongHolder()); } @Override protected void setLayoutResources(SongHolder holder, Song data) { // Set the View Holder objects properties with the current data holder.imgUiControl.setImageResource(data.thumbnail); holder.txtUiControl.setText(data.Name); } @Override protected void extractLayoutResources(View row, SongHolder holder) { // Convert XML UI component definitions into the static View Holder object holder.imgUiControl = (ImageView) row.findViewById(R.id.imgUiControl); holder.txtUiControl = (TextView) row.findViewById(R.id.txtUiControl); } // Class that holds all the UI component references static class SongHolder implements IHolder { ImageView imgUiControl; TextView txtUiControl; } } 

Song.java

(User created!)

 // Entity that holds ALL the data public class Song implements IData { public int thumbnail; public String Name; ... // Constructors, Getters, Setters ... } 

LEISURE CODES TO SUPPORT GOALS


IHolder.java

 // Current Adapter Pattern uses Holder Objects, this represents that and the data via interface public interface IHolder { interface IData { } } 

BaseDataAdapter.java

(User should not touch this)

 // D for Data....H for Holder (sorry not convention) public abstract class BaseDataAdapter<D extends IData, H extends IHolder> extends BaseAdapter { private Context context; private int layoutResourceID; private D data[] = null; private H holder = null; public BaseDataAdapter(Context context, int resource, D[] data) { //super(context, resource, data); this.layoutResourceID = resource; this.context = context; this.data = data; } @Override public View getView(int position, View convertView, ViewGroup parent) { //super.getView(position, convertView, parent); View row = convertView; if (row == null) { LayoutInflater newView = ((Activity) context).getLayoutInflater(); row = newView.inflate(this.layoutResourceID, parent, false); extractLayoutResources(row, holder); row.setTag(holder); } else { holder = (H) row.getTag(); } setLayoutResources(holder, data[position]); return row; } public void setViewHolder(H holder) { this.holder = holder; } abstract protected void setLayoutResources(H holder, D data); abstract protected void extractLayoutResources(View row, H holder); @Override public int getCount() { return this.data.length; } @Override public Object getItem(int position) { return this.data[position]; } @Override public long getItemId(int position) { return 0; } } 
+4
source share
3 answers

I would do it like this:

 public abstract class TypedListAdapter<T, H extends TypedListAdapter.ViewHolder> extends BaseAdapter { private final int itemViewId; private List<T> data; public TypedAdapter(final int itemViewId) { this(itemViewId, null); } public TypedAdapter(final int itemViewId, final List<T> data) { this.itemViewId = itemViewId; this.data = preventNull(data); } private List<T> preventNull(final List<T> data) { return data == null ? Collections.<T>emptyList() : data; } public void setData(final List<T> data) { final List<T> nonNullData = preventNull(data); if (nonNullData != this.data) { this.data = nonNullData; notifyDataSetChanged(); } } @Override public int getCount() { return data.size(); } @Override public T getItem(final int position) { return data.get(position); } @Override public long getItemId(final int position) { return position; } @Override public View getView(final int position, final View convertView, final ViewGroup parent) { final H holder = obtainHolder(convertView, parent); bind(holder, getItem(position)); return holder.view; } protected abstract void bind(final H holder, final T item); private H obtainHolder(final View convertView, final ViewGroup parent) { if (convertView == null) { final View view = LayoutInflater.from(parent.getContext()).inflate(itemViewId, parent, false); return createHolder(view); } else { return (H) convertView.getTag(); } } protected abstract H createHolder(final View view); public static class ViewHolder { public final View view; public ViewHolder(final View view) { this.view = view; view.setTag(this); } } } 

An adapter implementation might look like this:

 public class SongListAdapter extends TypedListAdapter<Song, SongListAdapter.SongHolder> { public SongListAdapter(final int itemViewId) { super(itemViewId); } public SongListAdapter(final int itemViewId, final List<Song> data) { super(itemViewId, data); } @Override protected void bind(final SongHolder holder, final Song item) { holder.name.setText(item.getName()); holder.thumbnail.setImageResource(item.getThumbnail()); } @Override protected SongHolder createHolder(final View view) { return new SongHolder(view); } public static class SongHolder extends ViewHolder { public final ImageView thumbnail; public final TextView name; public SongHolder(final View view) { super(view); thumbnail = (ImageView) view.findViewById(R.id.thumbnail); name = (TextView) view.findViewById(R.id.name); } } } 
+2
source

About the error in your SetLayoutResource method, you forgot to start IHolder.

  @Override protected void SetLayoutResource(IHolder holder, int position) { Song currentSong = data[position]; // ERRORS HERE: imgSongThumbnail and company cannot be resolved, or is not a field (in IHolder!) holder.imgSongThumbnail.setImageResource(currentSong.thumbnail); holder.txtSongName.setText(currentSong.Name); .... } 

Must be

  @Override protected void SetLayoutResource(IHolder holder, int position) { Song currentSong = data[position]; SongHolder songHolder = (IHolder) holder; // ERRORS HERE: imgSongThumbnail and company cannot be resolved, or is not a field (in IHolder!) songHolder.imgSongThumbnail.setImageResource(currentSong.thumbnail); songHolder.txtSongName.setText(currentSong.Name); .... } 

You can download the working example at the following link https://docs.google.com/file/d/0B5bbdw1x2IDET1YxZE4wdnpxZnc/edit?usp=sharing

+1
source

First, some general notes:

  • Method names must begin with a lowercase letter.
  • Personally, I would recommend renaming your generic types for a better understanding. For T I would go with TData , and for V I would use something like VHolder

Now for the answer:

Since you are using generics, you should also use them in method signatures:

 abstract protected void setLayoutResource(THolder holder, int position); abstract protected THolder extractLayoutResources(ViewGroup parent, View row, THolder holder); 

Now you are done with this in your GeneralSongListAdapter

 @Override protected SongHolder ExtractLayoutResources(ViewGroup parent, View row, SongHolder holder) { 

Now you can access the fields directly without casting.

Also, I would recommend renaming your abstract methods. I don't think their current names correctly say what this method should do. If you look at the CursorAdapter , you will see createView and bindView that do exactly what you are doing. Perhaps use this name.

+1
source

All Articles