Yes it is possible.
However you will have to write a Viewholder for each type of layout of the item, a Viewholderfactory and your Model will have to implement an interface.
Viewholder is required to connect the Model to the Views.
The Viewholderfactory for the Adapter to build the Viewholder it will use.
The interface, which the Model implements, is used to indicate, through the parameter viewType
of the method onCreateViewHolder()
, what layout to use to create Viewholder.
For all this to work it is necessary to create some abstractions:
Interface Typeprovider, to be implemented by Model
public interface TypeProvider {
int type(ViewHolderFactory viewHolderFactory);
}
Genericviewholder, abstract class of which each Viewholder should inherit.
public abstract class GenericViewHolder<T> extends RecyclerView.ViewHolder{
public GenericViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(T item);
}
Interface Viewholderfactory, each type of Viewmodel will be built by the corresponding implementation of this class.
public interface ViewHolderFactory {
int type();
GenericViewHolder createViewHolder(View parent);
}
It is these abstractions that will allow Adapter to work with any type of data(Model) and Viewholder.
Implementation of the Adapter:
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 made.
Example of use.
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 you get references to layout views and in the method bind()
the model values 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);
}
}
Gathered all 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 is almost as much as having to make several Adapters. The extra work is only the creation of abstractions, but they are created only once. Viewholder and Model must always be created, whatever the approach.
However, this "is more object oriented" than having multiple Adapters.
Source of inspiration:
Assuming you have an application, for example customer and employee registration, that have similar features, you want to create an Adapter that fits both?
– Costamilam
Exactly for one or more activities!
– Diego Dias Mól
I believe you can do that with inheritance
– Costamilam
The line layout is always the same?
– ramaral