Yes it is possible.
However, you will need to write a ViewHolder for each item layout type, a ViewHolderFactory, and your Model will need to implement an interface.
ViewHolder is required to connect Model with Views.
The ViewHolderFactory for the adapter to build the ViewHolder that it will use.
The interface that Model implements is used to indicate, through the viewType
parameter of the onCreateViewHolder()
method, which layout to use to create the ViewHolder.
For this to work, you need to create some abstractions:
-
TypeProvider interface to be implemented by Model
public interface TypeProvider {
int type(ViewHolderFactory viewHolderFactory);
}
-
GenericViewHolder , an abstract class that each ViewHolder should inherit.
public abstract class GenericViewHolder<T> extends RecyclerView.ViewHolder{
public GenericViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(T item);
}
-
ViewHolderFactory interface, each ViewModel type will be built by the corresponding implementation of this class.
public interface ViewHolderFactory {
int type();
GenericViewHolder createViewHolder(View parent);
}
These abstractions will allow the adapter to work with any type of data (Model) and ViewHolder.
Adapter implementation:
public class GenericRecyclerAdapter extends RecyclerView.Adapter<GenericViewHolder> {
private ArrayList<TypeProvider> items;
private ViewHolderFactory viewHolderFactory;
public GenericRecyclerAdapter(ArrayList<TypeProvider> items, ViewHolderFactory viewHolderFactory){
this.items = items;
this.viewHolderFactory = viewHolderFactory;
}
@Override
public GenericViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(viewType, parent, false);
return viewHolderFactory.createViewHolder(view);
}
@Override
public void onBindViewHolder(GenericViewHolder holder, int position) {
holder.bind(items.get(position));
}
@Override
public int getItemViewType(int position) {
return items.get(position).type(viewHolderFactory);
}
@Override
public int getItemCount() {
return items.size();
}
}
The infrastructure is done.
Example usage.
-
Model, in this example represents an image and its name.
public class Model implements TypeProvider {
private int imageId;
private String imageName;
public Model(int imageId, String imageName){
this.imageId = imageId;
this.imageName = imageName;
}
@Override
public int type(ViewHolderFactory viewHolderFactory) {
// Ao delegar para o viewHolderFactory a obtenção do Layout
// evita-se que o model dependa do framework Android, neste caso da classe R.
return viewHolderFactory.type();
}
public int getImageId() {
return imageId;
}
public String getImageName() {
return imageName;
}
}
-
ViewHolder, follows the classic implementation: in the constructor, the references to the layout views are obtained and in the bind()
method, the values of the model are assigned to the respective views:
public class ModelViewHolder extends GenericViewHolder<Model> {
//A ser usado pelo ViewHolderFactory para, através do model,
//indicar, no método onCreateViewHolder(), qual o layout a usar
public static final int LAYOUT = R.layout.item_view;
private ImageView mImageView;
private TextView mTextView;
public ModelViewHolder(View itemView) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.image);
mTextView = (TextView)itemView.findViewById(R.id.text);
}
@Override
public void bind(Model item) {
mTextView.setText(item.getImageName());
mImageView.setImageResource(item.getImageId());
}
}
-
ViewHolderFactory
public class ModelViewHolderFactory implements ViewHolderFactory {
@Override
public int type() {
return ModelViewHolder.LAYOUT;
}
@Override
public GenericViewHolder createViewHolder(View parent) {
return new ModelViewHolder(parent);
}
}
-
Put everything together in Activity
public class GenericAdapterActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
ArrayList<TypeProvider> items = new ArrayList<>();
items.add(new Model(R.mipmap.ic_launcher, "item1"));
items.add(new Model(R.mipmap.ic_launcher, "item2"));
items.add(new Model(R.mipmap.ic_launcher, "item3"));
items.add(new Model(R.mipmap.ic_launcher, "item4"));
items.add(new Model(R.mipmap.ic_launcher, "item5"));
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
ModelViewHolderFactory modelViewHolderFactory = new ModelViewHolderFactory();
GenericRecyclerAdapter adapter = new GenericRecyclerAdapter(items, modelViewHolderFactory);
recyclerView.setAdapter(adapter);
}
}
It seems like a lot of work, however it's almost as much as having to do multiple adapters. Extra work is just the creation of abstractions, but they are created only once. ViewHolder and Model will always have to be created, regardless of approach.
However, this "is more object-oriented" than having multiple Adapters.
Source of inspiration: